From 27b58a09f2e314dedc7479f361042441ecbab3e6 Mon Sep 17 00:00:00 2001 From: optixx Date: Wed, 8 Apr 2009 21:29:36 +0200 Subject: [PATCH] o add bsnes --- bsnes/Makefile | 278 ++ bsnes/base.hpp | 49 + bsnes/bsnes.lnk | Bin 0 -> 627 bytes bsnes/cart/cart.cpp | 234 ++ bsnes/cart/cart.hpp | 178 ++ bsnes/cart/cart_file.cpp | 109 + bsnes/cart/cart_header.cpp | 272 ++ bsnes/cart/cart_loader.cpp | 244 ++ bsnes/cc.bat | 3 + bsnes/cc.sh | 2 + bsnes/cheat/cheat.cpp | 392 +++ bsnes/cheat/cheat.hpp | 69 + bsnes/chip/bsx/bsx.cpp | 8 + bsnes/chip/bsx/bsx.hpp | 77 + bsnes/chip/bsx/bsx_base.cpp | 137 + bsnes/chip/bsx/bsx_cart.cpp | 101 + bsnes/chip/bsx/bsx_flash.cpp | 113 + bsnes/chip/chip.hpp | 11 + bsnes/chip/cx4/cx4.cpp | 197 ++ bsnes/chip/cx4/cx4.hpp | 97 + bsnes/chip/cx4/cx4data.cpp | 187 ++ bsnes/chip/cx4/cx4fn.cpp | 246 ++ bsnes/chip/cx4/cx4oam.cpp | 223 ++ bsnes/chip/cx4/cx4ops.cpp | 226 ++ bsnes/chip/dsp1/dsp1.cpp | 59 + bsnes/chip/dsp1/dsp1.hpp | 18 + bsnes/chip/dsp1/dsp1emu.cpp | 1625 ++++++++++ bsnes/chip/dsp1/dsp1emu.hpp | 127 + bsnes/chip/dsp2/dsp2.cpp | 136 + bsnes/chip/dsp2/dsp2.hpp | 44 + bsnes/chip/dsp2/dsp2_op.cpp | 177 ++ bsnes/chip/dsp3/dsp3.cpp | 35 + bsnes/chip/dsp3/dsp3.hpp | 12 + bsnes/chip/dsp3/dsp3emu.c | 1146 +++++++ bsnes/chip/dsp4/dsp4.cpp | 55 + bsnes/chip/dsp4/dsp4.hpp | 12 + bsnes/chip/dsp4/dsp4emu.c | 2150 +++++++++++++ bsnes/chip/dsp4/dsp4emu.h | 108 + bsnes/chip/obc1/obc1.cpp | 72 + bsnes/chip/obc1/obc1.hpp | 25 + bsnes/chip/sdd1/sdd1.cpp | 158 + bsnes/chip/sdd1/sdd1.hpp | 40 + bsnes/chip/sdd1/sdd1emu.cpp | 451 +++ bsnes/chip/sdd1/sdd1emu.hpp | 162 + bsnes/chip/spc7110/decomp.cpp | 511 ++++ bsnes/chip/spc7110/decomp.hpp | 45 + bsnes/chip/spc7110/spc7110.cpp | 672 ++++ bsnes/chip/spc7110/spc7110.hpp | 133 + bsnes/chip/srtc/srtc.cpp | 226 ++ bsnes/chip/srtc/srtc.hpp | 22 + bsnes/chip/st010/st010.cpp | 87 + bsnes/chip/st010/st010.hpp | 42 + bsnes/chip/st010/st010_data.hpp | 126 + bsnes/chip/st010/st010_op.cpp | 261 ++ bsnes/clean.bat | 1 + bsnes/clean.sh | 1 + bsnes/cpu/cpu.cpp | 17 + bsnes/cpu/cpu.hpp | 74 + bsnes/cpu/cpuregs.hpp | 74 + bsnes/cpu/dcpu.cpp | 483 +++ bsnes/cpu/scpu/core/cc.sh | 4 + bsnes/cpu/scpu/core/clean.sh | 1 + bsnes/cpu/scpu/core/core.cpp | 90 + bsnes/cpu/scpu/core/core.hpp | 54 + bsnes/cpu/scpu/core/op_misc.b | 298 ++ bsnes/cpu/scpu/core/op_misc.cpp | 539 ++++ bsnes/cpu/scpu/core/op_pc.b | 163 + bsnes/cpu/scpu/core/op_pc.cpp | 279 ++ bsnes/cpu/scpu/core/op_read.b | 317 ++ bsnes/cpu/scpu/core/op_read.cpp | 1654 ++++++++++ bsnes/cpu/scpu/core/op_rmw.b | 181 ++ bsnes/cpu/scpu/core/op_rmw.cpp | 573 ++++ bsnes/cpu/scpu/core/op_write.b | 181 ++ bsnes/cpu/scpu/core/op_write.cpp | 293 ++ bsnes/cpu/scpu/core/opfn.cpp | 371 +++ bsnes/cpu/scpu/core/scpugen.cpp | 12 + bsnes/cpu/scpu/dma/dma.cpp | 271 ++ bsnes/cpu/scpu/dma/dma.hpp | 71 + bsnes/cpu/scpu/memory/memory.cpp | 125 + bsnes/cpu/scpu/memory/memory.hpp | 35 + bsnes/cpu/scpu/mmio/mmio.cpp | 534 ++++ bsnes/cpu/scpu/mmio/mmio.hpp | 71 + bsnes/cpu/scpu/scpu.cpp | 61 + bsnes/cpu/scpu/scpu.hpp | 94 + bsnes/cpu/scpu/timing/event.cpp | 35 + bsnes/cpu/scpu/timing/irq.cpp | 107 + bsnes/cpu/scpu/timing/joypad.cpp | 28 + bsnes/cpu/scpu/timing/timing.cpp | 166 + bsnes/cpu/scpu/timing/timing.hpp | 41 + bsnes/data/bsnes.Manifest | 9 + bsnes/data/bsnes.desktop | 8 + bsnes/data/bsnes.ico | Bin 0 -> 22071 bytes bsnes/data/bsnes.png | Bin 0 -> 1368 bytes bsnes/data/documentation.html | 81 + bsnes/data/joypad.png | Bin 0 -> 102778 bytes bsnes/data/license.html | 87 + bsnes/data/logo.png | Bin 0 -> 16733 bytes bsnes/dsp/adsp/adsp.cpp | 588 ++++ bsnes/dsp/adsp/adsp.hpp | 172 ++ bsnes/dsp/adsp/adsp_tables.cpp | 77 + bsnes/dsp/dsp.hpp | 13 + bsnes/dsp/sdsp/brr.cpp | 62 + bsnes/dsp/sdsp/counter.cpp | 52 + 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 | 326 ++ bsnes/dsp/sdsp/sdsp.hpp | 166 + bsnes/dsp/sdsp/voice.cpp | 174 ++ bsnes/interface.hpp | 24 + bsnes/lib/libco/fiber.c | 51 + bsnes/lib/libco/libco.c | 21 + bsnes/lib/libco/libco.h | 34 + bsnes/lib/libco/ppc.s | 478 +++ bsnes/lib/libco/ppc64.s | 513 ++++ bsnes/lib/libco/sjlj.c | 102 + bsnes/lib/libco/ucontext.c | 67 + bsnes/lib/libco/x86-64.c | 81 + bsnes/lib/libco/x86.c | 110 + bsnes/lib/libfilter/colortable.cpp | 138 + bsnes/lib/libfilter/colortable.hpp | 44 + bsnes/lib/libfilter/direct.cpp | 30 + bsnes/lib/libfilter/direct.hpp | 6 + bsnes/lib/libfilter/filter.cpp | 34 + bsnes/lib/libfilter/filter.hpp | 32 + bsnes/lib/libfilter/hq2x.cpp | 160 + bsnes/lib/libfilter/hq2x.hpp | 14 + bsnes/lib/libfilter/hq2x_table.hpp | 2690 +++++++++++++++++ bsnes/lib/libfilter/libfilter.cpp | 14 + bsnes/lib/libfilter/libfilter.hpp | 22 + bsnes/lib/libfilter/ntsc.cpp | 73 + bsnes/lib/libfilter/ntsc.hpp | 16 + bsnes/lib/libfilter/scale2x.cpp | 47 + bsnes/lib/libfilter/scale2x.hpp | 6 + bsnes/lib/libfilter/scanline.cpp | 40 + bsnes/lib/libfilter/scanline.hpp | 6 + bsnes/lib/libfilter/snes_ntsc/snes_ntsc.c | 251 ++ bsnes/lib/libfilter/snes_ntsc/snes_ntsc.h | 228 ++ .../libfilter/snes_ntsc/snes_ntsc_config.h | 26 + .../lib/libfilter/snes_ntsc/snes_ntsc_impl.h | 439 +++ bsnes/lib/nall/Makefile.string | 63 + bsnes/lib/nall/algorithm.hpp | 23 + bsnes/lib/nall/any.hpp | 74 + bsnes/lib/nall/array.hpp | 94 + bsnes/lib/nall/base64.hpp | 92 + bsnes/lib/nall/bit.hpp | 51 + bsnes/lib/nall/config.hpp | 124 + bsnes/lib/nall/crc32.hpp | 66 + bsnes/lib/nall/detect.hpp | 30 + bsnes/lib/nall/dictionary.hpp | 73 + bsnes/lib/nall/endian.hpp | 38 + bsnes/lib/nall/file.hpp | 218 ++ bsnes/lib/nall/filemap.hpp | 190 ++ bsnes/lib/nall/function.hpp | 184 ++ bsnes/lib/nall/input.hpp | 263 ++ bsnes/lib/nall/lzss.hpp | 81 + bsnes/lib/nall/moduloarray.hpp | 36 + bsnes/lib/nall/new.hpp | 25 + bsnes/lib/nall/platform.hpp | 75 + bsnes/lib/nall/priorityqueue.hpp | 94 + bsnes/lib/nall/property.hpp | 45 + bsnes/lib/nall/serial.hpp | 80 + bsnes/lib/nall/sort.hpp | 62 + bsnes/lib/nall/static.hpp | 17 + bsnes/lib/nall/stdint.hpp | 44 + bsnes/lib/nall/string.cpp | 24 + bsnes/lib/nall/string.hpp | 175 ++ bsnes/lib/nall/string/compare.cpp | 99 + bsnes/lib/nall/string/convert.cpp | 284 ++ bsnes/lib/nall/string/core.cpp | 103 + bsnes/lib/nall/string/match.cpp | 71 + bsnes/lib/nall/string/math.cpp | 159 + bsnes/lib/nall/string/replace.cpp | 98 + bsnes/lib/nall/string/split.cpp | 51 + bsnes/lib/nall/string/strl.cpp | 47 + bsnes/lib/nall/string/trim.cpp | 35 + bsnes/lib/nall/string/utility.cpp | 74 + bsnes/lib/nall/traits.hpp | 94 + bsnes/lib/nall/ups.hpp | 191 ++ bsnes/lib/nall/utf8.hpp | 71 + bsnes/lib/nall/utility.hpp | 29 + bsnes/lib/nall/varint.hpp | 92 + bsnes/lib/nall/vector.hpp | 162 + bsnes/lib/ruby/audio.hpp | 28 + bsnes/lib/ruby/audio/alsa.cpp | 237 ++ bsnes/lib/ruby/audio/alsa.hpp | 23 + bsnes/lib/ruby/audio/ao.cpp | 97 + bsnes/lib/ruby/audio/ao.hpp | 23 + bsnes/lib/ruby/audio/directsound.cpp | 220 ++ bsnes/lib/ruby/audio/directsound.hpp | 24 + bsnes/lib/ruby/audio/openal.cpp | 207 ++ bsnes/lib/ruby/audio/openal.hpp | 24 + bsnes/lib/ruby/audio/oss.cpp | 116 + bsnes/lib/ruby/audio/oss.hpp | 23 + bsnes/lib/ruby/audio/pulseaudio.cpp | 121 + bsnes/lib/ruby/audio/pulseaudio.hpp | 23 + bsnes/lib/ruby/input.hpp | 24 + bsnes/lib/ruby/input/directinput.cpp | 403 +++ bsnes/lib/ruby/input/directinput.hpp | 22 + bsnes/lib/ruby/input/rawinput.cpp | 781 +++++ bsnes/lib/ruby/input/rawinput.hpp | 22 + bsnes/lib/ruby/input/sdl.cpp | 254 ++ bsnes/lib/ruby/input/sdl.hpp | 22 + bsnes/lib/ruby/input/x.cpp | 66 + bsnes/lib/ruby/input/x.hpp | 18 + bsnes/lib/ruby/input/xlibkeys.hpp | 138 + bsnes/lib/ruby/ruby.cpp | 317 ++ bsnes/lib/ruby/ruby.hpp | 106 + bsnes/lib/ruby/ruby_audio.cpp | 135 + bsnes/lib/ruby/ruby_impl.cpp | 78 + bsnes/lib/ruby/video.hpp | 28 + bsnes/lib/ruby/video/direct3d.cpp | 346 +++ bsnes/lib/ruby/video/direct3d.hpp | 22 + bsnes/lib/ruby/video/directdraw.cpp | 178 ++ bsnes/lib/ruby/video/directdraw.hpp | 22 + bsnes/lib/ruby/video/gdi.cpp | 103 + bsnes/lib/ruby/video/gdi.hpp | 21 + bsnes/lib/ruby/video/glx.cpp | 292 ++ bsnes/lib/ruby/video/glx.hpp | 22 + bsnes/lib/ruby/video/sdl.cpp | 137 + bsnes/lib/ruby/video/sdl.hpp | 22 + bsnes/lib/ruby/video/wgl.cpp | 214 ++ bsnes/lib/ruby/video/wgl.hpp | 22 + bsnes/lib/ruby/video/xv.cpp | 469 +++ bsnes/lib/ruby/video/xv.hpp | 22 + bsnes/lib/sync.bat | 9 + bsnes/lib/sync.sh | 6 + bsnes/lib/tool/opgen.cpp | 157 + bsnes/lib/tool/opgen_fnptr.cpp | 149 + bsnes/lib/tool/opgen_switch.cpp | 127 + bsnes/memory/memory.cpp | 112 + bsnes/memory/memory.hpp | 135 + bsnes/memory/smemory/mapper/chip.cpp | 54 + bsnes/memory/smemory/mapper/generic.cpp | 104 + bsnes/memory/smemory/mapper/system.cpp | 21 + bsnes/memory/smemory/smemory.cpp | 45 + bsnes/memory/smemory/smemory.hpp | 25 + bsnes/ppu/bppu/bppu.cpp | 346 +++ bsnes/ppu/bppu/bppu.hpp | 254 ++ bsnes/ppu/bppu/bppu_mmio.cpp | 839 +++++ bsnes/ppu/bppu/bppu_render.cpp | 150 + bsnes/ppu/bppu/bppu_render.hpp | 97 + bsnes/ppu/bppu/bppu_render_addsub.cpp | 25 + bsnes/ppu/bppu/bppu_render_bg.cpp | 211 ++ bsnes/ppu/bppu/bppu_render_cache.cpp | 151 + bsnes/ppu/bppu/bppu_render_line.cpp | 140 + bsnes/ppu/bppu/bppu_render_mode7.cpp | 148 + bsnes/ppu/bppu/bppu_render_oam.cpp | 224 ++ bsnes/ppu/bppu/bppu_render_windows.cpp | 99 + bsnes/ppu/counter.cpp | 50 + bsnes/ppu/counter.hpp | 73 + bsnes/ppu/ppu.cpp | 47 + bsnes/ppu/ppu.hpp | 42 + bsnes/reader/filereader.cpp | 50 + bsnes/reader/filereader.hpp | 12 + bsnes/reader/gzreader.cpp | 85 + bsnes/reader/gzreader.hpp | 16 + bsnes/reader/jma/7z.h | 28 + bsnes/reader/jma/7zlzma.cpp | 50 + bsnes/reader/jma/aribitcd.h | 73 + bsnes/reader/jma/ariconst.h | 29 + bsnes/reader/jma/ariprice.h | 12 + bsnes/reader/jma/btreecd.h | 126 + bsnes/reader/jma/crc32.h | 26 + bsnes/reader/jma/iiostrm.cpp | 132 + bsnes/reader/jma/iiostrm.h | 210 ++ bsnes/reader/jma/inbyte.cpp | 60 + bsnes/reader/jma/inbyte.h | 76 + bsnes/reader/jma/jcrc32.cpp | 80 + bsnes/reader/jma/jma.cpp | 550 ++++ bsnes/reader/jma/jma.h | 88 + bsnes/reader/jma/lencoder.h | 93 + bsnes/reader/jma/litcoder.h | 122 + bsnes/reader/jma/lzma.cpp | 41 + bsnes/reader/jma/lzma.h | 124 + bsnes/reader/jma/lzmadec.cpp | 298 ++ bsnes/reader/jma/lzmadec.h | 82 + bsnes/reader/jma/portable.h | 83 + bsnes/reader/jma/rcdefs.h | 60 + bsnes/reader/jma/rngcoder.h | 143 + bsnes/reader/jma/winout.cpp | 89 + bsnes/reader/jma/winout.h | 89 + bsnes/reader/jmareader.cpp | 38 + bsnes/reader/jmareader.hpp | 14 + bsnes/reader/reader.cpp | 37 + bsnes/reader/reader.hpp | 15 + bsnes/reader/zipreader.cpp | 61 + bsnes/reader/zipreader.hpp | 19 + bsnes/reader/zlib/adler32.c | 149 + bsnes/reader/zlib/compress.c | 79 + bsnes/reader/zlib/crc32.c | 423 +++ bsnes/reader/zlib/crc32.h | 441 +++ bsnes/reader/zlib/crypt.h | 132 + bsnes/reader/zlib/deflate.c | 1736 +++++++++++ bsnes/reader/zlib/deflate.h | 331 ++ bsnes/reader/zlib/gzio.c | 1026 +++++++ bsnes/reader/zlib/inffast.c | 724 +++++ bsnes/reader/zlib/inffast.h | 11 + bsnes/reader/zlib/inffixed.h | 94 + bsnes/reader/zlib/inflate.c | 1368 +++++++++ bsnes/reader/zlib/inflate.h | 115 + bsnes/reader/zlib/inftrees.c | 329 ++ bsnes/reader/zlib/inftrees.h | 55 + bsnes/reader/zlib/ioapi.c | 193 ++ bsnes/reader/zlib/ioapi.h | 75 + bsnes/reader/zlib/trees.c | 1219 ++++++++ bsnes/reader/zlib/trees.h | 128 + bsnes/reader/zlib/unzip.c | 1605 ++++++++++ bsnes/reader/zlib/unzip.h | 354 +++ bsnes/reader/zlib/zconf.h | 332 ++ bsnes/reader/zlib/zip.c | 1220 ++++++++ bsnes/reader/zlib/zip.h | 235 ++ bsnes/reader/zlib/zlib.h | 1357 +++++++++ bsnes/reader/zlib/zutil.c | 318 ++ bsnes/reader/zlib/zutil.h | 269 ++ bsnes/smp/dsmp.cpp | 312 ++ bsnes/smp/iplrom.hpp | 40 + bsnes/smp/smp.cpp | 5 + bsnes/smp/smp.hpp | 25 + bsnes/smp/smpregs.hpp | 31 + bsnes/smp/ssmp/core/cc.sh | 4 + bsnes/smp/ssmp/core/clean.sh | 1 + bsnes/smp/ssmp/core/core.cpp | 21 + bsnes/smp/ssmp/core/core.hpp | 19 + bsnes/smp/ssmp/core/op_misc.b | 163 + bsnes/smp/ssmp/core/op_misc.cpp | 349 +++ bsnes/smp/ssmp/core/op_mov.b | 217 ++ bsnes/smp/ssmp/core/op_mov.cpp | 392 +++ bsnes/smp/ssmp/core/op_pc.b | 179 ++ bsnes/smp/ssmp/core/op_pc.cpp | 606 ++++ bsnes/smp/ssmp/core/op_read.b | 205 ++ bsnes/smp/ssmp/core/op_read.cpp | 747 +++++ bsnes/smp/ssmp/core/op_rmw.b | 74 + bsnes/smp/ssmp/core/op_rmw.cpp | 265 ++ bsnes/smp/ssmp/core/opfn.cpp | 126 + bsnes/smp/ssmp/core/ssmpgen.cpp | 12 + bsnes/smp/ssmp/memory/memory.cpp | 269 ++ bsnes/smp/ssmp/memory/memory.hpp | 29 + bsnes/smp/ssmp/ssmp.cpp | 70 + bsnes/smp/ssmp/ssmp.hpp | 38 + bsnes/smp/ssmp/timing/timing.cpp | 13 + bsnes/smp/ssmp/timing/timing.hpp | 34 + bsnes/snes/audio/audio.cpp | 10 + bsnes/snes/audio/audio.hpp | 7 + bsnes/snes/input/input.cpp | 341 +++ bsnes/snes/input/input.hpp | 114 + bsnes/snes/interface/interface.hpp | 17 + bsnes/snes/scheduler/scheduler.cpp | 56 + bsnes/snes/scheduler/scheduler.hpp | 123 + bsnes/snes/snes.cpp | 210 ++ bsnes/snes/snes.hpp | 84 + bsnes/snes/tracer/tracer.cpp | 94 + bsnes/snes/tracer/tracer.hpp | 45 + bsnes/snes/video/video.cpp | 103 + bsnes/snes/video/video.hpp | 25 + bsnes/ui_qt/Makefile | 85 + bsnes/ui_qt/base/about.cpp | 35 + bsnes/ui_qt/base/about.hpp | 16 + bsnes/ui_qt/base/htmlviewer.cpp | 20 + bsnes/ui_qt/base/htmlviewer.hpp | 13 + bsnes/ui_qt/base/loader.cpp | 198 ++ bsnes/ui_qt/base/loader.hpp | 45 + bsnes/ui_qt/base/main.cpp | 371 +++ bsnes/ui_qt/base/main.hpp | 129 + bsnes/ui_qt/config.cpp | 237 ++ bsnes/ui_qt/input/device.cpp | 261 ++ bsnes/ui_qt/input/device.hpp | 73 + bsnes/ui_qt/input/input.cpp | 269 ++ bsnes/ui_qt/input/input.hpp | 88 + bsnes/ui_qt/input/userinterface.cpp | 56 + bsnes/ui_qt/input/userinterface.hpp | 33 + bsnes/ui_qt/interface.cpp | 31 + bsnes/ui_qt/main.cpp | 162 + bsnes/ui_qt/main.hpp | 62 + bsnes/ui_qt/platform.cpp | 99 + bsnes/ui_qt/resource/resource.qrc | 10 + bsnes/ui_qt/resource/resource.rc | 2 + bsnes/ui_qt/settings/advanced.cpp | 183 ++ bsnes/ui_qt/settings/advanced.hpp | 54 + bsnes/ui_qt/settings/audio.cpp | 129 + bsnes/ui_qt/settings/audio.hpp | 28 + bsnes/ui_qt/settings/cheateditor.cpp | 156 + bsnes/ui_qt/settings/cheateditor.hpp | 28 + bsnes/ui_qt/settings/input.cpp | 174 ++ bsnes/ui_qt/settings/input.hpp | 32 + bsnes/ui_qt/settings/paths.cpp | 200 ++ bsnes/ui_qt/settings/paths.hpp | 49 + bsnes/ui_qt/settings/settings.cpp | 84 + bsnes/ui_qt/settings/settings.hpp | 31 + bsnes/ui_qt/settings/utility/codeeditor.cpp | 180 ++ bsnes/ui_qt/settings/utility/codeeditor.hpp | 43 + bsnes/ui_qt/settings/utility/inputcapture.cpp | 454 +++ bsnes/ui_qt/settings/utility/inputcapture.hpp | 88 + bsnes/ui_qt/settings/video.cpp | 119 + bsnes/ui_qt/settings/video.hpp | 29 + bsnes/ui_qt/ui.cpp | 122 + bsnes/ui_qt/utility/cartridge.cpp | 163 + bsnes/ui_qt/utility/utility.cpp | 218 ++ bsnes/ui_qt/utility/utility.hpp | 37 + bsnes/ui_qt/utility/window.cpp | 123 + project.tmproj | 307 ++ pyusb/PKG-INFO | 14 + pyusb/README | 144 + pyusb/license.txt | 28 + pyusb/pyusb.c | 2149 +++++++++++++ pyusb/pyusb.h | 248 ++ pyusb/pyusb.sln | 19 + pyusb/samples/usbenum.py | 40 + pyusb/samples/usbprint.py | 125 + pyusb/setup.py | 64 + scripts/rom.py | 126 + scripts/roms.sqlite3 | Bin 0 -> 3072 bytes 413 files changed, 71887 insertions(+) create mode 100755 bsnes/Makefile create mode 100755 bsnes/base.hpp create mode 100755 bsnes/bsnes.lnk create mode 100755 bsnes/cart/cart.cpp create mode 100755 bsnes/cart/cart.hpp create mode 100755 bsnes/cart/cart_file.cpp create mode 100755 bsnes/cart/cart_header.cpp create mode 100755 bsnes/cart/cart_loader.cpp create mode 100755 bsnes/cc.bat create mode 100755 bsnes/cc.sh 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/cx4data.cpp create mode 100755 bsnes/chip/cx4/cx4fn.cpp create mode 100755 bsnes/chip/cx4/cx4oam.cpp create mode 100755 bsnes/chip/cx4/cx4ops.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/dsp2/dsp2.cpp create mode 100755 bsnes/chip/dsp2/dsp2.hpp create mode 100755 bsnes/chip/dsp2/dsp2_op.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/obc1/obc1.cpp create mode 100755 bsnes/chip/obc1/obc1.hpp 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/spc7110/decomp.cpp create mode 100755 bsnes/chip/spc7110/decomp.hpp create mode 100755 bsnes/chip/spc7110/spc7110.cpp create mode 100755 bsnes/chip/spc7110/spc7110.hpp create mode 100755 bsnes/chip/srtc/srtc.cpp create mode 100755 bsnes/chip/srtc/srtc.hpp create mode 100755 bsnes/chip/st010/st010.cpp create mode 100755 bsnes/chip/st010/st010.hpp create mode 100755 bsnes/chip/st010/st010_data.hpp create mode 100755 bsnes/chip/st010/st010_op.cpp create mode 100755 bsnes/clean.bat create mode 100755 bsnes/clean.sh create mode 100755 bsnes/cpu/cpu.cpp create mode 100755 bsnes/cpu/cpu.hpp create mode 100755 bsnes/cpu/cpuregs.hpp create mode 100755 bsnes/cpu/dcpu.cpp create mode 100755 bsnes/cpu/scpu/core/cc.sh create mode 100755 bsnes/cpu/scpu/core/clean.sh create mode 100755 bsnes/cpu/scpu/core/core.cpp create mode 100755 bsnes/cpu/scpu/core/core.hpp create mode 100755 bsnes/cpu/scpu/core/op_misc.b create mode 100755 bsnes/cpu/scpu/core/op_misc.cpp create mode 100755 bsnes/cpu/scpu/core/op_pc.b create mode 100755 bsnes/cpu/scpu/core/op_pc.cpp create mode 100755 bsnes/cpu/scpu/core/op_read.b create mode 100755 bsnes/cpu/scpu/core/op_read.cpp create mode 100755 bsnes/cpu/scpu/core/op_rmw.b create mode 100755 bsnes/cpu/scpu/core/op_rmw.cpp create mode 100755 bsnes/cpu/scpu/core/op_write.b create mode 100755 bsnes/cpu/scpu/core/op_write.cpp create mode 100755 bsnes/cpu/scpu/core/opfn.cpp create mode 100755 bsnes/cpu/scpu/core/scpugen.cpp 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/timing/event.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/data/bsnes.Manifest create mode 100755 bsnes/data/bsnes.desktop create mode 100755 bsnes/data/bsnes.ico create mode 100755 bsnes/data/bsnes.png create mode 100755 bsnes/data/documentation.html create mode 100755 bsnes/data/joypad.png create mode 100755 bsnes/data/license.html create mode 100755 bsnes/data/logo.png create mode 100755 bsnes/dsp/adsp/adsp.cpp create mode 100755 bsnes/dsp/adsp/adsp.hpp create mode 100755 bsnes/dsp/adsp/adsp_tables.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/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/voice.cpp create mode 100755 bsnes/interface.hpp create mode 100755 bsnes/lib/libco/fiber.c create mode 100755 bsnes/lib/libco/libco.c create mode 100755 bsnes/lib/libco/libco.h create mode 100755 bsnes/lib/libco/ppc.s create mode 100755 bsnes/lib/libco/ppc64.s create mode 100755 bsnes/lib/libco/sjlj.c create mode 100755 bsnes/lib/libco/ucontext.c create mode 100755 bsnes/lib/libco/x86-64.c create mode 100755 bsnes/lib/libco/x86.c create mode 100755 bsnes/lib/libfilter/colortable.cpp create mode 100755 bsnes/lib/libfilter/colortable.hpp create mode 100755 bsnes/lib/libfilter/direct.cpp create mode 100755 bsnes/lib/libfilter/direct.hpp create mode 100755 bsnes/lib/libfilter/filter.cpp create mode 100755 bsnes/lib/libfilter/filter.hpp create mode 100755 bsnes/lib/libfilter/hq2x.cpp create mode 100755 bsnes/lib/libfilter/hq2x.hpp create mode 100755 bsnes/lib/libfilter/hq2x_table.hpp create mode 100755 bsnes/lib/libfilter/libfilter.cpp create mode 100755 bsnes/lib/libfilter/libfilter.hpp create mode 100755 bsnes/lib/libfilter/ntsc.cpp create mode 100755 bsnes/lib/libfilter/ntsc.hpp create mode 100755 bsnes/lib/libfilter/scale2x.cpp create mode 100755 bsnes/lib/libfilter/scale2x.hpp create mode 100755 bsnes/lib/libfilter/scanline.cpp create mode 100755 bsnes/lib/libfilter/scanline.hpp create mode 100755 bsnes/lib/libfilter/snes_ntsc/snes_ntsc.c create mode 100755 bsnes/lib/libfilter/snes_ntsc/snes_ntsc.h create mode 100755 bsnes/lib/libfilter/snes_ntsc/snes_ntsc_config.h create mode 100755 bsnes/lib/libfilter/snes_ntsc/snes_ntsc_impl.h create mode 100755 bsnes/lib/nall/Makefile.string create mode 100755 bsnes/lib/nall/algorithm.hpp create mode 100755 bsnes/lib/nall/any.hpp create mode 100755 bsnes/lib/nall/array.hpp create mode 100755 bsnes/lib/nall/base64.hpp create mode 100755 bsnes/lib/nall/bit.hpp create mode 100755 bsnes/lib/nall/config.hpp create mode 100755 bsnes/lib/nall/crc32.hpp create mode 100755 bsnes/lib/nall/detect.hpp create mode 100755 bsnes/lib/nall/dictionary.hpp create mode 100755 bsnes/lib/nall/endian.hpp create mode 100755 bsnes/lib/nall/file.hpp create mode 100755 bsnes/lib/nall/filemap.hpp create mode 100755 bsnes/lib/nall/function.hpp create mode 100755 bsnes/lib/nall/input.hpp create mode 100755 bsnes/lib/nall/lzss.hpp create mode 100755 bsnes/lib/nall/moduloarray.hpp create mode 100755 bsnes/lib/nall/new.hpp create mode 100755 bsnes/lib/nall/platform.hpp create mode 100755 bsnes/lib/nall/priorityqueue.hpp create mode 100755 bsnes/lib/nall/property.hpp create mode 100755 bsnes/lib/nall/serial.hpp create mode 100755 bsnes/lib/nall/sort.hpp create mode 100755 bsnes/lib/nall/static.hpp create mode 100755 bsnes/lib/nall/stdint.hpp create mode 100755 bsnes/lib/nall/string.cpp create mode 100755 bsnes/lib/nall/string.hpp create mode 100755 bsnes/lib/nall/string/compare.cpp create mode 100755 bsnes/lib/nall/string/convert.cpp create mode 100755 bsnes/lib/nall/string/core.cpp create mode 100755 bsnes/lib/nall/string/match.cpp create mode 100755 bsnes/lib/nall/string/math.cpp create mode 100755 bsnes/lib/nall/string/replace.cpp create mode 100755 bsnes/lib/nall/string/split.cpp create mode 100755 bsnes/lib/nall/string/strl.cpp create mode 100755 bsnes/lib/nall/string/trim.cpp create mode 100755 bsnes/lib/nall/string/utility.cpp create mode 100755 bsnes/lib/nall/traits.hpp create mode 100755 bsnes/lib/nall/ups.hpp create mode 100755 bsnes/lib/nall/utf8.hpp create mode 100755 bsnes/lib/nall/utility.hpp create mode 100755 bsnes/lib/nall/varint.hpp create mode 100755 bsnes/lib/nall/vector.hpp create mode 100755 bsnes/lib/ruby/audio.hpp create mode 100755 bsnes/lib/ruby/audio/alsa.cpp create mode 100755 bsnes/lib/ruby/audio/alsa.hpp create mode 100755 bsnes/lib/ruby/audio/ao.cpp create mode 100755 bsnes/lib/ruby/audio/ao.hpp create mode 100755 bsnes/lib/ruby/audio/directsound.cpp create mode 100755 bsnes/lib/ruby/audio/directsound.hpp create mode 100755 bsnes/lib/ruby/audio/openal.cpp create mode 100755 bsnes/lib/ruby/audio/openal.hpp create mode 100755 bsnes/lib/ruby/audio/oss.cpp create mode 100755 bsnes/lib/ruby/audio/oss.hpp create mode 100755 bsnes/lib/ruby/audio/pulseaudio.cpp create mode 100755 bsnes/lib/ruby/audio/pulseaudio.hpp create mode 100755 bsnes/lib/ruby/input.hpp create mode 100755 bsnes/lib/ruby/input/directinput.cpp create mode 100755 bsnes/lib/ruby/input/directinput.hpp create mode 100755 bsnes/lib/ruby/input/rawinput.cpp create mode 100755 bsnes/lib/ruby/input/rawinput.hpp create mode 100755 bsnes/lib/ruby/input/sdl.cpp create mode 100755 bsnes/lib/ruby/input/sdl.hpp create mode 100755 bsnes/lib/ruby/input/x.cpp create mode 100755 bsnes/lib/ruby/input/x.hpp create mode 100755 bsnes/lib/ruby/input/xlibkeys.hpp create mode 100755 bsnes/lib/ruby/ruby.cpp create mode 100755 bsnes/lib/ruby/ruby.hpp create mode 100755 bsnes/lib/ruby/ruby_audio.cpp create mode 100755 bsnes/lib/ruby/ruby_impl.cpp create mode 100755 bsnes/lib/ruby/video.hpp create mode 100755 bsnes/lib/ruby/video/direct3d.cpp create mode 100755 bsnes/lib/ruby/video/direct3d.hpp create mode 100755 bsnes/lib/ruby/video/directdraw.cpp create mode 100755 bsnes/lib/ruby/video/directdraw.hpp create mode 100755 bsnes/lib/ruby/video/gdi.cpp create mode 100755 bsnes/lib/ruby/video/gdi.hpp create mode 100755 bsnes/lib/ruby/video/glx.cpp create mode 100755 bsnes/lib/ruby/video/glx.hpp create mode 100755 bsnes/lib/ruby/video/sdl.cpp create mode 100755 bsnes/lib/ruby/video/sdl.hpp create mode 100755 bsnes/lib/ruby/video/wgl.cpp create mode 100755 bsnes/lib/ruby/video/wgl.hpp create mode 100755 bsnes/lib/ruby/video/xv.cpp create mode 100755 bsnes/lib/ruby/video/xv.hpp create mode 100755 bsnes/lib/sync.bat create mode 100755 bsnes/lib/sync.sh create mode 100755 bsnes/lib/tool/opgen.cpp create mode 100755 bsnes/lib/tool/opgen_fnptr.cpp create mode 100755 bsnes/lib/tool/opgen_switch.cpp create mode 100755 bsnes/memory/memory.cpp create mode 100755 bsnes/memory/memory.hpp create mode 100755 bsnes/memory/smemory/mapper/chip.cpp create mode 100755 bsnes/memory/smemory/mapper/generic.cpp create mode 100755 bsnes/memory/smemory/mapper/system.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/bppu_mmio.cpp create mode 100755 bsnes/ppu/bppu/bppu_render.cpp create mode 100755 bsnes/ppu/bppu/bppu_render.hpp create mode 100755 bsnes/ppu/bppu/bppu_render_addsub.cpp create mode 100755 bsnes/ppu/bppu/bppu_render_bg.cpp create mode 100755 bsnes/ppu/bppu/bppu_render_cache.cpp create mode 100755 bsnes/ppu/bppu/bppu_render_line.cpp create mode 100755 bsnes/ppu/bppu/bppu_render_mode7.cpp create mode 100755 bsnes/ppu/bppu/bppu_render_oam.cpp create mode 100755 bsnes/ppu/bppu/bppu_render_windows.cpp create mode 100755 bsnes/ppu/counter.cpp create mode 100755 bsnes/ppu/counter.hpp create mode 100755 bsnes/ppu/ppu.cpp create mode 100755 bsnes/ppu/ppu.hpp create mode 100755 bsnes/reader/filereader.cpp create mode 100755 bsnes/reader/filereader.hpp create mode 100755 bsnes/reader/gzreader.cpp create mode 100755 bsnes/reader/gzreader.hpp create mode 100755 bsnes/reader/jma/7z.h create mode 100755 bsnes/reader/jma/7zlzma.cpp create mode 100755 bsnes/reader/jma/aribitcd.h create mode 100755 bsnes/reader/jma/ariconst.h create mode 100755 bsnes/reader/jma/ariprice.h create mode 100755 bsnes/reader/jma/btreecd.h create mode 100755 bsnes/reader/jma/crc32.h create mode 100755 bsnes/reader/jma/iiostrm.cpp create mode 100755 bsnes/reader/jma/iiostrm.h create mode 100755 bsnes/reader/jma/inbyte.cpp create mode 100755 bsnes/reader/jma/inbyte.h create mode 100755 bsnes/reader/jma/jcrc32.cpp create mode 100755 bsnes/reader/jma/jma.cpp create mode 100755 bsnes/reader/jma/jma.h create mode 100755 bsnes/reader/jma/lencoder.h create mode 100755 bsnes/reader/jma/litcoder.h create mode 100755 bsnes/reader/jma/lzma.cpp create mode 100755 bsnes/reader/jma/lzma.h create mode 100755 bsnes/reader/jma/lzmadec.cpp create mode 100755 bsnes/reader/jma/lzmadec.h create mode 100755 bsnes/reader/jma/portable.h create mode 100755 bsnes/reader/jma/rcdefs.h create mode 100755 bsnes/reader/jma/rngcoder.h create mode 100755 bsnes/reader/jma/winout.cpp create mode 100755 bsnes/reader/jma/winout.h create mode 100755 bsnes/reader/jmareader.cpp create mode 100755 bsnes/reader/jmareader.hpp create mode 100755 bsnes/reader/reader.cpp create mode 100755 bsnes/reader/reader.hpp create mode 100755 bsnes/reader/zipreader.cpp create mode 100755 bsnes/reader/zipreader.hpp create mode 100755 bsnes/reader/zlib/adler32.c create mode 100755 bsnes/reader/zlib/compress.c create mode 100755 bsnes/reader/zlib/crc32.c create mode 100755 bsnes/reader/zlib/crc32.h create mode 100755 bsnes/reader/zlib/crypt.h create mode 100755 bsnes/reader/zlib/deflate.c create mode 100755 bsnes/reader/zlib/deflate.h create mode 100755 bsnes/reader/zlib/gzio.c create mode 100755 bsnes/reader/zlib/inffast.c create mode 100755 bsnes/reader/zlib/inffast.h create mode 100755 bsnes/reader/zlib/inffixed.h create mode 100755 bsnes/reader/zlib/inflate.c create mode 100755 bsnes/reader/zlib/inflate.h create mode 100755 bsnes/reader/zlib/inftrees.c create mode 100755 bsnes/reader/zlib/inftrees.h create mode 100755 bsnes/reader/zlib/ioapi.c create mode 100755 bsnes/reader/zlib/ioapi.h create mode 100755 bsnes/reader/zlib/trees.c create mode 100755 bsnes/reader/zlib/trees.h create mode 100755 bsnes/reader/zlib/unzip.c create mode 100755 bsnes/reader/zlib/unzip.h create mode 100755 bsnes/reader/zlib/zconf.h create mode 100755 bsnes/reader/zlib/zip.c create mode 100755 bsnes/reader/zlib/zip.h create mode 100755 bsnes/reader/zlib/zlib.h create mode 100755 bsnes/reader/zlib/zutil.c create mode 100755 bsnes/reader/zlib/zutil.h create mode 100755 bsnes/smp/dsmp.cpp create mode 100755 bsnes/smp/iplrom.hpp create mode 100755 bsnes/smp/smp.cpp create mode 100755 bsnes/smp/smp.hpp create mode 100755 bsnes/smp/smpregs.hpp create mode 100755 bsnes/smp/ssmp/core/cc.sh create mode 100755 bsnes/smp/ssmp/core/clean.sh create mode 100755 bsnes/smp/ssmp/core/core.cpp create mode 100755 bsnes/smp/ssmp/core/core.hpp create mode 100755 bsnes/smp/ssmp/core/op_misc.b create mode 100755 bsnes/smp/ssmp/core/op_misc.cpp create mode 100755 bsnes/smp/ssmp/core/op_mov.b create mode 100755 bsnes/smp/ssmp/core/op_mov.cpp create mode 100755 bsnes/smp/ssmp/core/op_pc.b create mode 100755 bsnes/smp/ssmp/core/op_pc.cpp create mode 100755 bsnes/smp/ssmp/core/op_read.b create mode 100755 bsnes/smp/ssmp/core/op_read.cpp create mode 100755 bsnes/smp/ssmp/core/op_rmw.b create mode 100755 bsnes/smp/ssmp/core/op_rmw.cpp create mode 100755 bsnes/smp/ssmp/core/opfn.cpp create mode 100755 bsnes/smp/ssmp/core/ssmpgen.cpp create mode 100755 bsnes/smp/ssmp/memory/memory.cpp create mode 100755 bsnes/smp/ssmp/memory/memory.hpp 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/snes/audio/audio.cpp create mode 100755 bsnes/snes/audio/audio.hpp create mode 100755 bsnes/snes/input/input.cpp create mode 100755 bsnes/snes/input/input.hpp create mode 100755 bsnes/snes/interface/interface.hpp create mode 100755 bsnes/snes/scheduler/scheduler.cpp create mode 100755 bsnes/snes/scheduler/scheduler.hpp create mode 100755 bsnes/snes/snes.cpp create mode 100755 bsnes/snes/snes.hpp create mode 100755 bsnes/snes/tracer/tracer.cpp create mode 100755 bsnes/snes/tracer/tracer.hpp create mode 100755 bsnes/snes/video/video.cpp create mode 100755 bsnes/snes/video/video.hpp create mode 100755 bsnes/ui_qt/Makefile create mode 100755 bsnes/ui_qt/base/about.cpp create mode 100755 bsnes/ui_qt/base/about.hpp create mode 100755 bsnes/ui_qt/base/htmlviewer.cpp create mode 100755 bsnes/ui_qt/base/htmlviewer.hpp create mode 100755 bsnes/ui_qt/base/loader.cpp create mode 100755 bsnes/ui_qt/base/loader.hpp create mode 100755 bsnes/ui_qt/base/main.cpp create mode 100755 bsnes/ui_qt/base/main.hpp create mode 100755 bsnes/ui_qt/config.cpp create mode 100755 bsnes/ui_qt/input/device.cpp create mode 100755 bsnes/ui_qt/input/device.hpp create mode 100755 bsnes/ui_qt/input/input.cpp create mode 100755 bsnes/ui_qt/input/input.hpp create mode 100755 bsnes/ui_qt/input/userinterface.cpp create mode 100755 bsnes/ui_qt/input/userinterface.hpp create mode 100755 bsnes/ui_qt/interface.cpp create mode 100755 bsnes/ui_qt/main.cpp create mode 100755 bsnes/ui_qt/main.hpp create mode 100755 bsnes/ui_qt/platform.cpp create mode 100755 bsnes/ui_qt/resource/resource.qrc create mode 100755 bsnes/ui_qt/resource/resource.rc create mode 100755 bsnes/ui_qt/settings/advanced.cpp create mode 100755 bsnes/ui_qt/settings/advanced.hpp create mode 100755 bsnes/ui_qt/settings/audio.cpp create mode 100755 bsnes/ui_qt/settings/audio.hpp create mode 100755 bsnes/ui_qt/settings/cheateditor.cpp create mode 100755 bsnes/ui_qt/settings/cheateditor.hpp create mode 100755 bsnes/ui_qt/settings/input.cpp create mode 100755 bsnes/ui_qt/settings/input.hpp create mode 100755 bsnes/ui_qt/settings/paths.cpp create mode 100755 bsnes/ui_qt/settings/paths.hpp create mode 100755 bsnes/ui_qt/settings/settings.cpp create mode 100755 bsnes/ui_qt/settings/settings.hpp create mode 100755 bsnes/ui_qt/settings/utility/codeeditor.cpp create mode 100755 bsnes/ui_qt/settings/utility/codeeditor.hpp create mode 100755 bsnes/ui_qt/settings/utility/inputcapture.cpp create mode 100755 bsnes/ui_qt/settings/utility/inputcapture.hpp create mode 100755 bsnes/ui_qt/settings/video.cpp create mode 100755 bsnes/ui_qt/settings/video.hpp create mode 100755 bsnes/ui_qt/ui.cpp create mode 100755 bsnes/ui_qt/utility/cartridge.cpp create mode 100755 bsnes/ui_qt/utility/utility.cpp create mode 100755 bsnes/ui_qt/utility/utility.hpp create mode 100755 bsnes/ui_qt/utility/window.cpp create mode 100644 project.tmproj create mode 100644 pyusb/PKG-INFO create mode 100644 pyusb/README create mode 100644 pyusb/license.txt create mode 100644 pyusb/pyusb.c create mode 100644 pyusb/pyusb.h create mode 100644 pyusb/pyusb.sln create mode 100644 pyusb/samples/usbenum.py create mode 100644 pyusb/samples/usbprint.py create mode 100644 pyusb/setup.py create mode 100644 scripts/rom.py create mode 100644 scripts/roms.sqlite3 diff --git a/bsnes/Makefile b/bsnes/Makefile new file mode 100755 index 0000000..a765a58 --- /dev/null +++ b/bsnes/Makefile @@ -0,0 +1,278 @@ +include lib/nall/Makefile.string +prefix = /usr/local +ui = ui_qt + +################ +### compiler ### +################ + +ifneq ($(findstring gcc,$(compiler)),) # GCC family + flags = -O3 -fomit-frame-pointer -Ilib + # note: libco *requires* -fomit-frame-pointer on i386 arch + libcoflags := $(flags) -static + c = $(compiler) + cpp = $(subst cc,++,$(compiler)) + obj = o + rule = -c $< -o $@ + link = -s + mkbin = -o$1 + mkdef = -D$1 + mkincpath = -I$1 + mklib = -l$1 + mklibpath = -L$1 + + # profile-guided optimization: + # flags += -fprofile-generate + # link += -lgcov + # flags += -fprofile-use +else ifeq ($(compiler),cl) # Visual C++ + flags = /nologo /wd4355 /wd4805 /wd4996 /Ox /GL /EHsc /Ilib + libcoflags = $(flags) + c = cl + cpp = cl + obj = obj + rule = /c $< /Fo$@ + link = /link + mkbin = /Fe$1 + mkdef = /D$1 + mkincpath = /I$1 + mklib = $1.lib + mklibpath = /L$1 +else + unknown_compiler: help; +endif + +########## +### os ### +########## + +ifeq ($(platform),x) # X11 + ruby = video.glx video.xv video.sdl audio.alsa audio.openal audio.oss audio.pulseaudio audio.ao input.sdl input.x + delete = rm -f $1 +else ifeq ($(platform),win) # Windows + mingw_link_flags = -mwindows + # mingw_links_flags = -mconsole + + # enable static linking to Qt for Windows build + mingw_link_flags += -enable-stdcall-fixup -Wl,-s -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc + + ruby = video.direct3d video.wgl video.directdraw video.gdi audio.directsound input.rawinput input.directinput + delete = $(if $(findstring i586-mingw-gcc,$(compiler)),rm -f $1,del $(subst /,\,$1)) + link += $(if $(findstring mingw,$(compiler)),$(mingw_link_flags)) + link += $(call mklib,uuid) + link += $(call mklib,kernel32) + link += $(call mklib,user32) + link += $(call mklib,gdi32) + link += $(call mklib,shell32) +else + unknown_platform: help; +endif + +############ +### ruby ### +############ + +rubyflags = $(if $(findstring .sdl,$(ruby)),`sdl-config --cflags`) +link += $(if $(findstring .sdl,$(ruby)),`sdl-config --libs`) + +link += $(if $(findstring video.direct3d,$(ruby)),$(call mklib,d3d9)) +link += $(if $(findstring video.directdraw,$(ruby)),$(call mklib,ddraw)) +link += $(if $(findstring video.glx,$(ruby)),$(call mklib,GL)) +link += $(if $(findstring video.wgl,$(ruby)),$(call mklib,opengl32)) +link += $(if $(findstring video.xv,$(ruby)),$(call mklib,Xv)) +link += $(if $(findstring audio.alsa,$(ruby)),$(call mklib,asound)) +link += $(if $(findstring audio.ao,$(ruby)),$(call mklib,ao)) +link += $(if $(findstring audio.directsound,$(ruby)),$(call mklib,dsound)) +link += $(if $(findstring audio.openal,$(ruby)),$(if $(call streq,$(platform),x),$(call mklib,openal),$(call mklib,openal32))) +link += $(if $(findstring audio.pulseaudio,$(ruby)),$(call mklib,pulse-simple)) +link += $(if $(findstring input.directinput,$(ruby)),$(call mklib,dinput8) $(call mklib,dxguid)) +link += $(if $(findstring input.rawinput,$(ruby)),$(call mklib,xinput) $(call mklib,dinput8) $(call mklib,dxguid)) + +#################### +### core objects ### +#################### + +objects = libco ruby libfilter string \ + reader cart cheat \ + memory smemory cpu scpu smp ssmp sdsp ppu bppu snes \ + bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010 + +ifeq ($(enable_gzip),true) + objects += adler32 compress crc32 deflate gzio inffast inflate inftrees ioapi trees unzip zip zutil + flags += $(call mkdef,GZIP_SUPPORT) +endif + +ifeq ($(enable_jma),true) + objects += jma jcrc32 lzmadec 7zlzma iiostrm inbyte lzma winout + flags += $(call mkdef,JMA_SUPPORT) +endif + +###################### +### implicit rules ### +###################### + +compile = \ + $(strip \ + $(if $(filter %.c,$<), \ + $(c) $(flags) $1 $(rule), \ + $(if $(filter %.cpp,$<), \ + $(cpp) $(flags) $1 $(rule) \ + ) \ + ) \ + ) + +%.$(obj): $<; $(call compile) + +all: build; + +include $(ui)/Makefile +objects := $(patsubst %,obj/%.$(obj),$(objects)) +rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),$(call mkdef,$c)) + +################# +### libraries ### +################# + +obj/ruby.$(obj): lib/ruby/ruby.cpp lib/ruby/* lib/ruby/video/* lib/ruby/audio/* lib/ruby/input/* + $(call compile,$(rubydef) $(rubyflags)) +obj/libco.$(obj): lib/libco/libco.c lib/libco/* + $(c) $(libcoflags) $(rule) +obj/libfilter.$(obj): lib/libfilter/libfilter.cpp lib/libfilter/* +obj/string.$(obj): lib/nall/string.cpp lib/nall/* + +################# +### utilities ### +################# + +obj/reader.$(obj): reader/reader.cpp reader/* +obj/cart.$(obj) : cart/cart.cpp cart/* +obj/cheat.$(obj) : cheat/cheat.cpp cheat/* + +############## +### memory ### +############## + +obj/memory.$(obj) : memory/memory.cpp memory/* +obj/smemory.$(obj): memory/smemory/smemory.cpp memory/smemory/* memory/smemory/mapper/* + +########### +### cpu ### +########### + +obj/cpu.$(obj) : cpu/cpu.cpp cpu/* +obj/scpu.$(obj): cpu/scpu/scpu.cpp cpu/scpu/* cpu/scpu/core/* cpu/scpu/dma/* cpu/scpu/memory/* cpu/scpu/mmio/* cpu/scpu/timing/* + +########### +### smp ### +########### + +obj/smp.$(obj) : smp/smp.cpp smp/* +obj/ssmp.$(obj): smp/ssmp/ssmp.cpp smp/ssmp/* smp/ssmp/core/* smp/ssmp/memory/* smp/ssmp/timing/* + +########### +### dsp ### +########### + +obj/adsp.$(obj): dsp/adsp/adsp.cpp dsp/adsp/* +obj/sdsp.$(obj): dsp/sdsp/sdsp.cpp dsp/sdsp/* + +########### +### ppu ### +########### + +obj/ppu.$(obj) : ppu/ppu.cpp ppu/* +obj/bppu.$(obj): ppu/bppu/bppu.cpp ppu/bppu/* + +############ +### snes ### +############ + +obj/snes.$(obj): snes/snes.cpp snes/* snes/scheduler/* snes/video/* snes/audio/* snes/input/* + +##################### +### special chips ### +##################### + +obj/bsx.$(obj) : chip/bsx/bsx.cpp chip/bsx/* +obj/srtc.$(obj) : chip/srtc/srtc.cpp chip/srtc/* +obj/sdd1.$(obj) : chip/sdd1/sdd1.cpp chip/sdd1/* +obj/spc7110.$(obj): chip/spc7110/spc7110.cpp chip/spc7110/* +obj/cx4.$(obj) : chip/cx4/cx4.cpp chip/cx4/* +obj/dsp1.$(obj) : chip/dsp1/dsp1.cpp chip/dsp1/* +obj/dsp2.$(obj) : chip/dsp2/dsp2.cpp chip/dsp2/* +obj/dsp3.$(obj) : chip/dsp3/dsp3.cpp chip/dsp3/* +obj/dsp4.$(obj) : chip/dsp4/dsp4.cpp chip/dsp4/* +obj/obc1.$(obj) : chip/obc1/obc1.cpp chip/obc1/* +obj/st010.$(obj) : chip/st010/st010.cpp chip/st010/* + +############ +### zlib ### +############ + +obj/adler32.$(obj) : reader/zlib/adler32.c reader/zlib/* +obj/compress.$(obj): reader/zlib/compress.c reader/zlib/* +obj/crc32.$(obj) : reader/zlib/crc32.c reader/zlib/* +obj/deflate.$(obj) : reader/zlib/deflate.c reader/zlib/* +obj/gzio.$(obj) : reader/zlib/gzio.c reader/zlib/* +obj/inffast.$(obj) : reader/zlib/inffast.c reader/zlib/* +obj/inflate.$(obj) : reader/zlib/inflate.c reader/zlib/* +obj/inftrees.$(obj): reader/zlib/inftrees.c reader/zlib/* +obj/ioapi.$(obj) : reader/zlib/ioapi.c reader/zlib/* +obj/trees.$(obj) : reader/zlib/trees.c reader/zlib/* +obj/unzip.$(obj) : reader/zlib/unzip.c reader/zlib/* +obj/zip.$(obj) : reader/zlib/zip.c reader/zlib/* +obj/zutil.$(obj) : reader/zlib/zutil.c reader/zlib/* + +########### +### jma ### +########### + +obj/jma.$(obj) : reader/jma/jma.cpp reader/jma/* +obj/jcrc32.$(obj) : reader/jma/jcrc32.cpp reader/jma/* +obj/lzmadec.$(obj): reader/jma/lzmadec.cpp reader/jma/* +obj/7zlzma.$(obj) : reader/jma/7zlzma.cpp reader/jma/* +obj/iiostrm.$(obj): reader/jma/iiostrm.cpp reader/jma/* +obj/inbyte.$(obj) : reader/jma/inbyte.cpp reader/jma/* +obj/lzma.$(obj) : reader/jma/lzma.cpp reader/jma/* +obj/winout.$(obj) : reader/jma/winout.cpp reader/jma/* + +############### +### targets ### +############### + +build: ui_build $(objects) + $(strip $(cpp) $(call mkbin,../bsnes) $(objects) $(link)) + +install: + 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 + +clean: ui_clean + -@$(call delete,obj/*.$(obj)) + -@$(call delete,*.res) + -@$(call delete,*.pgd) + -@$(call delete,*.pgc) + -@$(call delete,*.ilk) + -@$(call delete,*.pdb) + -@$(call delete,*.manifest) + +help: + @echo "Usage: $(MAKE) platform=(os) compiler=(cc) [options]" + @echo "" + @echo "Supported platforms:" + @echo " x - Linux / BSD (x86, x86-64)" + @echo " win - Windows (x86, x86-64)" + @echo "" + @echo "Supported compilers:" + @echo " gcc - GCC compiler" + @echo " mingw32-gcc - MinGW compiler" + @echo " i586-mingw32-gcc - MinGW cross compiler" + @echo " cl - Visual C++" + @echo "" + @echo "Available options:" + @echo " enable_gzip=[true|false] - Enable ZIP / GZ support (default=false)" + @echo " enable_jma=[true|false] - Enable JMA support (default=false)" + @echo "" + @echo "Example: $(MAKE) platform=x compiler=gcc enable_gzip=true" + @echo "" diff --git a/bsnes/base.hpp b/bsnes/base.hpp new file mode 100755 index 0000000..ab2d5c3 --- /dev/null +++ b/bsnes/base.hpp @@ -0,0 +1,49 @@ +#define BSNES_VERSION "0.042" +#define BSNES_TITLE "bsnes v" BSNES_VERSION + +#define BUSCORE sBus +#define CPUCORE sCPU +#define SMPCORE sSMP +#define DSPCORE sDSP +#define PPUCORE 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 USE_STATE_MACHINE + +//FAST_FRAMESKIP disables calculation of RTO during frameskip +//frameskip offers near-zero speedup if RTO is calculated +//accuracy is not affected by this define when frameskipping is off +#define FAST_FRAMESKIP + +//game genie + pro action replay code support (~2% speed hit) +#define CHEAT_SYSTEM + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace nall; + +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; + +#include "interface.hpp" diff --git a/bsnes/bsnes.lnk b/bsnes/bsnes.lnk new file mode 100755 index 0000000000000000000000000000000000000000..7e2d037681103b13fc40159ffb8c6c6108a1a7d4 GIT binary patch literal 627 zcmeZaU|?VrVFHp239%ogs_vN9~z0;(|J1}Q_gtcW2W2!UD{L>PRaCTUxhX9+MQ73ZZEGgu&* z1QG|CWQf(IB!*&!Jcd*t2{J2!!H5CmK~t-_i3$+2^inHQ862SInOeohf#g8uol*i4 z4A~$82_VeV1DaX^#0(4)KWECP>95U zJp&=Z&S2nS&;!C4225Lc8KM}hfHJUI~GKMI6#n8Sz4-_l3JWyl3xJgYw-MIujAnGX}A9rl-{JjFEDs# W+Q#R11?Qe)VDV- +#include <../chip/chip.hpp> +#include <../reader/reader.hpp> +#define CART_CPP + +#include +#include + +#include "cart.hpp" +#include "cart_file.cpp" +#include "cart_header.cpp" +#include "cart_loader.cpp" + +namespace memory { + MappedRAM cartrom, cartram, cartrtc; + MappedRAM bscram; + MappedRAM stArom, stAram; + MappedRAM stBrom, stBram; +}; + +Cartridge cartridge; + +void Cartridge::load_begin(Mode cartridge_mode) { + cart.rom = cart.ram = cart.rtc = 0; + bs.ram = 0; + stA.rom = stA.ram = 0; + stB.rom = stB.ram = 0; + + cart.rom_size = cart.ram_size = cart.rtc_size = 0; + bs.ram_size = 0; + stA.rom_size = stA.ram_size = 0; + stB.rom_size = stB.ram_size = 0; + + set(loaded, false); + set(bsx_flash_loaded, false); + set(patched, false); + set(mode, cartridge_mode); +} + +void Cartridge::load_end() { + memory::cartrom.map(cart.rom, cart.rom_size); + memory::cartram.map(cart.ram, cart.ram_size); + memory::cartrtc.map(cart.rtc, cart.rtc_size); + memory::bscram.map(bs.ram, bs.ram_size); + memory::stArom.map(stA.rom, stA.rom_size); + memory::stAram.map(stA.ram, stA.ram_size); + memory::stBrom.map(stB.rom, stB.rom_size); + memory::stBram.map(stB.ram, stB.ram_size); + + memory::cartrom.write_protect(true); + memory::cartram.write_protect(false); + memory::bscram.write_protect(true); + memory::stArom.write_protect(true); + memory::stAram.write_protect(false); + memory::stBrom.write_protect(true); + memory::stBram.write_protect(false); + + string cheat_file = get_filename(cart.filename, "cht", snes.config.path.cheat); + if(file::exists(cheat_file)) { + cheat.clear(); + cheat.load(cheat_file); + } + + bus.load_cart(); + set(loaded, true); +} + +void Cartridge::unload() { + if(loaded() == false) return; + bus.unload_cart(); + + switch(mode()) { + case ModeNormal: unload_normal(); break; + case ModeBsxSlotted: unload_bsx_slotted(); break; + case ModeBsx: unload_bsx(); break; + case ModeSufamiTurbo: unload_sufami_turbo(); break; + } + + if(cart.rom) { delete[] cart.rom; cart.rom = 0; } + if(cart.ram) { delete[] cart.ram; cart.ram = 0; } + if(cart.rtc) { delete[] cart.rtc; cart.rtc = 0; } + if(bs.ram) { delete[] bs.ram; bs.ram = 0; } + if(stA.rom) { delete[] stA.rom; stA.rom = 0; } + if(stA.ram) { delete[] stA.ram; stA.ram = 0; } + if(stB.rom) { delete[] stB.rom; stB.rom = 0; } + if(stB.ram) { delete[] stB.ram; stB.ram = 0; } + + string cheat_file = get_filename(cart.filename, "cht", snes.config.path.cheat); + if(cheat.count() > 0 || file::exists(cheat_file)) { + cheat.save(cheat_file); + cheat.clear(); + } + + set(loaded, false); +} + +Cartridge::Cartridge() { + set(loaded, false); +} + +Cartridge::~Cartridge() { + if(loaded() == true) unload(); +} + +void Cartridge::set_cartinfo(const Cartridge::cartinfo_t &source) { + set(region, source.region); + set(mapper, source.mapper); + set(dsp1_mapper, source.dsp1_mapper); + + set(has_bsx_slot, source.bsx_slot); + set(has_superfx, source.superfx); + set(has_sa1, source.sa1); + set(has_srtc, source.srtc); + set(has_sdd1, source.sdd1); + set(has_spc7110, source.spc7110); + set(has_spc7110rtc, source.spc7110rtc); + set(has_cx4, source.cx4); + set(has_dsp1, source.dsp1); + set(has_dsp2, source.dsp2); + set(has_dsp3, source.dsp3); + set(has_dsp4, source.dsp4); + set(has_obc1, source.obc1); + set(has_st010, source.st010); + set(has_st011, source.st011); + set(has_st018, source.st018); +} + +//========== +//cartinfo_t +//========== + +void Cartridge::cartinfo_t::reset() { + type = TypeUnknown; + mapper = LoROM; + dsp1_mapper = DSP1Unmapped; + region = NTSC; + + rom_size = 0; + ram_size = 0; + + bsx_slot = false; + superfx = false; + sa1 = false; + srtc = false; + sdd1 = false; + spc7110 = false; + spc7110rtc = false; + cx4 = false; + dsp1 = false; + dsp2 = false; + dsp3 = false; + dsp4 = false; + obc1 = false; + st010 = false; + st011 = false; + st018 = false; +} + +Cartridge::cartinfo_t::cartinfo_t() { + reset(); +} + +//======= +//utility +//======= + +//ensure file path is absolute (eg resolve relative paths) +string Cartridge::filepath(const char *filename, const char *pathname) { + //if no pathname, return filename as-is + string file(filename); + file.replace("\\", "/"); + + string path = (!pathname || !*pathname) ? (const char*)snes.config.path.current : pathname; + //ensure path ends with trailing '/' + path.replace("\\", "/"); + if(!strend(path, "/")) path.append("/"); + + //replace relative path with absolute path + if(strbegin(path, "./")) { + ltrim(path, "./"); + path = string() << snes.config.path.base << path; + } + + //remove folder part of filename + lstring part; + part.split("/", file); + return path << part[part.size() - 1]; +} + +//remove directory information and file extension ("/foo/bar.ext" -> "bar") +string Cartridge::basename(const char *filename) { + string name(filename); + + //remove extension + for(signed i = strlen(name) - 1; i >= 0; i--) { + if(name[i] == '.') { + name[i] = 0; + break; + } + } + + //remove directory information + for(signed i = strlen(name) - 1; i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + i++; + char *output = name(); + while(true) { + *output++ = name[i]; + if(!name[i]) break; + i++; + } + break; + } + } + + return name; +} + +//remove filename and return path only ("/foo/bar.ext" -> "/foo/bar/") +string Cartridge::basepath(const char *filename) { + string path(filename); + path.replace("\\", "/"); + + //remove filename + for(signed i = strlen(path) - 1; i >= 0; i--) { + if(path[i] == '/') { + path[i] = 0; + break; + } + } + + if(!strend(path, "/")) path.append("/"); + return path; +} diff --git a/bsnes/cart/cart.hpp b/bsnes/cart/cart.hpp new file mode 100755 index 0000000..f2f399a --- /dev/null +++ b/bsnes/cart/cart.hpp @@ -0,0 +1,178 @@ +class Cartridge : public property { +public: + enum Mode { + ModeNormal, + ModeBsxSlotted, + ModeBsx, + ModeSufamiTurbo, + }; + + enum Type { + TypeNormal, + TypeBsxSlotted, + TypeBsxBios, + TypeBsx, + TypeSufamiTurboBios, + TypeSufamiTurbo, + TypeUnknown, + }; + + enum Region { + NTSC, + PAL, + }; + + enum MemoryMapper { + LoROM, + HiROM, + ExLoROM, + ExHiROM, + SPC7110ROM, + BSCLoROM, + BSCHiROM, + BSXROM, + STROM, + }; + + enum DSP1MemoryMapper { + DSP1Unmapped, + DSP1LoROM1MB, + DSP1LoROM2MB, + DSP1HiROM, + }; + + //properties can be read via operator(), eg "if(cartridge.loaded() == true)"; + //warning: if loaded() == false, no other property is considered valid! + + property_t loaded; //is a base cartridge inserted? + property_t bsx_flash_loaded; //is a BS-X flash cart connected? + property_t patched; //has a UPS patch been applied? + property_t name; //display name (filename sans path and extension) + + property_t mode; + property_t region; + property_t mapper; + property_t dsp1_mapper; + + property_t has_bsx_slot; + property_t has_superfx; + property_t has_sa1; + property_t has_srtc; + property_t has_sdd1; + property_t has_spc7110, has_spc7110rtc; + property_t has_cx4; + property_t has_dsp1, has_dsp2, has_dsp3, has_dsp4; + property_t has_obc1; + property_t has_st010, has_st011, has_st018; + + //main interface + bool load_normal (const char *base); + bool load_bsx_slotted (const char *base, const char *slot = ""); + bool load_bsx (const char *base, const char *slot = ""); + bool load_sufami_turbo(const char *base, const char *slotA = "", const char *slotB = ""); + void unload(); + + //utility functions + static string filepath(const char *filename, const char *pathname); //"./bar.ext" -> "/foo/bar.ext" + static string basename(const char *filename); //"/foo/bar.ext" -> "bar" + static string basepath(const char *filename); //"/foo/bar.ext" -> "/foo/bar/" + //this function will load 'filename', decompress it if needed, and determine what type of + //image file 'filename' refers to (eg normal cart, BS-X flash cart, Sufami Turbo cart, etc.) + //warning: this operation is very expensive, use sparingly! + Type detect_image_type(const char *filename) const; + + Cartridge(); + ~Cartridge(); + +private: + void load_begin(Mode); + void load_end(); + void unload_normal(); + void unload_bsx_slotted(); + void unload_bsx(); + void unload_sufami_turbo(); + + struct cartinfo_t { + Type type; + Region region; + MemoryMapper mapper; + DSP1MemoryMapper dsp1_mapper; + unsigned rom_size, ram_size; + + bool bsx_slot; + bool superfx; + bool sa1; + bool srtc; + bool sdd1; + bool spc7110, spc7110rtc; + bool cx4; + bool dsp1, dsp2, dsp3, dsp4; + bool obc1; + bool st010, st011, st018; + + void reset(); + cartinfo_t(); + }; + + enum HeaderField { + CartName = 0x00, + Mapper = 0x15, + RomType = 0x16, + RomSize = 0x17, + RamSize = 0x18, + CartRegion = 0x19, + Company = 0x1a, + Version = 0x1b, + Complement = 0x1c, //inverse checksum + Checksum = 0x1e, + ResetVector = 0x3c, + }; + + void read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const; + unsigned find_header(const uint8_t *data, unsigned size) const; + unsigned score_header(const uint8_t *data, unsigned size, unsigned addr) const; + void set_cartinfo(const cartinfo_t&); + + bool load_image(const char *filename, uint8_t *&data, unsigned &size, bool &patched) const; + bool load_ram (const char *filename, uint8_t *&data, unsigned size, uint8_t init_value) const; + + enum CompressionMode { + CompressionNone, //always load without compression + CompressionInspect, //use file header inspection + CompressionAuto, //use file extension or file header inspection (configured by user) + }; + + bool load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression = CompressionNone) const; + bool save_file(const char *fn, uint8 *data, unsigned size) const; + bool apply_patch(const uint8_t *pdata, unsigned psize, uint8_t *&data, unsigned &size) const; + + string modify_extension(const char *filename, const char *extension) const; + string get_filename(const char *source, const char *extension, const char *path) const; + + struct { + string filename; + uint8_t *rom, *ram, *rtc; + unsigned rom_size, ram_size, rtc_size; + } cart; + + struct { + string filename; + uint8_t *ram; + unsigned ram_size; + } bs; + + struct { + string filename; + uint8_t *rom, *ram; + unsigned rom_size, ram_size; + } stA, stB; +}; + +namespace memory { + extern MappedRAM cartrom, cartram, cartrtc; + extern MappedRAM bscram; + extern MappedRAM stArom, stAram; + extern MappedRAM stBrom, stBram; +}; + +extern Cartridge cartridge; diff --git a/bsnes/cart/cart_file.cpp b/bsnes/cart/cart_file.cpp new file mode 100755 index 0000000..1410229 --- /dev/null +++ b/bsnes/cart/cart_file.cpp @@ -0,0 +1,109 @@ +#ifdef CART_CPP + +#include "../reader/filereader.hpp" + +#if defined(GZIP_SUPPORT) + #include "../reader/gzreader.hpp" + #include "../reader/zipreader.hpp" +#endif + +#if defined(JMA_SUPPORT) + #include "../reader/jmareader.hpp" +#endif + +string Cartridge::modify_extension(const char *filename_, const char *extension) const { + string filename = filename_; + int i; + for(i = strlen(filename); i >= 0; i--) { + if(filename[i] == '.') break; + if(filename[i] == '/') break; + if(filename[i] == '\\') break; + } + if(i > 0 && filename[i] == '.') filename[i] = 0; + return filename << "." << extension; +} + +string Cartridge::get_filename(const char *source, const char *extension, const char *path) const { + return filepath(modify_extension(source, extension), path); +} + +bool Cartridge::load_file(const char *fn, uint8 *&data, unsigned &size, CompressionMode compression) const { + if(file::exists(fn) == false) return false; + + Reader::Type filetype = Reader::Normal; + if(compression == CompressionInspect) filetype = Reader::detect(fn, true); + if(compression == CompressionAuto) filetype = Reader::detect(fn, snes.config.file.autodetect_type); + + switch(filetype) { default: + case Reader::Normal: { + FileReader ff(fn); + if(!ff.ready()) return false; + size = ff.size(); + data = ff.read(); + } break; + + #ifdef GZIP_SUPPORT + case Reader::GZIP: { + GZReader gf(fn); + if(!gf.ready()) return false; + size = gf.size(); + data = gf.read(); + } break; + + case Reader::ZIP: { + ZipReader zf(fn); + if(!zf.ready()) return false; + size = zf.size(); + data = zf.read(); + } break; + #endif + + #ifdef JMA_SUPPORT + case Reader::JMA: { + try { + JMAReader jf(fn); + size = jf.size(); + data = jf.read(); + } catch(JMA::jma_errors jma_error) { + return false; + } + } break; + #endif + } + + return true; +} + +bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t *&data, unsigned &size) const { + uint8_t *outdata = 0; + unsigned outsize; + ups patcher; + ups::result result = patcher.apply(pdata, psize, data, size, outdata, outsize); + + bool apply = false; + if(result == ups::ok) apply = true; + if(snes.config.file.bypass_patch_crc32 == true) { + if(result == ups::input_crc32_invalid) apply = true; + if(result == ups::output_crc32_invalid) apply = true; + } + + //if patch application was successful, replace old data, size with new data, size + if(apply == true) { + delete[] data; + data = new uint8_t[size = outsize]; + memcpy(data, outdata, outsize); + } + + if(outdata) delete[] outdata; + return apply; +} + +bool Cartridge::save_file(const char *fn, uint8 *data, unsigned size) const { + file fp; + if(!fp.open(fn, file::mode_write)) return false; + fp.write(data, size); + fp.close(); + return true; +} + +#endif diff --git a/bsnes/cart/cart_header.cpp b/bsnes/cart/cart_header.cpp new file mode 100755 index 0000000..b99a1d4 --- /dev/null +++ b/bsnes/cart/cart_header.cpp @@ -0,0 +1,272 @@ +#ifdef CART_CPP + +void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const { + info.reset(); + unsigned index = find_header(data, size); + + //======================= + //detect BS-X flash carts + //======================= + + if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) { + if(data[index + 0x14] == 0x00) { + const uint8_t n15 = data[index + 0x15]; + if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { + if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { + info.type = TypeBsx; + info.mapper = BSXROM; + info.region = NTSC; //BS-X only released in Japan + return; + } + } + } + } + + //========================= + //detect Sufami Turbo carts + //========================= + + if(!memcmp(data, "BANDAI SFC-ADX", 14)) { + if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { + info.type = TypeSufamiTurboBios; + } else { + info.type = TypeSufamiTurbo; + } + info.mapper = STROM; + info.region = NTSC; //Sufami Turbo only released in Japan + return; //RAM size handled internally by load_cart_st(); + } + + //===================== + //detect standard carts + //===================== + + const uint8 mapper = data[index + Mapper]; + const uint8 rom_type = data[index + RomType]; + const uint8 rom_size = data[index + RomSize]; + const uint8 company = data[index + Company]; + const uint8 region = data[index + CartRegion] & 0x7f; + + //detect presence of BS-X flash cartridge connector (reads extended header information) + if(data[index - 14] == 'Z') { + if(data[index - 11] == 'J') { + uint8 n13 = data[index - 13]; + if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { + if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { + info.bsx_slot = true; + } + } + } + } + + if(info.bsx_slot == true) { + if(!memcmp(data + index, "Satellaview BS-X ", 21)) { + //BS-X base cart + info.type = TypeBsxBios; + info.mapper = BSXROM; + info.region = NTSC; //BS-X only released in Japan + return; //RAM size handled internally by load_cart_bsx() -> BSXCart class + } else { + info.type = TypeBsxSlotted; + info.mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); + } + } else { + //standard cart + info.type = TypeNormal; + + if(index == 0x7fc0 && size >= 0x401000) { + info.mapper = ExLoROM; + } else if(index == 0x7fc0 && mapper == 0x32) { + info.mapper = ExLoROM; + } else if(index == 0x7fc0) { + info.mapper = LoROM; + } else if(index == 0xffc0) { + info.mapper = HiROM; + } else { //index == 0x40ffc0 + info.mapper = ExHiROM; + } + } + + if(mapper == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { + info.superfx = true; + } + + if(mapper == 0x23 && (rom_type == 0x34 || rom_type == 0x35)) { + info.sa1 = true; + } + + if(mapper == 0x35 && rom_type == 0x55) { + info.srtc = true; + } + + if(mapper == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + info.sdd1 = true; + } + + if(mapper == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { + info.spc7110 = true; + info.spc7110rtc = (rom_type == 0xf9); + info.mapper = SPC7110ROM; + } + + if(mapper == 0x20 && rom_type == 0xf3) { + info.cx4 = true; + } + + if((mapper == 0x20 || mapper == 0x21) && rom_type == 0x03) { + info.dsp1 = true; + } + + if(mapper == 0x30 && rom_type == 0x05 && company != 0xb2) { + info.dsp1 = true; + } + + if(mapper == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { + info.dsp1 = true; + } + + if(info.dsp1 == true) { + if((mapper & 0x2f) == 0x20 && size <= 0x100000) { + info.dsp1_mapper = DSP1LoROM1MB; + } else if((mapper & 0x2f) == 0x20) { + info.dsp1_mapper = DSP1LoROM2MB; + } else if((mapper & 0x2f) == 0x21) { + info.dsp1_mapper = DSP1HiROM; + } + } + + if(mapper == 0x20 && rom_type == 0x05) { + info.dsp2 = true; + } + + if(mapper == 0x30 && rom_type == 0x05 && company == 0xb2) { + info.dsp3 = true; + } + + if(mapper == 0x30 && rom_type == 0x03) { + info.dsp4 = true; + } + + if(mapper == 0x30 && rom_type == 0x25) { + info.obc1 = true; + } + + if(mapper == 0x30 && rom_type == 0xf6 && rom_size >= 10) { + info.st010 = true; + } + + if(mapper == 0x30 && rom_type == 0xf6 && rom_size < 10) { + info.st011 = true; + } + + if(mapper == 0x30 && rom_type == 0xf5) { + info.st018 = true; + } + + if(data[index + RamSize] & 7) { + info.ram_size = 1024 << (data[index + RamSize] & 7); + } else { + info.ram_size = 0; + } + + //0, 1, 13 = NTSC; 2 - 12 = PAL + info.region = (region <= 1 || region >= 13) ? NTSC : PAL; +} + +unsigned Cartridge::find_header(const uint8_t *data, unsigned size) const { + unsigned score_lo = score_header(data, size, 0x007fc0); + unsigned score_hi = score_header(data, size, 0x00ffc0); + unsigned score_ex = score_header(data, size, 0x40ffc0); + if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits + + if(score_lo >= score_hi && score_lo >= score_ex) { + return 0x007fc0; + } else if(score_hi >= score_ex) { + return 0x00ffc0; + } else { + return 0x40ffc0; + } +} + +unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) const { + if(size < addr + 64) return 0; //image too small to contain header at this location? + int score = 0; + + uint16 resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); + uint16 checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); + uint16 complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); + + uint8 resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset + uint8 mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit + + //$00:[000-7fff] contains uninitialized RAM and MMIO. + //reset vector must point to ROM at $00:[8000-ffff] to be considered valid. + if(resetvector < 0x8000) return 0; + + //some images duplicate the header in multiple locations, and others have completely + //invalid header information that cannot be relied upon. + //below code will analyze the first opcode executed at the specified reset vector to + //determine the probability that this is the correct header. + + //most likely opcodes + if(resetop == 0x78 //sei + || resetop == 0x18 //clc (clc; xce) + || resetop == 0x38 //sec (sec; xce) + || resetop == 0x9c //stz $nnnn (stz $4200) + || resetop == 0x4c //jmp $nnnn + || resetop == 0x5c //jml $nnnnnn + ) score += 8; + + //plausible opcodes + if(resetop == 0xc2 //rep #$nn + || resetop == 0xe2 //sep #$nn + || resetop == 0xad //lda $nnnn + || resetop == 0xae //ldx $nnnn + || resetop == 0xac //ldy $nnnn + || resetop == 0xaf //lda $nnnnnn + || resetop == 0xa9 //lda #$nn + || resetop == 0xa2 //ldx #$nn + || resetop == 0xa0 //ldy #$nn + || resetop == 0x20 //jsr $nnnn + || resetop == 0x22 //jsl $nnnnnn + ) score += 4; + + //implausible opcodes + if(resetop == 0x40 //rti + || resetop == 0x60 //rts + || resetop == 0x6b //rtl + || resetop == 0xcd //cmp $nnnn + || resetop == 0xec //cpx $nnnn + || resetop == 0xcc //cpy $nnnn + ) score -= 4; + + //least likely opcodes + if(resetop == 0x00 //brk #$nn + || resetop == 0x02 //cop #$nn + || resetop == 0xdb //stp + || resetop == 0x42 //wdm + || resetop == 0xff //sbc $nnnnnn,x + ) score -= 8; + + //at times, both the header and reset vector's first opcode will match ... + //fallback and rely on info validity in these cases to determine more likely header. + + //a valid checksum is the biggest indicator of a valid header. + if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; + + if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM + if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM + if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM + if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM + + if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header + if(data[addr + RomType] < 0x08) score++; + if(data[addr + RomSize] < 0x10) score++; + if(data[addr + RamSize] < 0x08) score++; + if(data[addr + CartRegion] < 14) score++; + + if(score < 0) score = 0; + return score; +} + +#endif diff --git a/bsnes/cart/cart_loader.cpp b/bsnes/cart/cart_loader.cpp new file mode 100755 index 0000000..283496e --- /dev/null +++ b/bsnes/cart/cart_loader.cpp @@ -0,0 +1,244 @@ +#ifdef CART_CPP + +//================ +//Normal cartridge +//================ + +bool Cartridge::load_normal(const char *base) { + uint8_t *data; + unsigned size; + bool patch_applied; + cart.filename = base; + + load_begin(ModeNormal); + if(load_image(base, data, size, patch_applied) == false) return false; + + snes.config.path.current = basepath(cart.filename); + if(patch_applied) set(patched, true); + + cartinfo_t cartinfo; + read_header(cartinfo, cart.rom = data, cart.rom_size = size); + set_cartinfo(cartinfo); + + if(cartinfo.ram_size > 0) { + load_ram(get_filename(base, "srm", snes.config.path.save), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff); + } + + if(cartinfo.srtc || cartinfo.spc7110rtc) { + load_ram(get_filename(base, "rtc", snes.config.path.save), cart.rtc, cart.rtc_size = 20, 0x00); + } + + load_end(); + set(name, basename(base)); + return true; +} + +void Cartridge::unload_normal() { + if(cart.ram) save_file(get_filename(cart.filename, "srm", snes.config.path.save), cart.ram, cart.ram_size); + if(cart.rtc) save_file(get_filename(cart.filename, "rtc", snes.config.path.save), cart.rtc, cart.rtc_size); +} + +//====================== +//BS-X slotted cartridge +//====================== + +bool Cartridge::load_bsx_slotted(const char *base, const char *slot) { + uint8_t *data; + unsigned size; + bool patch_applied; + cart.filename = base; + bs.filename = slot; + + load_begin(ModeBsxSlotted); + if(load_image(base, data, size, patch_applied) == false) return false; + + snes.config.path.current = basepath(cart.filename); + if(patch_applied) set(patched, true); + + cartinfo_t cartinfo; + read_header(cartinfo, cart.rom = data, cart.rom_size = size); + set_cartinfo(cartinfo); + + if(load_image(slot, data, size, patch_applied) == true) { + set(bsx_flash_loaded, true); + if(patch_applied) set(patched, true); + bs.ram = data; + bs.ram_size = size; + } + + if(cartinfo.ram_size > 0) { + load_ram(get_filename(base, "srm", snes.config.path.save), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff); + } + + load_end(); + string filename = basename(base); + if(*slot) filename << " + " << basename(slot); + set(name, filename); + return true; +} + +void Cartridge::unload_bsx_slotted() { + if(cart.ram) save_file(get_filename(cart.filename, "srm", snes.config.path.save), cart.ram, cart.ram_size); +} + +//==================== +//BS-X flash cartridge +//==================== + +bool Cartridge::load_bsx(const char *base, const char *slot) { + uint8_t *data; + unsigned size; + bool patch_applied; + cart.filename = base; + bs.filename = slot; + + load_begin(ModeBsx); + if(load_image(base, data, size, patch_applied) == false) return false; + + snes.config.path.current = basepath(cart.filename); + if(patch_applied) set(patched, true); + + cartinfo_t cartinfo; + read_header(cartinfo, cart.rom = data, cart.rom_size = size); + set_cartinfo(cartinfo); + + cart.ram = 0; + cart.ram_size = 0; + + memset(bsxcart.sram.handle (), 0x00, bsxcart.sram.size ()); + memset(bsxcart.psram.handle(), 0x00, bsxcart.psram.size()); + + if(load_file(get_filename(base, "srm", snes.config.path.save), data, size, CompressionNone) == true) { + memcpy(bsxcart.sram.handle (), data, min(bsxcart.sram.size (), size)); + delete[] data; + } + + if(load_file(get_filename(base, "psr", snes.config.path.save), data, size, CompressionNone) == true) { + memcpy(bsxcart.psram.handle(), data, min(bsxcart.psram.size(), size)); + delete[] data; + } + + if(load_image(slot, data, size, patch_applied) == true) { + set(bsx_flash_loaded, true); + if(patch_applied) set(patched, true); + bs.ram = data; + bs.ram_size = size; + } + + load_end(); + set(name, !*slot ? basename(base) : basename(slot)); + return true; +} + +void Cartridge::unload_bsx() { + save_file(get_filename(cart.filename, "srm", snes.config.path.save), bsxcart.sram.handle (), bsxcart.sram.size ()); + save_file(get_filename(cart.filename, "psr", snes.config.path.save), bsxcart.psram.handle(), bsxcart.psram.size()); +} + +//============================ +//Sufami Turbo flash cartridge +//============================ + +bool Cartridge::load_sufami_turbo(const char *base, const char *slotA, const char *slotB) { + uint8_t *data; + unsigned size; + bool patch_applied; + cart.filename = base; + stA.filename = slotA; + stB.filename = slotB; + + load_begin(ModeSufamiTurbo); + if(load_image(base, data, size, patch_applied) == false) return false; + + snes.config.path.current = basepath(cart.filename); + if(patch_applied) set(patched, true); + + cartinfo_t cartinfo; + read_header(cartinfo, cart.rom = data, cart.rom_size = size); + set_cartinfo(cartinfo); + + if(load_image(slotA, data, size, patch_applied) == true) { + if(patch_applied) set(patched, true); + stA.rom = new(zeromemory) uint8_t[stA.rom_size = 0x100000]; + memcpy(stA.rom, data, min(size, stA.rom_size)); + delete[] data; + + load_ram(get_filename(slotA, "srm", snes.config.path.save), stA.ram, stA.ram_size = 0x020000, 0xff); + } + + if(load_image(slotB, data, size, patch_applied) == true) { + if(patch_applied) set(patched, true); + stB.rom = new(zeromemory) uint8_t[stB.rom_size = 0x100000]; + memcpy(stB.rom, data, min(size, stB.rom_size)); + delete[] data; + + load_ram(get_filename(slotB, "srm", snes.config.path.save), stB.ram, stB.ram_size = 0x020000, 0xff); + } + + load_end(); + string filename; + if(!*slotA && !*slotB) filename << basename(base); + else if( *slotA && !*slotB) filename << basename(slotA); + else if(!*slotA && *slotB) filename << basename(slotB); + else filename << basename(slotA) << " + " << basename(slotB); + set(name, filename); + return true; +} + +void Cartridge::unload_sufami_turbo() { + if(stA.ram) save_file(get_filename(stA.filename, "srm", snes.config.path.save), stA.ram, stA.ram_size); + if(stB.ram) save_file(get_filename(stB.filename, "srm", snes.config.path.save), stB.ram, stB.ram_size); +} + +//================= +//utility functions +//================= + +Cartridge::Type Cartridge::detect_image_type(const char *filename) const { + uint8_t *data; + unsigned size; + bool patch_applied; + if(!load_image(filename, data, size, patch_applied)) return TypeUnknown; + + cartinfo_t info; + read_header(info, data, size); + delete[] data; + return info.type; +} + +bool Cartridge::load_image(const char *filename, uint8_t *&data, unsigned &size, bool &patched) const { + if(!filename || !*filename) return false; + if(!load_file(filename, data, size, CompressionAuto)) return false; + + if((size & 0x7fff) == 512) { + //remove 512-byte header + memmove(data, data + 512, size -= 512); + } + + uint8_t *pdata; + unsigned psize; + if(load_file(get_filename(filename, "ups", snes.config.path.patch), pdata, psize, CompressionInspect) == true) { + apply_patch(pdata, psize, data, size); + delete[] pdata; + patched = true; + } else { + patched = false; + } + + return true; +} + +bool Cartridge::load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init) const { + data = new uint8_t[size]; + memset(data, init, size); + + uint8_t *savedata; + unsigned savesize; + if(load_file(filename, savedata, savesize, CompressionNone) == false) return false; + + memcpy(data, savedata, min(size, savesize)); + delete[] savedata; + return true; +} + +#endif diff --git a/bsnes/cc.bat b/bsnes/cc.bat new file mode 100755 index 0000000..ecdff18 --- /dev/null +++ b/bsnes/cc.bat @@ -0,0 +1,3 @@ +::@mingw32-make platform=win compiler=mingw32-gcc +@mingw32-make platform=win compiler=mingw32-gcc enable_gzip=true enable_jma=true +@pause diff --git a/bsnes/cc.sh b/bsnes/cc.sh new file mode 100755 index 0000000..51da6a6 --- /dev/null +++ b/bsnes/cc.sh @@ -0,0 +1,2 @@ +make platform=x compiler=gcc +#make platform=x compiler=gcc enable_gzip=true enable_jma=true diff --git a/bsnes/cheat/cheat.cpp b/bsnes/cheat/cheat.cpp new file mode 100755 index 0000000..b6f95e3 --- /dev/null +++ b/bsnes/cheat/cheat.cpp @@ -0,0 +1,392 @@ +#include <../base.hpp> + +Cheat cheat; + +Cheat::cheat_t& Cheat::cheat_t::operator=(const Cheat::cheat_t& source) { + enabled = source.enabled; + code = source.code; + desc = source.desc; + count = source.count; + + addr.reset(); + data.reset(); + for(unsigned n = 0; n < count; n++) { + addr[n] = source.addr[n]; + data[n] = source.data[n]; + } + + return *this; +} + +//used to sort cheat code list by description +bool Cheat::cheat_t::operator<(const Cheat::cheat_t& source) { + return strcmp(desc, source.desc) < 0; +} + +//parse item ("0123-4567+89AB-CDEF"), return cheat_t item +//return true if code is valid, false otherwise +bool Cheat::decode(const char *s, Cheat::cheat_t &item) const { + item.enabled = false; + item.count = 0; + + lstring list; + list.split("+", s); + + for(unsigned n = 0; n < list.size(); n++) { + unsigned addr; + uint8_t data; + type_t type; + if(decode(list[n], addr, data, type) == false) return false; + + item.addr[item.count] = addr; + item.data[item.count] = data; + item.count++; + } + + return true; +} + +//read() is used by MemBus::read() if Cheat::enabled(addr) returns true to look up cheat code. +//returns true if cheat code was found, false if it was not. +//when true, cheat code substitution value is stored in data. +bool Cheat::read(unsigned addr, uint8_t &data) const { + addr = mirror_address(addr); + for(unsigned i = 0; i < code.size(); i++) { + if(enabled(i) == false) continue; + + for(unsigned n = 0; n < code[i].count; n++) { + if(addr == mirror_address(code[i].addr[n])) { + data = code[i].data[n]; + return true; + } + } + } + + //code not found, or code is disabled + return false; +} + +//============== +//master control +//============== + +//global cheat system enable/disable: +//if disabled, *all* cheat codes are disabled; +//otherwise only individually disabled codes are. + +bool Cheat::enabled() const { + return cheat_system_enabled; +} + +void Cheat::enable() { + cheat_system_enabled = true; + cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists); +} + +void Cheat::disable() { + cheat_system_enabled = false; + cheat_enabled = false; +} + +//================================ +//cheat list manipulation routines +//================================ + +bool Cheat::add(bool enable, const char *code_, const char *desc_) { + cheat_t item; + if(decode(code_, item) == false) return false; + + unsigned i = code.size(); + code[i] = item; + code[i].enabled = enable; + code[i].desc = desc_; + code[i].code = code_; + encode_description(code[i].desc); + update(code[i]); + + update_cheat_status(); + return true; +} + +bool Cheat::edit(unsigned i, bool enable, const char *code_, const char *desc_) { + cheat_t item; + if(decode(code_, item) == false) return false; + + //disable current code and clear from code lookup table + code[i].enabled = false; + update(code[i]); + + code[i] = item; + code[i].enabled = enable; + code[i].desc = desc_; + code[i].code = code_; + encode_description(code[i].desc); + update(code[i]); + + update_cheat_status(); + return true; +} + +bool Cheat::remove(unsigned i) { + unsigned size = code.size(); + if(i >= size) return false; //also verifies size cannot be < 1 + + for(unsigned n = i; n < size - 1; n++) code[n] = code[n + 1]; + code.resize(size - 1); + + update_cheat_status(); + return true; +} + +bool Cheat::get(unsigned i, cheat_t &item) const { + if(i >= code.size()) return false; + + item = code[i]; + decode_description(item.desc); + return true; +} + +//============================== +//cheat status modifier routines +//============================== + +bool Cheat::enabled(unsigned i) const { + return (i < code.size() ? code[i].enabled : false); +} + +void Cheat::enable(unsigned i) { + if(i >= code.size()) return; + + code[i].enabled = true; + update(code[i]); + update_cheat_status(); +} + +void Cheat::disable(unsigned i) { + if(i >= code.size()) return; + + code[i].enabled = false; + update(code[i]); + update_cheat_status(); +} + +//=============================== +//cheat file load / save routines +// +//file format: +//"description", status, nnnn-nnnn[+nnnn-nnnn...]\r\n +//... +//=============================== + +bool Cheat::load(const char *fn) { + string data; + if(!data.readfile(fn)) return false; + data.replace("\r\n", "\n"); + data.qreplace(" ", ""); + + lstring line; + line.split("\n", data); + for(unsigned i = 0; i < line.size(); i++) { + lstring part; + part.qsplit(",", line[i]); + if(part.size() != 3) continue; + trim(part[0], "\""); + add(part[1] == "enabled", /* code = */ part[2], /* desc = */ part[0]); + } + + return true; +} + +bool Cheat::save(const char *fn) const { + file fp; + if(!fp.open(fn, file::mode_write)) return false; + for(unsigned i = 0; i < code.size(); i++) { + fp.print(string() + << "\"" << code[i].desc << "\", " + << (code[i].enabled ? "enabled, " : "disabled, ") + << code[i].code << "\r\n"); + } + fp.close(); + return true; +} + +void Cheat::clear() { + cheat_enabled_code_exists = false; + memset(mask, 0, 0x200000); + code.reset(); +} + +Cheat::Cheat() : cheat_system_enabled(true) { + clear(); +} + +//================== +//internal functions +//================== + +//string <> binary code translation routines +//decode() "7e123456" -> 0x7e123456 +//encode() 0x7e123456 -> "7e123456" + +bool Cheat::decode(const char *s, unsigned &addr, uint8_t &data, type_t &type) const { + string t = s; + strlower(t); + + #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 = 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 = GameGenie; + strtr(t, "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; + } +} + +bool Cheat::encode(string &s, unsigned addr, uint8_t data, type_t type) const { + char t[16]; + + if(type == ProActionReplay) { + sprintf(t, "%.6x%.2x", addr, data); + s = t; + return true; + } else if(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); + sprintf(t, "%.2x%.2x-%.4x", data, addr >> 16, addr & 0xffff); + strtr(t, "0123456789abcdef", "df4709156bc8a23e"); + s = t; + return true; + } else { + return false; + } +} + +//speed up S-CPU memory reads by disabling cheat code lookup when either: +//a) cheat system is disabled by user, or b) no enabled cheat codes exist +void Cheat::update_cheat_status() { + for(unsigned i = 0; i < code.size(); i++) { + if(code[i].enabled) { + cheat_enabled_code_exists = true; + cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists); + return; + } + } + cheat_enabled_code_exists = false; + cheat_enabled = false; +} + +//address lookup table manipulation and mirroring +//mirror_address() 0x000000 -> 0x7e0000 +//set() enable specified address, mirror accordingly +//clear() disable specified address, mirror accordingly +unsigned Cheat::mirror_address(unsigned addr) const { + if((addr & 0x40e000) != 0x0000) return addr; + //8k WRAM mirror + //$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff] + return (0x7e0000 + (addr & 0x1fff)); +} + +//updates mask[] table enabled bits; +//must be called after modifying item.enabled state. +void Cheat::update(const cheat_t &item) { + for(unsigned n = 0; n < item.count; n++) { + (item.enabled) ? set(item.addr[n]) : clear(item.addr[n]); + } +} + +void Cheat::set(unsigned addr) { + addr = mirror_address(addr); + + mask[addr >> 3] |= 1 << (addr & 7); + if((addr & 0xffe000) == 0x7e0000) { + //mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff] + unsigned mirror; + for(unsigned x = 0; x <= 0x3f; x++) { + mirror = ((0x00 + x) << 16) + (addr & 0x1fff); + mask[mirror >> 3] |= 1 << (mirror & 7); + mirror = ((0x80 + x) << 16) + (addr & 0x1fff); + mask[mirror >> 3] |= 1 << (mirror & 7); + } + } +} + +void Cheat::clear(unsigned addr) { + addr = mirror_address(addr); + + //if there is more than one cheat code using the same address, + //(eg with a different override value) then do not clear code + //lookup table entry. + uint8_t r; + if(read(addr, r) == true) return; + + mask[addr >> 3] &= ~(1 << (addr & 7)); + if((addr & 0xffe000) == 0x7e0000) { + //mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff] + unsigned mirror; + for(unsigned x = 0; x <= 0x3f; x++) { + mirror = ((0x00 + x) << 16) + (addr & 0x1fff); + mask[mirror >> 3] &= ~(1 << (mirror & 7)); + mirror = ((0x80 + x) << 16) + (addr & 0x1fff); + mask[mirror >> 3] &= ~(1 << (mirror & 7)); + } + } +} + +//these two functions are used to safely store description text inside .cfg file format. + +string& Cheat::encode_description(string &desc) const { + desc.replace("\"", "\\q"); + desc.replace("\n", "\\n"); + return desc; +} + +string& Cheat::decode_description(string &desc) const { + desc.replace("\\q", "\""); + desc.replace("\\n", "\n"); + return desc; +} diff --git a/bsnes/cheat/cheat.hpp b/bsnes/cheat/cheat.hpp new file mode 100755 index 0000000..58fa8b6 --- /dev/null +++ b/bsnes/cheat/cheat.hpp @@ -0,0 +1,69 @@ +class Cheat { +public: + enum type_t { + ProActionReplay, + GameGenie, + }; + + struct cheat_t { + bool enabled; + string code; + string desc; + + unsigned count; + array addr; + array data; + + cheat_t& operator=(const cheat_t&); + bool operator<(const cheat_t&); + }; + + bool decode(const char *s, cheat_t &item) const; + bool read(unsigned addr, uint8_t &data) const; + + bool enabled() const; + void enable(); + void disable(); + + inline unsigned count() const { return code.size(); } + inline bool active() const { return cheat_enabled; } + inline bool exists(unsigned addr) const { return mask[addr >> 3] & 1 << (addr & 7); } + + bool add(bool enable, const char *code, const char *desc); + bool edit(unsigned i, bool enable, const char *code, const char *desc); + bool remove(unsigned i); + bool get(unsigned i, cheat_t &item) const; + + bool enabled(unsigned i) const; + void enable(unsigned i); + void disable(unsigned i); + + bool load(const char *fn); + bool save(const char *fn) const; + void clear(); + + Cheat(); + +private: + bool cheat_enabled; //cheat_enabled == (cheat_enabled_code_exists && cheat_system_enabled); + bool cheat_enabled_code_exists; + bool cheat_system_enabled; + + uint8_t mask[0x200000]; + vector code; + + bool decode(const char *str, unsigned &addr, uint8_t &data, type_t &type) const; + bool encode(string &str, unsigned addr, uint8_t data, type_t type) const; + + void update_cheat_status(); + unsigned mirror_address(unsigned addr) const; + + void update(const cheat_t& item); + void set(unsigned addr); + void clear(unsigned addr); + + string& encode_description(string &desc) const; + string& decode_description(string &desc) const; +}; + +extern Cheat cheat; diff --git a/bsnes/chip/bsx/bsx.cpp b/bsnes/chip/bsx/bsx.cpp new file mode 100755 index 0000000..9d4de10 --- /dev/null +++ b/bsnes/chip/bsx/bsx.cpp @@ -0,0 +1,8 @@ +#include <../base.hpp> +#include <../cart/cart.hpp> +#define BSX_CPP + +#include "bsx.hpp" +#include "bsx_base.cpp" +#include "bsx_cart.cpp" +#include "bsx_flash.cpp" diff --git a/bsnes/chip/bsx/bsx.hpp b/bsnes/chip/bsx/bsx.hpp new file mode 100755 index 0000000..484cf42 --- /dev/null +++ b/bsnes/chip/bsx/bsx.hpp @@ -0,0 +1,77 @@ +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); + + MappedRAM sram; + MappedRAM psram; + + BSXCart(); + ~BSXCart(); + +private: + uint8 *sram_data; //256kbit SRAM + uint8 *psram_data; // 4mbit PSRAM + + 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/bsnes/chip/bsx/bsx_base.cpp b/bsnes/chip/bsx/bsx_base.cpp new file mode 100755 index 0000000..496150a --- /dev/null +++ b/bsnes/chip/bsx/bsx_base.cpp @@ -0,0 +1,137 @@ +#ifdef BSX_CPP + +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/bsnes/chip/bsx/bsx_cart.cpp b/bsnes/chip/bsx/bsx_cart.cpp new file mode 100755 index 0000000..68a0758 --- /dev/null +++ b/bsnes/chip/bsx/bsx_cart.cpp @@ -0,0 +1,101 @@ +#ifdef BSX_CPP + +void BSXCart::init() { +} + +void BSXCart::enable() { + for(uint16 i = 0x5000; i <= 0x5fff; i++) memory::mmio.map(i, *this); +} + +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&)psram; + + if((regs.r[0x02] & 0x80) == 0x00) { + //LoROM mapping + bus.map(Bus::MapLinear, 0x00, 0x7d, 0x8000, 0xffff, cart); + bus.map(Bus::MapLinear, 0x80, 0xff, 0x8000, 0xffff, cart); + } else { + //HiROM mapping + bus.map(Bus::MapShadow, 0x00, 0x3f, 0x8000, 0xffff, cart); + bus.map(Bus::MapLinear, 0x40, 0x7d, 0x0000, 0xffff, cart); + bus.map(Bus::MapShadow, 0x80, 0xbf, 0x8000, 0xffff, cart); + bus.map(Bus::MapLinear, 0xc0, 0xff, 0x0000, 0xffff, cart); + } + + if(regs.r[0x03] & 0x80) { + bus.map(Bus::MapLinear, 0x60, 0x6f, 0x0000, 0xffff, psram); + //bus.map(Bus::MapLinear, 0x70, 0x77, 0x0000, 0xffff, psram); + } + + if((regs.r[0x05] & 0x80) == 0x00) { + bus.map(Bus::MapLinear, 0x40, 0x4f, 0x0000, 0xffff, psram); + } + + if((regs.r[0x06] & 0x80) == 0x00) { + bus.map(Bus::MapLinear, 0x50, 0x5f, 0x0000, 0xffff, psram); + } + + if(regs.r[0x07] & 0x80) { + bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom); + } + + if(regs.r[0x08] & 0x80) { + bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom); + } + + bus.map(Bus::MapShadow, 0x20, 0x3f, 0x6000, 0x7fff, psram); + bus.map(Bus::MapLinear, 0x70, 0x77, 0x0000, 0xffff, psram); +} + +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 sram.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 sram.write(((addr >> 16) & 7) * 0x1000 + (addr & 0xfff), data); + } +} + +BSXCart::BSXCart() { + sram_data = new uint8_t[ 32 * 1024]; + psram_data = new uint8_t[512 * 1024]; + + sram.map (sram_data, 32 * 1024); + psram.map(psram_data, 512 * 1024); +} + +BSXCart::~BSXCart() { + delete[] sram_data; + delete[] psram_data; +} + +#endif diff --git a/bsnes/chip/bsx/bsx_flash.cpp b/bsnes/chip/bsx/bsx_flash.cpp new file mode 100755 index 0000000..deceb95 --- /dev/null +++ b/bsnes/chip/bsx/bsx_flash.cpp @@ -0,0 +1,113 @@ +#ifdef BSX_CPP + +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; +} + +unsigned BSXFlash::size() const { + return memory::bscram.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::bscram.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::bscram.write(addr, data); + } + } else { + if(regs.write_enable) { + return memory::bscram.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; + } + } +} + +#endif diff --git a/bsnes/chip/chip.hpp b/bsnes/chip/chip.hpp new file mode 100755 index 0000000..1769862 --- /dev/null +++ b/bsnes/chip/chip.hpp @@ -0,0 +1,11 @@ +#include "bsx/bsx.hpp" +#include "srtc/srtc.hpp" +#include "sdd1/sdd1.hpp" +#include "spc7110/spc7110.hpp" +#include "cx4/cx4.hpp" +#include "dsp1/dsp1.hpp" +#include "dsp2/dsp2.hpp" +#include "dsp3/dsp3.hpp" +#include "dsp4/dsp4.hpp" +#include "obc1/obc1.hpp" +#include "st010/st010.hpp" diff --git a/bsnes/chip/cx4/cx4.cpp b/bsnes/chip/cx4/cx4.cpp new file mode 100755 index 0000000..963d42c --- /dev/null +++ b/bsnes/chip/cx4/cx4.cpp @@ -0,0 +1,197 @@ +/* + C4 emulation + + Used in Rockman X2/X3 (Megaman X2/X3) + Portions (c) anomie, Overload, zsKnight, Nach, byuu +*/ + +#include <../base.hpp> +#define CX4_CPP + +#include "cx4.hpp" +#include "cx4data.cpp" +#include "cx4fn.cpp" +#include "cx4oam.cpp" +#include "cx4ops.cpp" + +void Cx4::init() {} +void Cx4::enable() {} + +uint32 Cx4::ldr(uint8 r) { +uint16 addr = 0x0080 + (r * 3); + return (reg[addr]) | (reg[addr + 1] << 8) | (reg[addr + 2] << 16); +} + +void Cx4::str(uint8 r, uint32 data) { +uint16 addr = 0x0080 + (r * 3); + reg[addr ] = (data); + 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, data); + write(addr + 1, data >> 8); +} + +void Cx4::writel(uint16 addr, uint32 data) { + write(addr, data); + 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/bsnes/chip/cx4/cx4.hpp b/bsnes/chip/cx4/cx4.hpp new file mode 100755 index 0000000..c4cf950 --- /dev/null +++ b/bsnes/chip/cx4/cx4.hpp @@ -0,0 +1,97 @@ +class Cx4 : public Memory { +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; + + double tanval; + double c4x,c4y,c4z, c4x2,c4y2,c4z2; + + 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); + +// + + void init(); + void enable(); + void power(); + void reset(); + + uint8 read (unsigned addr); + void write(unsigned addr, uint8 data); +}; + +extern Cx4 cx4; diff --git a/bsnes/chip/cx4/cx4data.cpp b/bsnes/chip/cx4/cx4data.cpp new file mode 100755 index 0000000..426dfee --- /dev/null +++ b/bsnes/chip/cx4/cx4data.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/bsnes/chip/cx4/cx4fn.cpp b/bsnes/chip/cx4/cx4fn.cpp new file mode 100755 index 0000000..ed60bc8 --- /dev/null +++ b/bsnes/chip/cx4/cx4fn.cpp @@ -0,0 +1,246 @@ +#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() { + c4x = (double)C4WFXVal; + c4y = (double)C4WFYVal; + c4z = (double)C4WFZVal - 0x95; + +//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() { + c4x = (double)C4WFXVal; + c4y = (double)C4WFYVal; + c4z = (double)C4WFZVal; + +//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/bsnes/chip/cx4/cx4oam.cpp b/bsnes/chip/cx4/cx4oam.cpp new file mode 100755 index 0000000..8214fbb --- /dev/null +++ b/bsnes/chip/cx4/cx4oam.cpp @@ -0,0 +1,223 @@ +#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/bsnes/chip/cx4/cx4ops.cpp b/bsnes/chip/cx4/cx4ops.cpp new file mode 100755 index 0000000..f5373cc --- /dev/null +++ b/bsnes/chip/cx4/cx4ops.cpp @@ -0,0 +1,226 @@ +#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); + 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 { + 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/bsnes/chip/dsp1/dsp1.cpp b/bsnes/chip/dsp1/dsp1.cpp new file mode 100755 index 0000000..323c88b --- /dev/null +++ b/bsnes/chip/dsp1/dsp1.cpp @@ -0,0 +1,59 @@ +#include <../base.hpp> +#include <../cart/cart.hpp> +#define DSP1_CPP + +#include "dsp1.hpp" +#include "dsp1emu.cpp" + +void DSP1::init() {} +void DSP1::enable() {} + +void DSP1::power() { + reset(); +} + +void DSP1::reset() { + dsp1.reset(); +} + +/***** + * addr_decode() + * determine whether address is accessing + * data register (DR) or status register (SR) + * -- 0 (false) = DR + * -- 1 (true ) = SR + * + * note: there is no need to bounds check addresses, + * as memory mapper will not allow DSP1 accesses outside + * of expected ranges + *****/ +bool DSP1::addr_decode(uint16 addr) { + switch(cartridge.dsp1_mapper()) { + case Cartridge::DSP1LoROM1MB: { + //$[20-3f]:[8000-bfff] = DR, $[20-3f]:[c000-ffff] = SR + return (addr >= 0xc000); + } + + case Cartridge::DSP1LoROM2MB: { + //$[60-6f]:[0000-3fff] = DR, $[60-6f]:[4000-7fff] = SR + return (addr >= 0x4000); + } + + case Cartridge::DSP1HiROM: { + //$[00-1f]:[6000-6fff] = DR, $[00-1f]:[7000-7fff] = SR + return (addr >= 0x7000); + } + } + + return 0; +} + +uint8 DSP1::read(unsigned addr) { + return (addr_decode(addr) == 0) ? dsp1.getDr() : dsp1.getSr(); +} + +void DSP1::write(unsigned addr, uint8 data) { + if(addr_decode(addr) == 0) { + dsp1.setDr(data); + } +} diff --git a/bsnes/chip/dsp1/dsp1.hpp b/bsnes/chip/dsp1/dsp1.hpp new file mode 100755 index 0000000..5bbdc74 --- /dev/null +++ b/bsnes/chip/dsp1/dsp1.hpp @@ -0,0 +1,18 @@ +#include "dsp1emu.hpp" + +class DSP1 : public Memory { +private: + Dsp1 dsp1; + bool addr_decode(uint16 addr); + +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); +}; + +extern DSP1 dsp1; diff --git a/bsnes/chip/dsp1/dsp1emu.cpp b/bsnes/chip/dsp1/dsp1emu.cpp new file mode 100755 index 0000000..0e6a29c --- /dev/null +++ b/bsnes/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/bsnes/chip/dsp1/dsp1emu.hpp b/bsnes/chip/dsp1/dsp1emu.hpp new file mode 100755 index 0000000..c89e57d --- /dev/null +++ b/bsnes/chip/dsp1/dsp1emu.hpp @@ -0,0 +1,127 @@ +// 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(); + + 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 + FsmMajorState 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/bsnes/chip/dsp2/dsp2.cpp b/bsnes/chip/dsp2/dsp2.cpp new file mode 100755 index 0000000..de40c0e --- /dev/null +++ b/bsnes/chip/dsp2/dsp2.cpp @@ -0,0 +1,136 @@ +#include <../base.hpp> +#define DSP2_CPP + +#include "dsp2.hpp" +#include "dsp2_op.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() {} diff --git a/bsnes/chip/dsp2/dsp2.hpp b/bsnes/chip/dsp2/dsp2.hpp new file mode 100755 index 0000000..e84c939 --- /dev/null +++ b/bsnes/chip/dsp2/dsp2.hpp @@ -0,0 +1,44 @@ +class DSP2 : public Memory { +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); + + DSP2(); + ~DSP2(); + +protected: + void op01(); + void op03(); + void op05(); + void op06(); + void op09(); + void op0d(); +}; + +extern DSP2 dsp2; diff --git a/bsnes/chip/dsp2/dsp2_op.cpp b/bsnes/chip/dsp2/dsp2_op.cpp new file mode 100755 index 0000000..dc6c7cc --- /dev/null +++ b/bsnes/chip/dsp2/dsp2_op.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/bsnes/chip/dsp3/dsp3.cpp b/bsnes/chip/dsp3/dsp3.cpp new file mode 100755 index 0000000..e6c7c7c --- /dev/null +++ b/bsnes/chip/dsp3/dsp3.cpp @@ -0,0 +1,35 @@ +#include <../base.hpp> +#define DSP3_CPP + +#include "dsp3.hpp" +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/bsnes/chip/dsp3/dsp3.hpp b/bsnes/chip/dsp3/dsp3.hpp new file mode 100755 index 0000000..3869633 --- /dev/null +++ b/bsnes/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/bsnes/chip/dsp3/dsp3emu.c b/bsnes/chip/dsp3/dsp3emu.c new file mode 100755 index 0000000..21f82a6 --- /dev/null +++ b/bsnes/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/bsnes/chip/dsp4/dsp4.cpp b/bsnes/chip/dsp4/dsp4.cpp new file mode 100755 index 0000000..aed7caf --- /dev/null +++ b/bsnes/chip/dsp4/dsp4.cpp @@ -0,0 +1,55 @@ +#include <../base.hpp> +#define DSP4_CPP + +#include "dsp4.hpp" +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::init() { +} + +void DSP4::enable() { +} + +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/bsnes/chip/dsp4/dsp4.hpp b/bsnes/chip/dsp4/dsp4.hpp new file mode 100755 index 0000000..17d6533 --- /dev/null +++ b/bsnes/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/bsnes/chip/dsp4/dsp4emu.c b/bsnes/chip/dsp4/dsp4emu.c new file mode 100755 index 0000000..1b6ea27 --- /dev/null +++ b/bsnes/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/bsnes/chip/dsp4/dsp4emu.h b/bsnes/chip/dsp4/dsp4emu.h new file mode 100755 index 0000000..7610387 --- /dev/null +++ b/bsnes/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/bsnes/chip/obc1/obc1.cpp b/bsnes/chip/obc1/obc1.cpp new file mode 100755 index 0000000..9606995 --- /dev/null +++ b/bsnes/chip/obc1/obc1.cpp @@ -0,0 +1,72 @@ +#include <../base.hpp> +#include <../cart/cart.hpp> +#include "obc1.hpp" + +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/bsnes/chip/obc1/obc1.hpp b/bsnes/chip/obc1/obc1.hpp new file mode 100755 index 0000000..2fb9754 --- /dev/null +++ b/bsnes/chip/obc1/obc1.hpp @@ -0,0 +1,25 @@ +class OBC1 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + 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/bsnes/chip/sdd1/sdd1.cpp b/bsnes/chip/sdd1/sdd1.cpp new file mode 100755 index 0000000..8530b4b --- /dev/null +++ b/bsnes/chip/sdd1/sdd1.cpp @@ -0,0 +1,158 @@ +#include <../base.hpp> +#include <../cart/cart.hpp> +#define SDD1_CPP + +#include "sdd1.hpp" +#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.get(i); + memory::mmio.map(i, *this); + } + + //hook S-DD1 MMIO registers + for(unsigned i = 0x4800; i <= 0x4807; i++) { + memory::mmio.map(i, *this); + } +} + +void SDD1::power() { + 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; + + bus.map(Bus::MapDirect, 0xc0, 0xff, 0x0000, 0xffff, *this); +} + +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) & 7; + case 0x4805: return (mmc[1] >> 20) & 7; + case 0x4806: return (mmc[2] >> 20) & 7; + case 0x4807: return (mmc[3] >> 20) & 7; + } + + return cpu.regs.mdr; +} + +void SDD1::mmio_write(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 & 7) << 20; break; + case 0x4805: mmc[1] = (data & 7) << 20; break; + case 0x4806: mmc[2] = (data & 7) << 20; break; + case 0x4807: mmc[3] = (data & 7) << 20; break; + } +} + +//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() { + buffer.data = new uint8[65536]; +} + +SDD1::~SDD1() { + delete[] buffer.data; +} diff --git a/bsnes/chip/sdd1/sdd1.hpp b/bsnes/chip/sdd1/sdd1.hpp new file mode 100755 index 0000000..9588bc0 --- /dev/null +++ b/bsnes/chip/sdd1/sdd1.hpp @@ -0,0 +1,40 @@ +#include "sdd1emu.hpp" + +class SDD1 : public MMIO, public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + SDD1(); + ~SDD1(); + +private: + MMIO *cpu_mmio[0x80]; //bus spying hooks to glean information for struct dma[] + + uint8 sdd1_enable; //channel bit-mask + uint8 xfer_enable; //channel bit-mask + unsigned mmc[4]; //memory map controller ROM indices + + struct { + unsigned addr; //$43x2-$43x4 -- DMA transfer address + uint16 size; //$43x5-$43x6 -- DMA transfer size + } dma[8]; + + SDD1emu sdd1emu; + struct { + uint8 *data; //pointer to decompressed S-DD1 data (65536 bytes) + uint16 offset; //read index into S-DD1 decompression buffer + unsigned size; //length of data buffer; reads decrement counter, set ready to false at 0 + bool ready; //true when data[] is valid; false to invoke sdd1emu.decompress() + } buffer; +}; + +extern SDD1 sdd1; diff --git a/bsnes/chip/sdd1/sdd1emu.cpp b/bsnes/chip/sdd1/sdd1emu.cpp new file mode 100755 index 0000000..2581ff9 --- /dev/null +++ b/bsnes/chip/sdd1/sdd1emu.cpp @@ -0,0 +1,451 @@ +#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. + +************************************************************************/ + +#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/bsnes/chip/sdd1/sdd1emu.hpp b/bsnes/chip/sdd1/sdd1emu.hpp new file mode 100755 index 0000000..a9ff01f --- /dev/null +++ b/bsnes/chip/sdd1/sdd1emu.hpp @@ -0,0 +1,162 @@ +/************************************************************************ + +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_t bool8; + +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; + +}; diff --git a/bsnes/chip/spc7110/decomp.cpp b/bsnes/chip/spc7110/decomp.cpp new file mode 100755 index 0000000..99d4e1b --- /dev/null +++ b/bsnes/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() - 0x100000; + while(decomp_offset >= size) decomp_offset -= size; + return memory::cartrom.read(0x100000 + 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/bsnes/chip/spc7110/decomp.hpp b/bsnes/chip/spc7110/decomp.hpp new file mode 100755 index 0000000..f7878a6 --- /dev/null +++ b/bsnes/chip/spc7110/decomp.hpp @@ -0,0 +1,45 @@ +class SPC7110Decomp { +public: + uint8 read(); + void init(unsigned mode, unsigned offset, unsigned index); + void reset(); + + 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/bsnes/chip/spc7110/spc7110.cpp b/bsnes/chip/spc7110/spc7110.cpp new file mode 100755 index 0000000..b5c59ae --- /dev/null +++ b/bsnes/chip/spc7110/spc7110.cpp @@ -0,0 +1,672 @@ +#include <../base.hpp> +#include <../cart/cart.hpp> +#define SPC7110_CPP + +#include "spc7110.hpp" +#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() { + uint16_t limit = (cartridge.has_spc7110rtc() ? 0x4842 : 0x483f); + for(uint16_t i = 0x4800; i <= limit; i++) memory::mmio.map(i, *this); +} + +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() - 0x100000; + while(addr >= size) addr -= size; + return addr + 0x100000; +} + +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 & 7) * 0x100000); + } break; + + case 0x4832: { + r4832 = data; + ex_offset = datarom_addr((data & 7) * 0x100000); + } break; + + case 0x4833: { + r4833 = data; + fx_offset = datarom_addr((data & 7) * 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; + } +} + +uint8 SPC7110::read(unsigned addr) { + //$[00-0f|80-8f]:[8000-ffff], $[c0-cf]:[0000-ffff] mapped directly to memory::cartrom + + if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) { + //$[00|30]:[6000-7fff] + return memory::cartram.read(addr & 0x1fff); + } + + if((addr & 0xff0000) == 0x500000) { + //$[50]:[0000-ffff] + return mmio_read(0x4800); + } + + if((addr & 0xf00000) == 0xd00000) { + //$[d0-df]:[0000-ffff] + return memory::cartrom.read(dx_offset + (addr & 0x0fffff)); + } + + if((addr & 0xf00000) == 0xe00000) { + //$[e0-ef]:[0000-ffff] + return memory::cartrom.read(ex_offset + (addr & 0x0fffff)); + } + + if((addr & 0xf00000) == 0xf00000) { + //$[f0-ff]:[0000-ffff] + return memory::cartrom.read(fx_offset + (addr & 0x0fffff)); + } + + return cpu.regs.mdr; +} + +void SPC7110::write(unsigned addr, uint8 data) { + if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) { + //$[00|30]:[6000-7fff] + if(r4830 & 0x80) memory::cartram.write(addr & 0x1fff, data); + return; + } +} + +SPC7110::SPC7110() { +} diff --git a/bsnes/chip/spc7110/spc7110.hpp b/bsnes/chip/spc7110/spc7110.hpp new file mode 100755 index 0000000..45baefc --- /dev/null +++ b/bsnes/chip/spc7110/spc7110.hpp @@ -0,0 +1,133 @@ +/***** + * SPC7110 emulator - version 0.03 (2008-08-10) + * Copyright (c) 2008, 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 Memory { +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); + + uint8 read (unsigned addr); + void write(unsigned addr, uint8 data); + + //spc7110decomp + void decomp_init(); + uint8 decomp_read(); + + 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 } rtc_state; + enum RTC_Mode { RTCM_Linear = 0x03, RTCM_Indexed = 0x0c } rtc_mode; + unsigned rtc_index; + + static const unsigned months[12]; +}; + +extern SPC7110 spc7110; diff --git a/bsnes/chip/srtc/srtc.cpp b/bsnes/chip/srtc/srtc.cpp new file mode 100755 index 0000000..f76ecf4 --- /dev/null +++ b/bsnes/chip/srtc/srtc.cpp @@ -0,0 +1,226 @@ +#include <../base.hpp> +#include <../cart/cart.hpp> +#include "srtc.hpp" + +const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +void SRTC::init() { +} + +void SRTC::enable() { + memory::mmio.map(0x2800, *this); + memory::mmio.map(0x2801, *this); +} + +void SRTC::power() { + reset(); +} + +void SRTC::reset() { + rtc_mode = RTCM_Read; + 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 != RTCM_Read) 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 = RTCM_Read; + rtc_index = -1; + return; + } + + if(data == 0x0e) { + rtc_mode = RTCM_Command; + return; + } + + if(data == 0x0f) return; //unknown behavior + + if(rtc_mode == RTCM_Write) { + 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 == RTCM_Command) { + if(data == 0) { + rtc_mode = RTCM_Write; + rtc_index = 0; + } else if(data == 4) { + rtc_mode = RTCM_Ready; + rtc_index = -1; + for(unsigned i = 0; i < 13; i++) memory::cartrtc.write(i, 0); + } else { + //unknown behavior + rtc_mode = RTCM_Ready; + } + } + } +} + +SRTC::SRTC() { +} diff --git a/bsnes/chip/srtc/srtc.hpp b/bsnes/chip/srtc/srtc.hpp new file mode 100755 index 0000000..e2c9994 --- /dev/null +++ b/bsnes/chip/srtc/srtc.hpp @@ -0,0 +1,22 @@ +class SRTC : public MMIO { +public: + void update_time(); + unsigned weekday(unsigned year, unsigned month, unsigned day); + + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read (unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + SRTC(); + +private: + static const unsigned months[12]; + enum RTC_Mode { RTCM_Ready, RTCM_Command, RTCM_Read, RTCM_Write } rtc_mode; + signed rtc_index; +}; + +extern SRTC srtc; diff --git a/bsnes/chip/st010/st010.cpp b/bsnes/chip/st010/st010.cpp new file mode 100755 index 0000000..c0c8f90 --- /dev/null +++ b/bsnes/chip/st010/st010.cpp @@ -0,0 +1,87 @@ +#include <../base.hpp> +#define ST010_CPP + +#include "st010.hpp" +#include "st010_data.hpp" +#include "st010_op.cpp" + +int16 ST010::sin(int16 theta) { + return sin_table[(theta >> 8) & 0xff]; +} + +int16 ST010::cos(int16 theta) { + return sin_table[((theta + 0x4000) >> 8) & 0xff]; +} + +uint8 ST010::readb(uint16 addr) { + return ram[addr & 0xfff]; +} + +uint16 ST010::readw(uint16 addr) { + return (readb(addr + 0) << 0) | + (readb(addr + 1) << 8); +} + +uint32 ST010::readd(uint16 addr) { + return (readb(addr + 0) << 0) | + (readb(addr + 1) << 8) | + (readb(addr + 2) << 16) | + (readb(addr + 3) << 24); +} + +void ST010::writeb(uint16 addr, uint8 data) { + ram[addr & 0xfff] = data; +} + +void ST010::writew(uint16 addr, uint16 data) { + writeb(addr + 0, data); + writeb(addr + 1, data >> 8); +} + +void ST010::writed(uint16 addr, uint32 data) { + writeb(addr + 0, data); + writeb(addr + 1, data >> 8); + writeb(addr + 2, data >> 16); + writeb(addr + 3, data >> 24); +} + +// + +void ST010::init() { +} + +void ST010::enable() { +} + +void ST010::power() { + reset(); +} + +void ST010::reset() { + memset(ram, 0x00, sizeof ram); +} + +// + +uint8 ST010::read(unsigned addr) { + return readb(addr); +} + +void ST010::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/bsnes/chip/st010/st010.hpp b/bsnes/chip/st010/st010.hpp new file mode 100755 index 0000000..d4445ce --- /dev/null +++ b/bsnes/chip/st010/st010.hpp @@ -0,0 +1,42 @@ +class ST010 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read (unsigned addr); + void write(unsigned addr, uint8 data); + +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 ST010 st010; diff --git a/bsnes/chip/st010/st010_data.hpp b/bsnes/chip/st010/st010_data.hpp new file mode 100755 index 0000000..df7a00c --- /dev/null +++ b/bsnes/chip/st010/st010_data.hpp @@ -0,0 +1,126 @@ +const int16 ST010::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 ST010::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 ST010::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 } +}; diff --git a/bsnes/chip/st010/st010_op.cpp b/bsnes/chip/st010/st010_op.cpp new file mode 100755 index 0000000..77b4a9c --- /dev/null +++ b/bsnes/chip/st010/st010_op.cpp @@ -0,0 +1,261 @@ +#ifdef ST010_CPP + +//ST-010 emulation code - Copyright (C) 2003 The Dumper, Matthew Kendora, Overload, Feather +//bsnes port - Copyright (C) 2007 byuu + +void ST010::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 ST010::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 ST010::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 ST010::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 ST010::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 ST010::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 of 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 ST010::op_06() { +int16 multiplicand = readw(0x0000); +int16 multiplier = readw(0x0002); +int32 product; + + product = multiplicand * multiplier << 1; + + writed(0x0010, product); +} + +void ST010::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 ST010::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/bsnes/clean.bat b/bsnes/clean.bat new file mode 100755 index 0000000..1d563cc --- /dev/null +++ b/bsnes/clean.bat @@ -0,0 +1 @@ +@mingw32-make platform=win compiler=mingw32-gcc clean diff --git a/bsnes/clean.sh b/bsnes/clean.sh new file mode 100755 index 0000000..fddeab6 --- /dev/null +++ b/bsnes/clean.sh @@ -0,0 +1 @@ +make platform=x compiler=gcc clean diff --git a/bsnes/cpu/cpu.cpp b/bsnes/cpu/cpu.cpp new file mode 100755 index 0000000..0d10758 --- /dev/null +++ b/bsnes/cpu/cpu.cpp @@ -0,0 +1,17 @@ +#include <../base.hpp> +#define CPU_CPP + +#include "dcpu.cpp" + +void CPU::power() { + cpu_version = snes.config.cpu.version; +} + +void CPU::reset() { +} + +CPU::CPU() { +} + +CPU::~CPU() { +} diff --git a/bsnes/cpu/cpu.hpp b/bsnes/cpu/cpu.hpp new file mode 100755 index 0000000..e13a430 --- /dev/null +++ b/bsnes/cpu/cpu.hpp @@ -0,0 +1,74 @@ +class CPU : public MMIO { +public: + virtual void enter() = 0; + + //CPU version number + //* 1 and 2 are known + //* reported by $4210 + //* affects timing (DRAM refresh, HDMA init, etc) + uint8 cpu_version; + + virtual uint8 pio() = 0; + virtual bool joylatch() = 0; + virtual uint8 port_read(uint8 port) = 0; + virtual void port_write(uint8 port, uint8 value) = 0; + + #include "cpuregs.hpp" + regs_t regs; + + virtual void scanline() = 0; + virtual void power(); + virtual void reset(); + + /***** + * in opcode-based CPU emulators, the main emulation routine + * will only be able to call the disassemble_opcode() function + * on clean opcode edges. but with cycle-based CPU emulators, + * the CPU may be in the middle of executing an opcode when the + * emulator (e.g. debugger) wants to disassemble an opcode. this + * would mean that important registers may not reflect what they + * did at the start of the opcode (especially regs.pc), so in + * cycle-based emulators, this function should be overridden to + * reflect whether or not an opcode has only been partially + * executed. if not, the debugger should abort attempts to skip, + * disable, or disassemble the current opcode. + *****/ + virtual bool in_opcode() { return false; } + + /***** + * opcode disassembler + *****/ + 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); + uint8 dreadb(uint32 addr); + uint16 dreadw(uint32 addr); + uint32 dreadl(uint32 addr); + uint32 decode(uint8 offset_type, uint32 addr); + uint8 opcode_length(); + + CPU(); + virtual ~CPU(); +}; diff --git a/bsnes/cpu/cpuregs.hpp b/bsnes/cpu/cpuregs.hpp new file mode 100755 index 0000000..88360aa --- /dev/null +++ b/bsnes/cpu/cpuregs.hpp @@ -0,0 +1,74 @@ +struct flag_t { + bool n, v, m, x, d, i, z, c; + + inline operator unsigned() const { + return (n << 7) + (v << 6) + (m << 5) + (x << 4) + + (d << 3) + (i << 2) + (z << 1) + (c << 0); + } + + inline unsigned operator=(uint8_t data) { + n = data & 0x80; v = data & 0x40; m = data & 0x20; x = data & 0x10; + d = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01; + return data; + } + + inline unsigned operator|=(unsigned data) { return operator=(operator unsigned() | data); } + inline unsigned operator^=(unsigned data) { return operator=(operator unsigned() ^ data); } + inline unsigned operator&=(unsigned data) { return operator=(operator unsigned() & data); } + + flag_t() : n(0), v(0), m(0), x(0), d(0), i(0), z(0), c(0) {} +}; + +struct reg16_t { + union { + uint16 w; + struct { uint8 order_lsb2(l, h); }; + }; + + inline operator unsigned() const { return w; } + inline unsigned operator = (unsigned i) { return w = i; } + inline unsigned operator |= (unsigned i) { return w |= i; } + inline unsigned operator ^= (unsigned i) { return w ^= i; } + inline unsigned operator &= (unsigned i) { return w &= i; } + inline unsigned operator <<= (unsigned i) { return w <<= i; } + inline unsigned operator >>= (unsigned i) { return w >>= i; } + inline unsigned operator += (unsigned i) { return w += i; } + inline unsigned operator -= (unsigned i) { return w -= i; } + inline unsigned operator *= (unsigned i) { return w *= i; } + inline unsigned operator /= (unsigned i) { return w /= i; } + inline unsigned operator %= (unsigned i) { return w %= i; } + + reg16_t() : w(0) {} +}; + +struct reg24_t { + union { + uint32 d; + struct { uint16 order_lsb2(w, wh); }; + struct { uint8 order_lsb4(l, h, b, bh); }; + }; + + inline operator unsigned() const { return d; } + inline unsigned operator = (unsigned i) { return d = uclip<24>(i); } + inline unsigned operator |= (unsigned i) { return d = uclip<24>(d | i); } + inline unsigned operator ^= (unsigned i) { return d = uclip<24>(d ^ i); } + inline unsigned operator &= (unsigned i) { return d = uclip<24>(d & i); } + inline unsigned operator <<= (unsigned i) { return d = uclip<24>(d << i); } + inline unsigned operator >>= (unsigned i) { return d = uclip<24>(d >> i); } + inline unsigned operator += (unsigned i) { return d = uclip<24>(d + i); } + inline unsigned operator -= (unsigned i) { return d = uclip<24>(d - i); } + inline unsigned operator *= (unsigned i) { return d = uclip<24>(d * i); } + inline unsigned operator /= (unsigned i) { return d = uclip<24>(d / i); } + inline unsigned operator %= (unsigned i) { return d = uclip<24>(d % i); } + + reg24_t() : d(0) {} +}; + +struct regs_t { + reg24_t pc; + reg16_t a, x, y, s, d; + flag_t p; + uint8_t db, mdr; + bool e; + regs_t() : db(0), mdr(0), e(false) {} +}; diff --git a/bsnes/cpu/dcpu.cpp b/bsnes/cpu/dcpu.cpp new file mode 100755 index 0000000..bf8942a --- /dev/null +++ b/bsnes/cpu/dcpu.cpp @@ -0,0 +1,483 @@ +#ifdef CPU_CPP + +uint8 CPU::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 CPU::dreadw(uint32 addr) { + uint16 r; + r = dreadb((addr + 0) & 0xffffff) << 0; + r |= dreadb((addr + 1) & 0xffffff) << 8; + return r; +} + +uint32 CPU::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 CPU::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 CPU::disassemble_opcode(char *output) { + static reg24_t pc; + char t[256]; + char *s = output; + + if(in_opcode() == true) { + strcpy(s, "?????? "); + return; + } + + pc.d = regs.pc.d; + 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", ppu.vcounter(), ppu.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 CPU::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(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 //ifdef CPU_CPP diff --git a/bsnes/cpu/scpu/core/cc.sh b/bsnes/cpu/scpu/core/cc.sh new file mode 100755 index 0000000..97971d7 --- /dev/null +++ b/bsnes/cpu/scpu/core/cc.sh @@ -0,0 +1,4 @@ +g++ -c scpugen.cpp -I../../../lib +g++ -c ../../../lib/nall/string.cpp -I../../../lib +g++ -o scpugen scpugen.o string.o +rm *.o diff --git a/bsnes/cpu/scpu/core/clean.sh b/bsnes/cpu/scpu/core/clean.sh new file mode 100755 index 0000000..33da881 --- /dev/null +++ b/bsnes/cpu/scpu/core/clean.sh @@ -0,0 +1 @@ +rm scpugen diff --git a/bsnes/cpu/scpu/core/core.cpp b/bsnes/cpu/scpu/core/core.cpp new file mode 100755 index 0000000..b746d6b --- /dev/null +++ b/bsnes/cpu/scpu/core/core.cpp @@ -0,0 +1,90 @@ +#ifdef SCPU_CPP + +#include "opfn.cpp" + +void sCPU::enter() { + initialize: + //initial latch values for $213c/$213d + //[x]0035 : [y]0000 (53.0 -> 212) [lda $2137] + //[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137] + add_clocks(186); + + loop: + if(status.interrupt_pending) { + status.interrupt_pending = false; + if(status.nmi_pending) { + status.nmi_pending = false; + status.interrupt_vector = (regs.e == false ? 0xffea : 0xfffa); + } else if(status.irq_pending) { + status.irq_pending = false; + status.interrupt_vector = (regs.e == false ? 0xffee : 0xfffe); + } + op_irq(); + } + + tracer.trace_cpuop(); //traces CPU opcode (only if tracer is enabled) + + status.in_opcode = true; + switch(op_readpc()) { + #include "op_read.cpp" + #include "op_write.cpp" + #include "op_rmw.cpp" + #include "op_pc.cpp" + #include "op_misc.cpp" + } + status.in_opcode = false; + + goto loop; +} + +void sCPU::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; +} + +//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 sCPU::op_io_irq() { + if(status.interrupt_pending) { + //IRQ pending, modify I/O cycle to bus read cycle, do not increment PC + op_read(regs.pc.d); + } else { + op_io(); + } +} + +alwaysinline void sCPU::op_io_cond2() { + if(regs.d.l != 0x00) { + op_io(); + } +} + +alwaysinline void sCPU::op_io_cond4(uint16 x, uint16 y) { + if(!regs.p.x || (x & 0xff00) != (y & 0xff00)) { + op_io(); + } +} + +alwaysinline void sCPU::op_io_cond6(uint16 addr) { + if(regs.e && (regs.pc.w & 0xff00) != (addr & 0xff00)) { + op_io(); + } +} + +#endif diff --git a/bsnes/cpu/scpu/core/core.hpp b/bsnes/cpu/scpu/core/core.hpp new file mode 100755 index 0000000..945c917 --- /dev/null +++ b/bsnes/cpu/scpu/core/core.hpp @@ -0,0 +1,54 @@ + reg24_t aa, rd; + uint8_t dp, sp; + + void op_irq(); + + inline bool in_opcode() { return status.in_opcode; } + + //op_read + 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(); + //op_rmw + 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(); + + void op_io_irq(); + void op_io_cond2(); + void op_io_cond4(uint16 x, uint16 y); + void op_io_cond6(uint16 addr); diff --git a/bsnes/cpu/scpu/core/op_misc.b b/bsnes/cpu/scpu/core/op_misc.b new file mode 100755 index 0000000..491b14a --- /dev/null +++ b/bsnes/cpu/scpu/core/op_misc.b @@ -0,0 +1,298 @@ +nop(0xea) { +1:last_cycle(); + op_io_irq(); +} + +wdm(0x42) { +1:last_cycle(); + op_readpc(); +} + +xba(0xeb) { +1:op_io(); +2:last_cycle(); + 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); +} + +mvn(0x54, ++), +mvp(0x44, --) { +1:dp = op_readpc(); +2:sp = op_readpc(); +3:regs.db = dp; + rd.l = op_readlong((sp << 16) | regs.x.w); +4:op_writelong((dp << 16) | regs.y.w, rd.l); +5:op_io(); + if(regs.p.x) { + regs.x.l $1; + regs.y.l $1; + } else { + regs.x.w $1; + regs.y.w $1; + } +6:last_cycle(); + op_io(); + if(regs.a.w--) regs.pc.w -= 3; +} + +brk(0x00, 0xfffe, 0xffff, 0xffe6, 0xffe7), +cop(0x02, 0xfff4, 0xfff5, 0xffe4, 0xffe5) { +1:op_readpc(); +2:if(!regs.e) op_writestack(regs.pc.b); +3:op_writestack(regs.pc.h); +4:op_writestack(regs.pc.l); +5:op_writestack(regs.p); +6:rd.l = op_readlong(regs.e ? $1 : $3); + regs.pc.b = 0x00; + regs.p.i = 1; + regs.p.d = 0; +7:last_cycle(); + rd.h = op_readlong(regs.e ? $2 : $4); + regs.pc.w = rd.w; +} + +stp(0xdb) { +1:op_io(); +2:last_cycle(); + while(true) op_io(); +} + +wai(0xcb) { +//last_cycle() will clear status.wai_lock once an NMI / IRQ edge is reached +1:status.wai_lock = true; + while(status.wai_lock) { + last_cycle(); + op_io(); + } +2:op_io(); +} + +xce(0xfb) { +1:last_cycle(); + 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; + } +} + +clc(0x18, regs.p.c = 0), +cld(0xd8, regs.p.d = 0), +cli(0x58, regs.p.i = 0), +clv(0xb8, regs.p.v = 0), +sec(0x38, regs.p.c = 1), +sed(0xf8, regs.p.d = 1), +sei(0x78, regs.p.i = 1) { +1:last_cycle(); + op_io_irq(); + $1; +} + +rep(0xc2, &=~), +sep(0xe2, |=) { +1:rd.l = op_readpc(); +2:last_cycle(); + op_io(); + regs.p $1 rd.l; + if(regs.e) regs.p |= 0x30; + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } +} + +tax(0xaa, regs.p.x, x, a), +tay(0xa8, regs.p.x, y, a), +txa(0x8a, regs.p.m, a, x), +txy(0x9b, regs.p.x, y, x), +tya(0x98, regs.p.m, a, y), +tyx(0xbb, regs.p.x, x, y) { +1:last_cycle(); + op_io_irq(); + if($1) { + regs.$2.l = regs.$3.l; + regs.p.n = !!(regs.$2.l & 0x80); + regs.p.z = (regs.$2.l == 0); + } else { + regs.$2.w = regs.$3.w; + regs.p.n = !!(regs.$2.w & 0x8000); + regs.p.z = (regs.$2.w == 0); + } +} + +tcd(0x5b) { +1:last_cycle(); + op_io_irq(); + regs.d.w = regs.a.w; + regs.p.n = !!(regs.d.w & 0x8000); + regs.p.z = (regs.d.w == 0); +} + +tcs(0x1b) { +1:last_cycle(); + op_io_irq(); + regs.s.w = regs.a.w; + if(regs.e) regs.s.h = 0x01; +} + +tdc(0x7b) { +1:last_cycle(); + op_io_irq(); + regs.a.w = regs.d.w; + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); +} + +tsc(0x3b) { +1:last_cycle(); + op_io_irq(); + regs.a.w = regs.s.w; + if(regs.e) { + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + } else { + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); + } +} + +tsx(0xba) { +1:last_cycle(); + op_io_irq(); + if(regs.p.x) { + regs.x.l = regs.s.l; + regs.p.n = !!(regs.x.l & 0x80); + regs.p.z = (regs.x.l == 0); + } else { + regs.x.w = regs.s.w; + regs.p.n = !!(regs.x.w & 0x8000); + regs.p.z = (regs.x.w == 0); + } +} + +txs(0x9a) { +1:last_cycle(); + op_io_irq(); + if(regs.e) { + regs.s.l = regs.x.l; + } else { + regs.s.w = regs.x.w; + } +} + +pha(0x48, regs.p.m, a), +phx(0xda, regs.p.x, x), +phy(0x5a, regs.p.x, y) { +1:op_io(); +2:if(!$1)op_writestack(regs.$2.h); +3:last_cycle(); + op_writestack(regs.$2.l); +} + +phd(0x0b) { +1:op_io(); +2:op_writestackn(regs.d.h); +3:last_cycle(); + op_writestackn(regs.d.l); + if(regs.e) regs.s.h = 0x01; +} + +phb(0x8b, regs.db), +phk(0x4b, regs.pc.b), +php(0x08, regs.p) { +1:op_io(); +2:last_cycle(); + op_writestack($1); +} + +pla(0x68, regs.p.m, a), +plx(0xfa, regs.p.x, x), +ply(0x7a, regs.p.x, y) { +1:op_io(); +2:op_io(); +3:if($1)last_cycle(); + regs.$2.l = op_readstack(); + if($1) { + regs.p.n = !!(regs.$2.l & 0x80); + regs.p.z = (regs.$2.l == 0); + end; + } +4:last_cycle(); + regs.$2.h = op_readstack(); + regs.p.n = !!(regs.$2.w & 0x8000); + regs.p.z = (regs.$2.w == 0); +} + +pld(0x2b) { +1:op_io(); +2:op_io(); +3:regs.d.l = op_readstackn(); +4:last_cycle(); + regs.d.h = op_readstackn(); + regs.p.n = !!(regs.d.w & 0x8000); + regs.p.z = (regs.d.w == 0); + if(regs.e) regs.s.h = 0x01; +} + +plb(0xab) { +1:op_io(); +2:op_io(); +3:last_cycle(); + regs.db = op_readstack(); + regs.p.n = !!(regs.db & 0x80); + regs.p.z = (regs.db == 0); +} + +plp(0x28) { +1:op_io(); +2:op_io(); +3:last_cycle(); + regs.p = op_readstack(); + if(regs.e) regs.p |= 0x30; + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } +} + +pea(0xf4) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:op_writestackn(aa.h); +4:last_cycle(); + op_writestackn(aa.l); + if(regs.e) regs.s.h = 0x01; +} + +pei(0xd4) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:aa.l = op_readdp(dp); +4:aa.h = op_readdp(dp + 1); +5:op_writestackn(aa.h); +6:last_cycle(); + op_writestackn(aa.l); + if(regs.e) regs.s.h = 0x01; +} + +per(0x62) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:op_io(); + rd.w = regs.pc.d + (int16)aa.w; +4:op_writestackn(rd.h); +5:last_cycle(); + op_writestackn(rd.l); + if(regs.e) regs.s.h = 0x01; +} diff --git a/bsnes/cpu/scpu/core/op_misc.cpp b/bsnes/cpu/scpu/core/op_misc.cpp new file mode 100755 index 0000000..ef1fc98 --- /dev/null +++ b/bsnes/cpu/scpu/core/op_misc.cpp @@ -0,0 +1,539 @@ +#ifdef SCPU_CPP + +//nop +case 0xea: { + last_cycle(); + op_io_irq(); +} break; + +//wdm +case 0x42: { + last_cycle(); + op_readpc(); +} break; + +//xba +case 0xeb: { + op_io(); + last_cycle(); + 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); +} break; + +//mvn +case 0x54: { + 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(); + if(regs.p.x) { + regs.x.l ++; + regs.y.l ++; + } else { + regs.x.w ++; + regs.y.w ++; + } + last_cycle(); + op_io(); + if(regs.a.w--) regs.pc.w -= 3; +} break; + +//mvp +case 0x44: { + 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(); + if(regs.p.x) { + regs.x.l --; + regs.y.l --; + } else { + regs.x.w --; + regs.y.w --; + } + last_cycle(); + op_io(); + if(regs.a.w--) regs.pc.w -= 3; +} break; + +//brk +case 0x00: { + op_readpc(); + if(!regs.e) op_writestack(regs.pc.b); + op_writestack(regs.pc.h); + op_writestack(regs.pc.l); + op_writestack(regs.p); + rd.l = op_readlong(regs.e ? 0xfffe : 0xffe6); + regs.pc.b = 0x00; + regs.p.i = 1; + regs.p.d = 0; + last_cycle(); + rd.h = op_readlong(regs.e ? 0xffff : 0xffe7); + regs.pc.w = rd.w; +} break; + +//cop +case 0x02: { + op_readpc(); + if(!regs.e) op_writestack(regs.pc.b); + op_writestack(regs.pc.h); + op_writestack(regs.pc.l); + op_writestack(regs.p); + rd.l = op_readlong(regs.e ? 0xfff4 : 0xffe4); + regs.pc.b = 0x00; + regs.p.i = 1; + regs.p.d = 0; + last_cycle(); + rd.h = op_readlong(regs.e ? 0xfff5 : 0xffe5); + regs.pc.w = rd.w; +} break; + +//stp +case 0xdb: { + op_io(); + last_cycle(); + while(true) op_io(); +} break; + +//wai +case 0xcb: { + //last_cycle() will clear status.wai_lock once an NMI / IRQ edge is reached + status.wai_lock = true; + while(status.wai_lock) { + last_cycle(); + op_io(); + } + op_io(); +} break; + +//xce +case 0xfb: { + last_cycle(); + 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; + } +} break; + +//clc +case 0x18: { + last_cycle(); + op_io_irq(); + regs.p.c = 0; +} break; + +//cld +case 0xd8: { + last_cycle(); + op_io_irq(); + regs.p.d = 0; +} break; + +//cli +case 0x58: { + last_cycle(); + op_io_irq(); + regs.p.i = 0; +} break; + +//clv +case 0xb8: { + last_cycle(); + op_io_irq(); + regs.p.v = 0; +} break; + +//sec +case 0x38: { + last_cycle(); + op_io_irq(); + regs.p.c = 1; +} break; + +//sed +case 0xf8: { + last_cycle(); + op_io_irq(); + regs.p.d = 1; +} break; + +//sei +case 0x78: { + last_cycle(); + op_io_irq(); + regs.p.i = 1; +} break; + +//rep +case 0xc2: { + rd.l = op_readpc(); + last_cycle(); + op_io(); + regs.p &=~ rd.l; + if(regs.e) regs.p |= 0x30; + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } +} break; + +//sep +case 0xe2: { + rd.l = op_readpc(); + last_cycle(); + op_io(); + regs.p |= rd.l; + if(regs.e) regs.p |= 0x30; + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } +} break; + +//tax +case 0xaa: { + last_cycle(); + op_io_irq(); + if(regs.p.x) { + regs.x.l = regs.a.l; + regs.p.n = !!(regs.x.l & 0x80); + regs.p.z = (regs.x.l == 0); + } else { + regs.x.w = regs.a.w; + regs.p.n = !!(regs.x.w & 0x8000); + regs.p.z = (regs.x.w == 0); + } +} break; + +//tay +case 0xa8: { + last_cycle(); + op_io_irq(); + if(regs.p.x) { + regs.y.l = regs.a.l; + regs.p.n = !!(regs.y.l & 0x80); + regs.p.z = (regs.y.l == 0); + } else { + regs.y.w = regs.a.w; + regs.p.n = !!(regs.y.w & 0x8000); + regs.p.z = (regs.y.w == 0); + } +} break; + +//txa +case 0x8a: { + last_cycle(); + op_io_irq(); + if(regs.p.m) { + regs.a.l = regs.x.l; + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + } else { + regs.a.w = regs.x.w; + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); + } +} break; + +//txy +case 0x9b: { + last_cycle(); + op_io_irq(); + if(regs.p.x) { + regs.y.l = regs.x.l; + regs.p.n = !!(regs.y.l & 0x80); + regs.p.z = (regs.y.l == 0); + } else { + regs.y.w = regs.x.w; + regs.p.n = !!(regs.y.w & 0x8000); + regs.p.z = (regs.y.w == 0); + } +} break; + +//tya +case 0x98: { + last_cycle(); + op_io_irq(); + if(regs.p.m) { + regs.a.l = regs.y.l; + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + } else { + regs.a.w = regs.y.w; + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); + } +} break; + +//tyx +case 0xbb: { + last_cycle(); + op_io_irq(); + if(regs.p.x) { + regs.x.l = regs.y.l; + regs.p.n = !!(regs.x.l & 0x80); + regs.p.z = (regs.x.l == 0); + } else { + regs.x.w = regs.y.w; + regs.p.n = !!(regs.x.w & 0x8000); + regs.p.z = (regs.x.w == 0); + } +} break; + +//tcd +case 0x5b: { + last_cycle(); + op_io_irq(); + regs.d.w = regs.a.w; + regs.p.n = !!(regs.d.w & 0x8000); + regs.p.z = (regs.d.w == 0); +} break; + +//tcs +case 0x1b: { + last_cycle(); + op_io_irq(); + regs.s.w = regs.a.w; + if(regs.e) regs.s.h = 0x01; +} break; + +//tdc +case 0x7b: { + last_cycle(); + op_io_irq(); + regs.a.w = regs.d.w; + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); +} break; + +//tsc +case 0x3b: { + last_cycle(); + op_io_irq(); + regs.a.w = regs.s.w; + if(regs.e) { + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + } else { + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); + } +} break; + +//tsx +case 0xba: { + last_cycle(); + op_io_irq(); + if(regs.p.x) { + regs.x.l = regs.s.l; + regs.p.n = !!(regs.x.l & 0x80); + regs.p.z = (regs.x.l == 0); + } else { + regs.x.w = regs.s.w; + regs.p.n = !!(regs.x.w & 0x8000); + regs.p.z = (regs.x.w == 0); + } +} break; + +//txs +case 0x9a: { + last_cycle(); + op_io_irq(); + if(regs.e) { + regs.s.l = regs.x.l; + } else { + regs.s.w = regs.x.w; + } +} break; + +//pha +case 0x48: { + op_io(); + if(!regs.p.m)op_writestack(regs.a.h); + last_cycle(); + op_writestack(regs.a.l); +} break; + +//phx +case 0xda: { + op_io(); + if(!regs.p.x)op_writestack(regs.x.h); + last_cycle(); + op_writestack(regs.x.l); +} break; + +//phy +case 0x5a: { + op_io(); + if(!regs.p.x)op_writestack(regs.y.h); + last_cycle(); + op_writestack(regs.y.l); +} break; + +//phd +case 0x0b: { + op_io(); + op_writestackn(regs.d.h); + last_cycle(); + op_writestackn(regs.d.l); + if(regs.e) regs.s.h = 0x01; +} break; + +//phb +case 0x8b: { + op_io(); + last_cycle(); + op_writestack(regs.db); +} break; + +//phk +case 0x4b: { + op_io(); + last_cycle(); + op_writestack(regs.pc.b); +} break; + +//php +case 0x08: { + op_io(); + last_cycle(); + op_writestack(regs.p); +} break; + +//pla +case 0x68: { + op_io(); + op_io(); + if(regs.p.m)last_cycle(); + regs.a.l = op_readstack(); + if(regs.p.m) { + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + break; + } + last_cycle(); + regs.a.h = op_readstack(); + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); +} break; + +//plx +case 0xfa: { + op_io(); + op_io(); + if(regs.p.x)last_cycle(); + regs.x.l = op_readstack(); + if(regs.p.x) { + regs.p.n = !!(regs.x.l & 0x80); + regs.p.z = (regs.x.l == 0); + break; + } + last_cycle(); + regs.x.h = op_readstack(); + regs.p.n = !!(regs.x.w & 0x8000); + regs.p.z = (regs.x.w == 0); +} break; + +//ply +case 0x7a: { + op_io(); + op_io(); + if(regs.p.x)last_cycle(); + regs.y.l = op_readstack(); + if(regs.p.x) { + regs.p.n = !!(regs.y.l & 0x80); + regs.p.z = (regs.y.l == 0); + break; + } + last_cycle(); + regs.y.h = op_readstack(); + regs.p.n = !!(regs.y.w & 0x8000); + regs.p.z = (regs.y.w == 0); +} break; + +//pld +case 0x2b: { + op_io(); + op_io(); + regs.d.l = op_readstackn(); + last_cycle(); + regs.d.h = op_readstackn(); + regs.p.n = !!(regs.d.w & 0x8000); + regs.p.z = (regs.d.w == 0); + if(regs.e) regs.s.h = 0x01; +} break; + +//plb +case 0xab: { + op_io(); + op_io(); + last_cycle(); + regs.db = op_readstack(); + regs.p.n = !!(regs.db & 0x80); + regs.p.z = (regs.db == 0); +} break; + +//plp +case 0x28: { + op_io(); + op_io(); + last_cycle(); + regs.p = op_readstack(); + if(regs.e) regs.p |= 0x30; + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } +} break; + +//pea +case 0xf4: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_writestackn(aa.h); + last_cycle(); + op_writestackn(aa.l); + if(regs.e) regs.s.h = 0x01; +} break; + +//pei +case 0xd4: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + op_writestackn(aa.h); + last_cycle(); + op_writestackn(aa.l); + if(regs.e) regs.s.h = 0x01; +} break; + +//per +case 0x62: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.w = regs.pc.d + (int16)aa.w; + op_writestackn(rd.h); + last_cycle(); + op_writestackn(rd.l); + if(regs.e) regs.s.h = 0x01; +} break; + +#endif diff --git a/bsnes/cpu/scpu/core/op_pc.b b/bsnes/cpu/scpu/core/op_pc.b new file mode 100755 index 0000000..aac012c --- /dev/null +++ b/bsnes/cpu/scpu/core/op_pc.b @@ -0,0 +1,163 @@ +bcc(0x90, !regs.p.c), +bcs(0xb0, regs.p.c), +bne(0xd0, !regs.p.z), +beq(0xf0, regs.p.z), +bpl(0x10, !regs.p.n), +bmi(0x30, regs.p.n), +bvc(0x50, !regs.p.v), +bvs(0x70, regs.p.v) { +1:if(!$1) last_cycle(); + rd.l = op_readpc(); + if($1) { + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; + } else { + end; + } +2:op_io_cond6(aa.w); +3:last_cycle(); + op_io(); +} + +bra(0x80) { +1:rd.l = op_readpc(); + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; +2:op_io_cond6(aa.w); +3:last_cycle(); + op_io(); +} + +brl(0x82) { +1:rd.l = op_readpc(); +2:rd.h = op_readpc(); +3:last_cycle(); + op_io(); + regs.pc.w = regs.pc.d + (int16)rd.w; +} + +jmp_addr(0x4c) { +1:rd.l = op_readpc(); +2:last_cycle(); + rd.h = op_readpc(); + regs.pc.w = rd.w; +} + +jmp_long(0x5c) { +1:rd.l = op_readpc(); +2:rd.h = op_readpc(); +3:last_cycle(); + rd.b = op_readpc(); + regs.pc.d = rd.d & 0xffffff; +} + +jmp_iaddr(0x6c) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:rd.l = op_readaddr(aa.w); +4:last_cycle(); + rd.h = op_readaddr(aa.w + 1); + regs.pc.w = rd.w; +} + +jmp_iaddrx(0x7c) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:op_io(); +4:rd.l = op_readpbr(aa.w + regs.x.w); +5:last_cycle(); + rd.h = op_readpbr(aa.w + regs.x.w + 1); + regs.pc.w = rd.w; +} + +jmp_iladdr(0xdc) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:rd.l = op_readaddr(aa.w); +4:rd.h = op_readaddr(aa.w + 1); +5:last_cycle(); + rd.b = op_readaddr(aa.w + 2); + regs.pc.d = rd.d & 0xffffff; +} + +jsr_addr(0x20) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:op_io(); +4:regs.pc.w--; + op_writestack(regs.pc.h); +5:last_cycle(); + op_writestack(regs.pc.l); + regs.pc.w = aa.w; +} + +jsr_long(0x22) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:op_writestackn(regs.pc.b); +4:op_io(); +5:aa.b = op_readpc(); +6:regs.pc.w--; + op_writestackn(regs.pc.h); +7:last_cycle(); + op_writestackn(regs.pc.l); + regs.pc.d = aa.d & 0xffffff; + if(regs.e) regs.s.h = 0x01; +} + +jsr_iaddrx(0xfc) { +1:aa.l = op_readpc(); +2:op_writestackn(regs.pc.h); +3:op_writestackn(regs.pc.l); +4:aa.h = op_readpc(); +5:op_io(); +6:rd.l = op_readpbr(aa.w + regs.x.w); +7:last_cycle(); + rd.h = op_readpbr(aa.w + regs.x.w + 1); + regs.pc.w = rd.w; + if(regs.e) regs.s.h = 0x01; +} + +rti(0x40) { +1:op_io(); +2:op_io(); +3:regs.p = op_readstack(); + if(regs.e) regs.p |= 0x30; + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } +4:rd.l = op_readstack(); +5:if(regs.e) last_cycle(); + rd.h = op_readstack(); + if(regs.e) { + regs.pc.w = rd.w; + end; + } +6:last_cycle(); + rd.b = op_readstack(); + regs.pc.d = rd.d & 0xffffff; +} + +rts(0x60) { +1:op_io(); +2:op_io(); +3:rd.l = op_readstack(); +4:rd.h = op_readstack(); +5:last_cycle(); + op_io(); + regs.pc.w = rd.w; + regs.pc.w++; +} + +rtl(0x6b) { +1:op_io(); +2:op_io(); +3:rd.l = op_readstackn(); +4:rd.h = op_readstackn(); +5:last_cycle(); + rd.b = op_readstackn(); + regs.pc.d = rd.d & 0xffffff; + regs.pc.w++; + if(regs.e) regs.s.h = 0x01; +} diff --git a/bsnes/cpu/scpu/core/op_pc.cpp b/bsnes/cpu/scpu/core/op_pc.cpp new file mode 100755 index 0000000..9588206 --- /dev/null +++ b/bsnes/cpu/scpu/core/op_pc.cpp @@ -0,0 +1,279 @@ +#ifdef SCPU_CPP + +//bcc +case 0x90: { + if(!!regs.p.c) last_cycle(); + rd.l = op_readpc(); + if(!regs.p.c) { + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; + } else { + break; + } + op_io_cond6(aa.w); + last_cycle(); + op_io(); +} break; + +//bcs +case 0xb0: { + if(!regs.p.c) last_cycle(); + rd.l = op_readpc(); + if(regs.p.c) { + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; + } else { + break; + } + op_io_cond6(aa.w); + last_cycle(); + op_io(); +} break; + +//bne +case 0xd0: { + if(!!regs.p.z) last_cycle(); + rd.l = op_readpc(); + if(!regs.p.z) { + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; + } else { + break; + } + op_io_cond6(aa.w); + last_cycle(); + op_io(); +} break; + +//beq +case 0xf0: { + if(!regs.p.z) last_cycle(); + rd.l = op_readpc(); + if(regs.p.z) { + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; + } else { + break; + } + op_io_cond6(aa.w); + last_cycle(); + op_io(); +} break; + +//bpl +case 0x10: { + if(!!regs.p.n) last_cycle(); + rd.l = op_readpc(); + if(!regs.p.n) { + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; + } else { + break; + } + op_io_cond6(aa.w); + last_cycle(); + op_io(); +} break; + +//bmi +case 0x30: { + if(!regs.p.n) last_cycle(); + rd.l = op_readpc(); + if(regs.p.n) { + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; + } else { + break; + } + op_io_cond6(aa.w); + last_cycle(); + op_io(); +} break; + +//bvc +case 0x50: { + if(!!regs.p.v) last_cycle(); + rd.l = op_readpc(); + if(!regs.p.v) { + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; + } else { + break; + } + op_io_cond6(aa.w); + last_cycle(); + op_io(); +} break; + +//bvs +case 0x70: { + if(!regs.p.v) last_cycle(); + rd.l = op_readpc(); + if(regs.p.v) { + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; + } else { + break; + } + op_io_cond6(aa.w); + last_cycle(); + op_io(); +} break; + +//bra +case 0x80: { + rd.l = op_readpc(); + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; + op_io_cond6(aa.w); + last_cycle(); + op_io(); +} break; + +//brl +case 0x82: { + rd.l = op_readpc(); + rd.h = op_readpc(); + last_cycle(); + op_io(); + regs.pc.w = regs.pc.d + (int16)rd.w; +} break; + +//jmp_addr +case 0x4c: { + rd.l = op_readpc(); + last_cycle(); + rd.h = op_readpc(); + regs.pc.w = rd.w; +} break; + +//jmp_long +case 0x5c: { + rd.l = op_readpc(); + rd.h = op_readpc(); + last_cycle(); + rd.b = op_readpc(); + regs.pc.d = rd.d & 0xffffff; +} break; + +//jmp_iaddr +case 0x6c: { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readaddr(aa.w); + last_cycle(); + rd.h = op_readaddr(aa.w + 1); + regs.pc.w = rd.w; +} break; + +//jmp_iaddrx +case 0x7c: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readpbr(aa.w + regs.x.w); + last_cycle(); + rd.h = op_readpbr(aa.w + regs.x.w + 1); + regs.pc.w = rd.w; +} break; + +//jmp_iladdr +case 0xdc: { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readaddr(aa.w); + rd.h = op_readaddr(aa.w + 1); + last_cycle(); + rd.b = op_readaddr(aa.w + 2); + regs.pc.d = rd.d & 0xffffff; +} break; + +//jsr_addr +case 0x20: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + regs.pc.w--; + op_writestack(regs.pc.h); + last_cycle(); + op_writestack(regs.pc.l); + regs.pc.w = aa.w; +} break; + +//jsr_long +case 0x22: { + 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); + last_cycle(); + op_writestackn(regs.pc.l); + regs.pc.d = aa.d & 0xffffff; + if(regs.e) regs.s.h = 0x01; +} break; + +//jsr_iaddrx +case 0xfc: { + 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); + last_cycle(); + rd.h = op_readpbr(aa.w + regs.x.w + 1); + regs.pc.w = rd.w; + if(regs.e) regs.s.h = 0x01; +} break; + +//rti +case 0x40: { + op_io(); + op_io(); + regs.p = op_readstack(); + if(regs.e) regs.p |= 0x30; + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + rd.l = op_readstack(); + if(regs.e) last_cycle(); + rd.h = op_readstack(); + if(regs.e) { + regs.pc.w = rd.w; + break; + } + last_cycle(); + rd.b = op_readstack(); + regs.pc.d = rd.d & 0xffffff; +} break; + +//rts +case 0x60: { + op_io(); + op_io(); + rd.l = op_readstack(); + rd.h = op_readstack(); + last_cycle(); + op_io(); + regs.pc.w = rd.w; + regs.pc.w++; +} break; + +//rtl +case 0x6b: { + op_io(); + op_io(); + rd.l = op_readstackn(); + rd.h = op_readstackn(); + last_cycle(); + rd.b = op_readstackn(); + regs.pc.d = rd.d & 0xffffff; + regs.pc.w++; + if(regs.e) regs.s.h = 0x01; +} break; + +#endif diff --git a/bsnes/cpu/scpu/core/op_read.b b/bsnes/cpu/scpu/core/op_read.b new file mode 100755 index 0000000..82785f9 --- /dev/null +++ b/bsnes/cpu/scpu/core/op_read.b @@ -0,0 +1,317 @@ +adc_const(0x69, adc, regs.p.m), +and_const(0x29, and, regs.p.m), +cmp_const(0xc9, cmp, regs.p.m), +cpx_const(0xe0, cpx, regs.p.x), +cpy_const(0xc0, cpy, regs.p.x), +eor_const(0x49, eor, regs.p.m), +lda_const(0xa9, lda, regs.p.m), +ldx_const(0xa2, ldx, regs.p.x), +ldy_const(0xa0, ldy, regs.p.x), +ora_const(0x09, ora, regs.p.m), +sbc_const(0xe9, sbc, regs.p.m) { +1:if($2) last_cycle(); + rd.l = op_readpc(); + if($2) { op_$1_b(); end; } +2:last_cycle(); + rd.h = op_readpc(); + op_$1_w(); +} + +adc_addr(0x6d, adc, regs.p.m), +and_addr(0x2d, and, regs.p.m), +bit_addr(0x2c, bit, regs.p.m), +cmp_addr(0xcd, cmp, regs.p.m), +cpx_addr(0xec, cpx, regs.p.x), +cpy_addr(0xcc, cpy, regs.p.x), +eor_addr(0x4d, eor, regs.p.m), +lda_addr(0xad, lda, regs.p.m), +ldx_addr(0xae, ldx, regs.p.x), +ldy_addr(0xac, ldy, regs.p.x), +ora_addr(0x0d, ora, regs.p.m), +sbc_addr(0xed, sbc, regs.p.m) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:if($2) last_cycle(); + rd.l = op_readdbr(aa.w); + if($2) { op_$1_b(); end; } +4:last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_$1_w(); +} + +adc_addrx(0x7d, adc, regs.p.m), +and_addrx(0x3d, and, regs.p.m), +bit_addrx(0x3c, bit, regs.p.m), +cmp_addrx(0xdd, cmp, regs.p.m), +eor_addrx(0x5d, eor, regs.p.m), +lda_addrx(0xbd, lda, regs.p.m), +ldy_addrx(0xbc, ldy, regs.p.x), +ora_addrx(0x1d, ora, regs.p.m), +sbc_addrx(0xfd, sbc, regs.p.m) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:op_io_cond4(aa.w, aa.w + regs.x.w); +4:if($2) last_cycle(); + rd.l = op_readdbr(aa.w + regs.x.w); + if($2) { op_$1_b(); end; } +5:last_cycle(); + rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_$1_w(); +} + +adc_addry(0x79, adc, regs.p.m), +and_addry(0x39, and, regs.p.m), +cmp_addry(0xd9, cmp, regs.p.m), +eor_addry(0x59, eor, regs.p.m), +lda_addry(0xb9, lda, regs.p.m), +ldx_addry(0xbe, ldx, regs.p.x), +ora_addry(0x19, ora, regs.p.m), +sbc_addry(0xf9, sbc, regs.p.m) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:op_io_cond4(aa.w, aa.w + regs.y.w); +4:if($2) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if($2) { op_$1_b(); end; } +5:last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_$1_w(); +} + +adc_long(0x6f, adc, regs.p.m), +and_long(0x2f, and, regs.p.m), +cmp_long(0xcf, cmp, regs.p.m), +eor_long(0x4f, eor, regs.p.m), +lda_long(0xaf, lda, regs.p.m), +ora_long(0x0f, ora, regs.p.m), +sbc_long(0xef, sbc, regs.p.m) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:aa.b = op_readpc(); +4:if($2) last_cycle(); + rd.l = op_readlong(aa.d); + if($2) { op_$1_b(); end; } +5:last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_$1_w(); +} + +adc_longx(0x7f, adc, regs.p.m), +and_longx(0x3f, and, regs.p.m), +cmp_longx(0xdf, cmp, regs.p.m), +eor_longx(0x5f, eor, regs.p.m), +lda_longx(0xbf, lda, regs.p.m), +ora_longx(0x1f, ora, regs.p.m), +sbc_longx(0xff, sbc, regs.p.m) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:aa.b = op_readpc(); +4:if($2) last_cycle(); + rd.l = op_readlong(aa.d + regs.x.w); + if($2) { op_$1_b(); end; } +5:last_cycle(); + rd.h = op_readlong(aa.d + regs.x.w + 1); + op_$1_w(); +} + +adc_dp(0x65, adc, regs.p.m), +and_dp(0x25, and, regs.p.m), +bit_dp(0x24, bit, regs.p.m), +cmp_dp(0xc5, cmp, regs.p.m), +cpx_dp(0xe4, cpx, regs.p.x), +cpy_dp(0xc4, cpy, regs.p.x), +eor_dp(0x45, eor, regs.p.m), +lda_dp(0xa5, lda, regs.p.m), +ldx_dp(0xa6, ldx, regs.p.x), +ldy_dp(0xa4, ldy, regs.p.x), +ora_dp(0x05, ora, regs.p.m), +sbc_dp(0xe5, sbc, regs.p.m) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:if($2) last_cycle(); + rd.l = op_readdp(dp); + if($2) { op_$1_b(); end; } +4:last_cycle(); + rd.h = op_readdp(dp + 1); + op_$1_w(); +} + +adc_dpx(0x75, adc, regs.p.m), +and_dpx(0x35, and, regs.p.m), +bit_dpx(0x34, bit, regs.p.m), +cmp_dpx(0xd5, cmp, regs.p.m), +eor_dpx(0x55, eor, regs.p.m), +lda_dpx(0xb5, lda, regs.p.m), +ldy_dpx(0xb4, ldy, regs.p.x), +ora_dpx(0x15, ora, regs.p.m), +sbc_dpx(0xf5, sbc, regs.p.m) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:op_io(); +4:if($2) last_cycle(); + rd.l = op_readdp(dp + regs.x.w); + if($2) { op_$1_b(); end; } +5:last_cycle(); + rd.h = op_readdp(dp + regs.x.w + 1); + op_$1_w(); +} + +ldx_dpy(0xb6, ldx, regs.p.x) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:op_io(); +4:if($2) last_cycle(); + rd.l = op_readdp(dp + regs.y.w); + if($2) { op_$1_b(); end; } +5:last_cycle(); + rd.h = op_readdp(dp + regs.y.w + 1); + op_$1_w(); +} + +adc_idp(0x72, adc, regs.p.m), +and_idp(0x32, and, regs.p.m), +cmp_idp(0xd2, cmp, regs.p.m), +eor_idp(0x52, eor, regs.p.m), +lda_idp(0xb2, lda, regs.p.m), +ora_idp(0x12, ora, regs.p.m), +sbc_idp(0xf2, sbc, regs.p.m) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:aa.l = op_readdp(dp); +4:aa.h = op_readdp(dp + 1); +5:if($2) last_cycle(); + rd.l = op_readdbr(aa.w); + if($2) { op_$1_b(); end; } +6:last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_$1_w(); +} + +adc_idpx(0x61, adc, regs.p.m), +and_idpx(0x21, and, regs.p.m), +cmp_idpx(0xc1, cmp, regs.p.m), +eor_idpx(0x41, eor, regs.p.m), +lda_idpx(0xa1, lda, regs.p.m), +ora_idpx(0x01, ora, regs.p.m), +sbc_idpx(0xe1, sbc, regs.p.m) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:op_io(); +4:aa.l = op_readdp(dp + regs.x.w); +5:aa.h = op_readdp(dp + regs.x.w + 1); +6:if($2) last_cycle(); + rd.l = op_readdbr(aa.w); + if($2) { op_$1_b(); end; } +7:last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_$1_w(); +} + +adc_idpy(0x71, adc, regs.p.m), +and_idpy(0x31, and, regs.p.m), +cmp_idpy(0xd1, cmp, regs.p.m), +eor_idpy(0x51, eor, regs.p.m), +lda_idpy(0xb1, lda, regs.p.m), +ora_idpy(0x11, ora, regs.p.m), +sbc_idpy(0xf1, sbc, regs.p.m) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:aa.l = op_readdp(dp); +4:aa.h = op_readdp(dp + 1); +5:op_io_cond4(aa.w, aa.w + regs.y.w); +6:if($2) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if($2) { op_$1_b(); end; } +7:last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_$1_w(); +} + +adc_ildp(0x67, adc, regs.p.m), +and_ildp(0x27, and, regs.p.m), +cmp_ildp(0xc7, cmp, regs.p.m), +eor_ildp(0x47, eor, regs.p.m), +lda_ildp(0xa7, lda, regs.p.m), +ora_ildp(0x07, ora, regs.p.m), +sbc_ildp(0xe7, sbc, regs.p.m) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:aa.l = op_readdp(dp); +4:aa.h = op_readdp(dp + 1); +5:aa.b = op_readdp(dp + 2); +6:if($2) last_cycle(); + rd.l = op_readlong(aa.d); + if($2) { op_$1_b(); end; } +7:last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_$1_w(); +} + +adc_ildpy(0x77, adc, regs.p.m), +and_ildpy(0x37, and, regs.p.m), +cmp_ildpy(0xd7, cmp, regs.p.m), +eor_ildpy(0x57, eor, regs.p.m), +lda_ildpy(0xb7, lda, regs.p.m), +ora_ildpy(0x17, ora, regs.p.m), +sbc_ildpy(0xf7, sbc, regs.p.m) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:aa.l = op_readdp(dp); +4:aa.h = op_readdp(dp + 1); +5:aa.b = op_readdp(dp + 2); +6:if($2) last_cycle(); + rd.l = op_readlong(aa.d + regs.y.w); + if($2) { op_$1_b(); end; } +7:last_cycle(); + rd.h = op_readlong(aa.d + regs.y.w + 1); + op_$1_w(); +} + +adc_sr(0x63, adc, regs.p.m), +and_sr(0x23, and, regs.p.m), +cmp_sr(0xc3, cmp, regs.p.m), +eor_sr(0x43, eor, regs.p.m), +lda_sr(0xa3, lda, regs.p.m), +ora_sr(0x03, ora, regs.p.m), +sbc_sr(0xe3, sbc, regs.p.m) { +1:sp = op_readpc(); +2:op_io(); +3:if($2) last_cycle(); + rd.l = op_readsp(sp); + if($2) { op_$1_b(); end; } +4:last_cycle(); + rd.h = op_readsp(sp + 1); + op_$1_w(); +} + +adc_isry(0x73, adc), +and_isry(0x33, and), +cmp_isry(0xd3, cmp), +eor_isry(0x53, eor), +lda_isry(0xb3, lda), +ora_isry(0x13, ora), +sbc_isry(0xf3, sbc) { +1:sp = op_readpc(); +2:op_io(); +3:aa.l = op_readsp(sp); +4:aa.h = op_readsp(sp + 1); +5:op_io(); +6:if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_$1_b(); end; } +7:last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_$1_w(); +} + +bit_const(0x89) { +1:if(regs.p.m) last_cycle(); + rd.l = op_readpc(); + if(regs.p.m) { + regs.p.z = ((rd.l & regs.a.l) == 0); + end; + } +2:last_cycle(); + rd.h = op_readpc(); + regs.p.z = ((rd.w & regs.a.w) == 0); +} diff --git a/bsnes/cpu/scpu/core/op_read.cpp b/bsnes/cpu/scpu/core/op_read.cpp new file mode 100755 index 0000000..a486b5f --- /dev/null +++ b/bsnes/cpu/scpu/core/op_read.cpp @@ -0,0 +1,1654 @@ +#ifdef SCPU_CPP + +//adc_const +case 0x69: { + if(regs.p.m) last_cycle(); + rd.l = op_readpc(); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readpc(); + op_adc_w(); +} break; + +//and_const +case 0x29: { + if(regs.p.m) last_cycle(); + rd.l = op_readpc(); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readpc(); + op_and_w(); +} break; + +//cmp_const +case 0xc9: { + if(regs.p.m) last_cycle(); + rd.l = op_readpc(); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readpc(); + op_cmp_w(); +} break; + +//cpx_const +case 0xe0: { + if(regs.p.x) last_cycle(); + rd.l = op_readpc(); + if(regs.p.x) { op_cpx_b(); break; } + last_cycle(); + rd.h = op_readpc(); + op_cpx_w(); +} break; + +//cpy_const +case 0xc0: { + if(regs.p.x) last_cycle(); + rd.l = op_readpc(); + if(regs.p.x) { op_cpy_b(); break; } + last_cycle(); + rd.h = op_readpc(); + op_cpy_w(); +} break; + +//eor_const +case 0x49: { + if(regs.p.m) last_cycle(); + rd.l = op_readpc(); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readpc(); + op_eor_w(); +} break; + +//lda_const +case 0xa9: { + if(regs.p.m) last_cycle(); + rd.l = op_readpc(); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readpc(); + op_lda_w(); +} break; + +//ldx_const +case 0xa2: { + if(regs.p.x) last_cycle(); + rd.l = op_readpc(); + if(regs.p.x) { op_ldx_b(); break; } + last_cycle(); + rd.h = op_readpc(); + op_ldx_w(); +} break; + +//ldy_const +case 0xa0: { + if(regs.p.x) last_cycle(); + rd.l = op_readpc(); + if(regs.p.x) { op_ldy_b(); break; } + last_cycle(); + rd.h = op_readpc(); + op_ldy_w(); +} break; + +//ora_const +case 0x09: { + if(regs.p.m) last_cycle(); + rd.l = op_readpc(); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readpc(); + op_ora_w(); +} break; + +//sbc_const +case 0xe9: { + if(regs.p.m) last_cycle(); + rd.l = op_readpc(); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readpc(); + op_sbc_w(); +} break; + +//adc_addr +case 0x6d: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_adc_w(); +} break; + +//and_addr +case 0x2d: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_and_w(); +} break; + +//bit_addr +case 0x2c: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_bit_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_bit_w(); +} break; + +//cmp_addr +case 0xcd: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_cmp_w(); +} break; + +//cpx_addr +case 0xec: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.x) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.x) { op_cpx_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_cpx_w(); +} break; + +//cpy_addr +case 0xcc: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.x) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.x) { op_cpy_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_cpy_w(); +} break; + +//eor_addr +case 0x4d: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_eor_w(); +} break; + +//lda_addr +case 0xad: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_lda_w(); +} break; + +//ldx_addr +case 0xae: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.x) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.x) { op_ldx_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_ldx_w(); +} break; + +//ldy_addr +case 0xac: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.x) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.x) { op_ldy_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_ldy_w(); +} break; + +//ora_addr +case 0x0d: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_ora_w(); +} break; + +//sbc_addr +case 0xed: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_sbc_w(); +} break; + +//adc_addrx +case 0x7d: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_adc_w(); +} break; + +//and_addrx +case 0x3d: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_and_w(); +} break; + +//bit_addrx +case 0x3c: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(regs.p.m) { op_bit_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_bit_w(); +} break; + +//cmp_addrx +case 0xdd: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_cmp_w(); +} break; + +//eor_addrx +case 0x5d: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_eor_w(); +} break; + +//lda_addrx +case 0xbd: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_lda_w(); +} break; + +//ldy_addrx +case 0xbc: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); + if(regs.p.x) last_cycle(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(regs.p.x) { op_ldy_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_ldy_w(); +} break; + +//ora_addrx +case 0x1d: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_ora_w(); +} break; + +//sbc_addrx +case 0xfd: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_sbc_w(); +} break; + +//adc_addry +case 0x79: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_adc_w(); +} break; + +//and_addry +case 0x39: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_and_w(); +} break; + +//cmp_addry +case 0xd9: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_cmp_w(); +} break; + +//eor_addry +case 0x59: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_eor_w(); +} break; + +//lda_addry +case 0xb9: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_lda_w(); +} break; + +//ldx_addry +case 0xbe: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.x) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.x) { op_ldx_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_ldx_w(); +} break; + +//ora_addry +case 0x19: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_ora_w(); +} break; + +//sbc_addry +case 0xf9: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_sbc_w(); +} break; + +//adc_long +case 0x6f: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_adc_w(); +} break; + +//and_long +case 0x2f: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_and_w(); +} break; + +//cmp_long +case 0xcf: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_cmp_w(); +} break; + +//eor_long +case 0x4f: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_eor_w(); +} break; + +//lda_long +case 0xaf: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_lda_w(); +} break; + +//ora_long +case 0x0f: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_ora_w(); +} break; + +//sbc_long +case 0xef: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_sbc_w(); +} break; + +//adc_longx +case 0x7f: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.x.w); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.x.w + 1); + op_adc_w(); +} break; + +//and_longx +case 0x3f: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.x.w); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.x.w + 1); + op_and_w(); +} break; + +//cmp_longx +case 0xdf: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.x.w); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.x.w + 1); + op_cmp_w(); +} break; + +//eor_longx +case 0x5f: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.x.w); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.x.w + 1); + op_eor_w(); +} break; + +//lda_longx +case 0xbf: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.x.w); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.x.w + 1); + op_lda_w(); +} break; + +//ora_longx +case 0x1f: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.x.w); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.x.w + 1); + op_ora_w(); +} break; + +//sbc_longx +case 0xff: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.x.w); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.x.w + 1); + op_sbc_w(); +} break; + +//adc_dp +case 0x65: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + 1); + op_adc_w(); +} break; + +//and_dp +case 0x25: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + 1); + op_and_w(); +} break; + +//bit_dp +case 0x24: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp); + if(regs.p.m) { op_bit_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + 1); + op_bit_w(); +} break; + +//cmp_dp +case 0xc5: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + 1); + op_cmp_w(); +} break; + +//cpx_dp +case 0xe4: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.x) last_cycle(); + rd.l = op_readdp(dp); + if(regs.p.x) { op_cpx_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + 1); + op_cpx_w(); +} break; + +//cpy_dp +case 0xc4: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.x) last_cycle(); + rd.l = op_readdp(dp); + if(regs.p.x) { op_cpy_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + 1); + op_cpy_w(); +} break; + +//eor_dp +case 0x45: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + 1); + op_eor_w(); +} break; + +//lda_dp +case 0xa5: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + 1); + op_lda_w(); +} break; + +//ldx_dp +case 0xa6: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.x) last_cycle(); + rd.l = op_readdp(dp); + if(regs.p.x) { op_ldx_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + 1); + op_ldx_w(); +} break; + +//ldy_dp +case 0xa4: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.x) last_cycle(); + rd.l = op_readdp(dp); + if(regs.p.x) { op_ldy_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + 1); + op_ldy_w(); +} break; + +//ora_dp +case 0x05: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + 1); + op_ora_w(); +} break; + +//sbc_dp +case 0xe5: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + 1); + op_sbc_w(); +} break; + +//adc_dpx +case 0x75: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp + regs.x.w); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + regs.x.w + 1); + op_adc_w(); +} break; + +//and_dpx +case 0x35: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp + regs.x.w); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + regs.x.w + 1); + op_and_w(); +} break; + +//bit_dpx +case 0x34: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp + regs.x.w); + if(regs.p.m) { op_bit_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + regs.x.w + 1); + op_bit_w(); +} break; + +//cmp_dpx +case 0xd5: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp + regs.x.w); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + regs.x.w + 1); + op_cmp_w(); +} break; + +//eor_dpx +case 0x55: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp + regs.x.w); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + regs.x.w + 1); + op_eor_w(); +} break; + +//lda_dpx +case 0xb5: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp + regs.x.w); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + regs.x.w + 1); + op_lda_w(); +} break; + +//ldy_dpx +case 0xb4: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.x) last_cycle(); + rd.l = op_readdp(dp + regs.x.w); + if(regs.p.x) { op_ldy_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + regs.x.w + 1); + op_ldy_w(); +} break; + +//ora_dpx +case 0x15: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp + regs.x.w); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + regs.x.w + 1); + op_ora_w(); +} break; + +//sbc_dpx +case 0xf5: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdp(dp + regs.x.w); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + regs.x.w + 1); + op_sbc_w(); +} break; + +//ldx_dpy +case 0xb6: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.x) last_cycle(); + rd.l = op_readdp(dp + regs.y.w); + if(regs.p.x) { op_ldx_b(); break; } + last_cycle(); + rd.h = op_readdp(dp + regs.y.w + 1); + op_ldx_w(); +} break; + +//adc_idp +case 0x72: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_adc_w(); +} break; + +//and_idp +case 0x32: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_and_w(); +} break; + +//cmp_idp +case 0xd2: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_cmp_w(); +} break; + +//eor_idp +case 0x52: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_eor_w(); +} break; + +//lda_idp +case 0xb2: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_lda_w(); +} break; + +//ora_idp +case 0x12: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_ora_w(); +} break; + +//sbc_idp +case 0xf2: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_sbc_w(); +} break; + +//adc_idpx +case 0x61: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w); + aa.h = op_readdp(dp + regs.x.w + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_adc_w(); +} break; + +//and_idpx +case 0x21: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w); + aa.h = op_readdp(dp + regs.x.w + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_and_w(); +} break; + +//cmp_idpx +case 0xc1: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w); + aa.h = op_readdp(dp + regs.x.w + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_cmp_w(); +} break; + +//eor_idpx +case 0x41: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w); + aa.h = op_readdp(dp + regs.x.w + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_eor_w(); +} break; + +//lda_idpx +case 0xa1: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w); + aa.h = op_readdp(dp + regs.x.w + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_lda_w(); +} break; + +//ora_idpx +case 0x01: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w); + aa.h = op_readdp(dp + regs.x.w + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_ora_w(); +} break; + +//sbc_idpx +case 0xe1: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w); + aa.h = op_readdp(dp + regs.x.w + 1); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + 1); + op_sbc_w(); +} break; + +//adc_idpy +case 0x71: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_adc_w(); +} break; + +//and_idpy +case 0x31: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_and_w(); +} break; + +//cmp_idpy +case 0xd1: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_cmp_w(); +} break; + +//eor_idpy +case 0x51: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_eor_w(); +} break; + +//lda_idpy +case 0xb1: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_lda_w(); +} break; + +//ora_idpy +case 0x11: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_ora_w(); +} break; + +//sbc_idpy +case 0xf1: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + op_io_cond4(aa.w, aa.w + regs.y.w); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_sbc_w(); +} break; + +//adc_ildp +case 0x67: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_adc_w(); +} break; + +//and_ildp +case 0x27: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_and_w(); +} break; + +//cmp_ildp +case 0xc7: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_cmp_w(); +} break; + +//eor_ildp +case 0x47: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_eor_w(); +} break; + +//lda_ildp +case 0xa7: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_lda_w(); +} break; + +//ora_ildp +case 0x07: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_ora_w(); +} break; + +//sbc_ildp +case 0xe7: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + 1); + op_sbc_w(); +} break; + +//adc_ildpy +case 0x77: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.y.w); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.y.w + 1); + op_adc_w(); +} break; + +//and_ildpy +case 0x37: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.y.w); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.y.w + 1); + op_and_w(); +} break; + +//cmp_ildpy +case 0xd7: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.y.w); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.y.w + 1); + op_cmp_w(); +} break; + +//eor_ildpy +case 0x57: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.y.w); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.y.w + 1); + op_eor_w(); +} break; + +//lda_ildpy +case 0xb7: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.y.w); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.y.w + 1); + op_lda_w(); +} break; + +//ora_ildpy +case 0x17: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.y.w); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.y.w + 1); + op_ora_w(); +} break; + +//sbc_ildpy +case 0xf7: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + rd.l = op_readlong(aa.d + regs.y.w); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readlong(aa.d + regs.y.w + 1); + op_sbc_w(); +} break; + +//adc_sr +case 0x63: { + sp = op_readpc(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readsp(sp); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readsp(sp + 1); + op_adc_w(); +} break; + +//and_sr +case 0x23: { + sp = op_readpc(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readsp(sp); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readsp(sp + 1); + op_and_w(); +} break; + +//cmp_sr +case 0xc3: { + sp = op_readpc(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readsp(sp); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readsp(sp + 1); + op_cmp_w(); +} break; + +//eor_sr +case 0x43: { + sp = op_readpc(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readsp(sp); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readsp(sp + 1); + op_eor_w(); +} break; + +//lda_sr +case 0xa3: { + sp = op_readpc(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readsp(sp); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readsp(sp + 1); + op_lda_w(); +} break; + +//ora_sr +case 0x03: { + sp = op_readpc(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readsp(sp); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readsp(sp + 1); + op_ora_w(); +} break; + +//sbc_sr +case 0xe3: { + sp = op_readpc(); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readsp(sp); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readsp(sp + 1); + op_sbc_w(); +} break; + +//adc_isry +case 0x73: { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp); + aa.h = op_readsp(sp + 1); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_adc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_adc_w(); +} break; + +//and_isry +case 0x33: { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp); + aa.h = op_readsp(sp + 1); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_and_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_and_w(); +} break; + +//cmp_isry +case 0xd3: { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp); + aa.h = op_readsp(sp + 1); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_cmp_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_cmp_w(); +} break; + +//eor_isry +case 0x53: { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp); + aa.h = op_readsp(sp + 1); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_eor_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_eor_w(); +} break; + +//lda_isry +case 0xb3: { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp); + aa.h = op_readsp(sp + 1); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_lda_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_lda_w(); +} break; + +//ora_isry +case 0x13: { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp); + aa.h = op_readsp(sp + 1); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_ora_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_ora_w(); +} break; + +//sbc_isry +case 0xf3: { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp); + aa.h = op_readsp(sp + 1); + op_io(); + if(regs.p.m) last_cycle(); + rd.l = op_readdbr(aa.w + regs.y.w); + if(regs.p.m) { op_sbc_b(); break; } + last_cycle(); + rd.h = op_readdbr(aa.w + regs.y.w + 1); + op_sbc_w(); +} break; + +//bit_const +case 0x89: { + if(regs.p.m) last_cycle(); + rd.l = op_readpc(); + if(regs.p.m) { + regs.p.z = ((rd.l & regs.a.l) == 0); + break; + } + last_cycle(); + rd.h = op_readpc(); + regs.p.z = ((rd.w & regs.a.w) == 0); +} break; + +#endif diff --git a/bsnes/cpu/scpu/core/op_rmw.b b/bsnes/cpu/scpu/core/op_rmw.b new file mode 100755 index 0000000..14b1613 --- /dev/null +++ b/bsnes/cpu/scpu/core/op_rmw.b @@ -0,0 +1,181 @@ +inc(0x1a, regs.p.m, a), +inx(0xe8, regs.p.x, x), +iny(0xc8, regs.p.x, y) { +1:last_cycle(); + op_io_irq(); + if($1) { + regs.$2.l++; + regs.p.n = !!(regs.$2.l & 0x80); + regs.p.z = (regs.$2.l == 0); + } else { + regs.$2.w++; + regs.p.n = !!(regs.$2.w & 0x8000); + regs.p.z = (regs.$2.w == 0); + } +} + +dec(0x3a, regs.p.m, a), +dex(0xca, regs.p.x, x), +dey(0x88, regs.p.x, y) { +1:last_cycle(); + op_io_irq(); + if($1) { + regs.$2.l--; + regs.p.n = !!(regs.$2.l & 0x80); + regs.p.z = (regs.$2.l == 0); + } else { + regs.$2.w--; + regs.p.n = !!(regs.$2.w & 0x8000); + regs.p.z = (regs.$2.w == 0); + } +} + +asl(0x0a) { +1:last_cycle(); + op_io_irq(); + if(regs.p.m) { + 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); + } else { + 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); + } +} + +lsr(0x4a) { +1:last_cycle(); + op_io_irq(); + if(regs.p.m) { + regs.p.c = regs.a.l & 1; + regs.a.l >>= 1; + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + } else { + regs.p.c = regs.a.w & 1; + regs.a.w >>= 1; + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); + } +} + +rol(0x2a) { +1:last_cycle(); + op_io_irq(); + uint16 c = regs.p.c; + if(regs.p.m) { + regs.p.c = !!(regs.a.l & 0x80); + regs.a.l <<= 1; + regs.a.l |= c; + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + } else { + regs.p.c = !!(regs.a.w & 0x8000); + regs.a.w <<= 1; + regs.a.w |= c; + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); + } +} + +ror(0x6a) { +1:last_cycle(); + op_io_irq(); + uint16 c; + if(regs.p.m) { + c = regs.p.c ? 0x80 : 0; + regs.p.c = regs.a.l & 1; + regs.a.l >>= 1; + regs.a.l |= c; + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + } else { + c = regs.p.c ? 0x8000 : 0; + regs.p.c = regs.a.w & 1; + regs.a.w >>= 1; + regs.a.w |= c; + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); + } +} + +inc_addr(0xee, inc), +dec_addr(0xce, dec), +asl_addr(0x0e, asl), +lsr_addr(0x4e, lsr), +rol_addr(0x2e, rol), +ror_addr(0x6e, ror), +trb_addr(0x1c, trb), +tsb_addr(0x0c, tsb) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:rd.l = op_readdbr(aa.w); +4:if(!regs.p.m) rd.h = op_readdbr(aa.w + 1); +5:op_io(); + if(regs.p.m) { op_$1_b(); } + else { op_$1_w(); +6:op_writedbr(aa.w + 1, rd.h); } +7:last_cycle(); + op_writedbr(aa.w, rd.l); +} + +inc_addrx(0xfe, inc), +dec_addrx(0xde, dec), +asl_addrx(0x1e, asl), +lsr_addrx(0x5e, lsr), +rol_addrx(0x3e, rol), +ror_addrx(0x7e, ror) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:op_io(); +4:rd.l = op_readdbr(aa.w + regs.x.w); +5:if(!regs.p.m) rd.h = op_readdbr(aa.w + regs.x.w + 1); +6:op_io(); + if(regs.p.m) { op_$1_b(); } + else { op_$1_w(); +7:op_writedbr(aa.w + regs.x.w + 1, rd.h); } +8:last_cycle(); + op_writedbr(aa.w + regs.x.w, rd.l); +} + +inc_dp(0xe6, inc), +dec_dp(0xc6, dec), +asl_dp(0x06, asl), +lsr_dp(0x46, lsr), +rol_dp(0x26, rol), +ror_dp(0x66, ror), +trb_dp(0x14, trb), +tsb_dp(0x04, tsb) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:rd.l = op_readdp(dp); +4:if(!regs.p.m) rd.h = op_readdp(dp + 1); +5:op_io(); + if(regs.p.m) { op_$1_b(); } + else { op_$1_w(); +6:op_writedp(dp + 1, rd.h); } +7:last_cycle(); + op_writedp(dp, rd.l); +} + +inc_dpx(0xf6, inc), +dec_dpx(0xd6, dec), +asl_dpx(0x16, asl), +lsr_dpx(0x56, lsr), +rol_dpx(0x36, rol), +ror_dpx(0x76, ror) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:op_io(); +4:rd.l = op_readdp(dp + regs.x.w); +5:if(!regs.p.m) rd.h = op_readdp(dp + regs.x.w + 1); +6:op_io(); + if(regs.p.m) { op_$1_b(); } + else { op_$1_w(); +7:op_writedp(dp + regs.x.w + 1, rd.h); } +8:last_cycle(); + op_writedp(dp + regs.x.w, rd.l); +} diff --git a/bsnes/cpu/scpu/core/op_rmw.cpp b/bsnes/cpu/scpu/core/op_rmw.cpp new file mode 100755 index 0000000..6d37dd0 --- /dev/null +++ b/bsnes/cpu/scpu/core/op_rmw.cpp @@ -0,0 +1,573 @@ +#ifdef SCPU_CPP + +//inc +case 0x1a: { + last_cycle(); + op_io_irq(); + if(regs.p.m) { + regs.a.l++; + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + } else { + regs.a.w++; + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); + } +} break; + +//inx +case 0xe8: { + last_cycle(); + op_io_irq(); + if(regs.p.x) { + regs.x.l++; + regs.p.n = !!(regs.x.l & 0x80); + regs.p.z = (regs.x.l == 0); + } else { + regs.x.w++; + regs.p.n = !!(regs.x.w & 0x8000); + regs.p.z = (regs.x.w == 0); + } +} break; + +//iny +case 0xc8: { + last_cycle(); + op_io_irq(); + if(regs.p.x) { + regs.y.l++; + regs.p.n = !!(regs.y.l & 0x80); + regs.p.z = (regs.y.l == 0); + } else { + regs.y.w++; + regs.p.n = !!(regs.y.w & 0x8000); + regs.p.z = (regs.y.w == 0); + } +} break; + +//dec +case 0x3a: { + last_cycle(); + op_io_irq(); + if(regs.p.m) { + regs.a.l--; + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + } else { + regs.a.w--; + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); + } +} break; + +//dex +case 0xca: { + last_cycle(); + op_io_irq(); + if(regs.p.x) { + regs.x.l--; + regs.p.n = !!(regs.x.l & 0x80); + regs.p.z = (regs.x.l == 0); + } else { + regs.x.w--; + regs.p.n = !!(regs.x.w & 0x8000); + regs.p.z = (regs.x.w == 0); + } +} break; + +//dey +case 0x88: { + last_cycle(); + op_io_irq(); + if(regs.p.x) { + regs.y.l--; + regs.p.n = !!(regs.y.l & 0x80); + regs.p.z = (regs.y.l == 0); + } else { + regs.y.w--; + regs.p.n = !!(regs.y.w & 0x8000); + regs.p.z = (regs.y.w == 0); + } +} break; + +//asl +case 0x0a: { + last_cycle(); + op_io_irq(); + if(regs.p.m) { + 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); + } else { + 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); + } +} break; + +//lsr +case 0x4a: { + last_cycle(); + op_io_irq(); + if(regs.p.m) { + regs.p.c = regs.a.l & 1; + regs.a.l >>= 1; + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + } else { + regs.p.c = regs.a.w & 1; + regs.a.w >>= 1; + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); + } +} break; + +//rol +case 0x2a: { + last_cycle(); + op_io_irq(); + uint16 c = regs.p.c; + if(regs.p.m) { + regs.p.c = !!(regs.a.l & 0x80); + regs.a.l <<= 1; + regs.a.l |= c; + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + } else { + regs.p.c = !!(regs.a.w & 0x8000); + regs.a.w <<= 1; + regs.a.w |= c; + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); + } +} break; + +//ror +case 0x6a: { + last_cycle(); + op_io_irq(); + uint16 c; + if(regs.p.m) { + c = regs.p.c ? 0x80 : 0; + regs.p.c = regs.a.l & 1; + regs.a.l >>= 1; + regs.a.l |= c; + regs.p.n = !!(regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); + } else { + c = regs.p.c ? 0x8000 : 0; + regs.p.c = regs.a.w & 1; + regs.a.w >>= 1; + regs.a.w |= c; + regs.p.n = !!(regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); + } +} break; + +//inc_addr +case 0xee: { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + 1); + op_io(); + if(regs.p.m) { op_inc_b(); } + else { op_inc_w(); + op_writedbr(aa.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w, rd.l); +} break; + +//dec_addr +case 0xce: { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + 1); + op_io(); + if(regs.p.m) { op_dec_b(); } + else { op_dec_w(); + op_writedbr(aa.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w, rd.l); +} break; + +//asl_addr +case 0x0e: { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + 1); + op_io(); + if(regs.p.m) { op_asl_b(); } + else { op_asl_w(); + op_writedbr(aa.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w, rd.l); +} break; + +//lsr_addr +case 0x4e: { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + 1); + op_io(); + if(regs.p.m) { op_lsr_b(); } + else { op_lsr_w(); + op_writedbr(aa.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w, rd.l); +} break; + +//rol_addr +case 0x2e: { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + 1); + op_io(); + if(regs.p.m) { op_rol_b(); } + else { op_rol_w(); + op_writedbr(aa.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w, rd.l); +} break; + +//ror_addr +case 0x6e: { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + 1); + op_io(); + if(regs.p.m) { op_ror_b(); } + else { op_ror_w(); + op_writedbr(aa.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w, rd.l); +} break; + +//trb_addr +case 0x1c: { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + 1); + op_io(); + if(regs.p.m) { op_trb_b(); } + else { op_trb_w(); + op_writedbr(aa.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w, rd.l); +} break; + +//tsb_addr +case 0x0c: { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + 1); + op_io(); + if(regs.p.m) { op_tsb_b(); } + else { op_tsb_w(); + op_writedbr(aa.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w, rd.l); +} break; + +//inc_addrx +case 0xfe: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_io(); + if(regs.p.m) { op_inc_b(); } + else { op_inc_w(); + op_writedbr(aa.w + regs.x.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w + regs.x.w, rd.l); +} break; + +//dec_addrx +case 0xde: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_io(); + if(regs.p.m) { op_dec_b(); } + else { op_dec_w(); + op_writedbr(aa.w + regs.x.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w + regs.x.w, rd.l); +} break; + +//asl_addrx +case 0x1e: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_io(); + if(regs.p.m) { op_asl_b(); } + else { op_asl_w(); + op_writedbr(aa.w + regs.x.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w + regs.x.w, rd.l); +} break; + +//lsr_addrx +case 0x5e: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_io(); + if(regs.p.m) { op_lsr_b(); } + else { op_lsr_w(); + op_writedbr(aa.w + regs.x.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w + regs.x.w, rd.l); +} break; + +//rol_addrx +case 0x3e: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_io(); + if(regs.p.m) { op_rol_b(); } + else { op_rol_w(); + op_writedbr(aa.w + regs.x.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w + regs.x.w, rd.l); +} break; + +//ror_addrx +case 0x7e: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readdbr(aa.w + regs.x.w); + if(!regs.p.m) rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_io(); + if(regs.p.m) { op_ror_b(); } + else { op_ror_w(); + op_writedbr(aa.w + regs.x.w + 1, rd.h); } + last_cycle(); + op_writedbr(aa.w + regs.x.w, rd.l); +} break; + +//inc_dp +case 0xe6: { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp); + if(!regs.p.m) rd.h = op_readdp(dp + 1); + op_io(); + if(regs.p.m) { op_inc_b(); } + else { op_inc_w(); + op_writedp(dp + 1, rd.h); } + last_cycle(); + op_writedp(dp, rd.l); +} break; + +//dec_dp +case 0xc6: { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp); + if(!regs.p.m) rd.h = op_readdp(dp + 1); + op_io(); + if(regs.p.m) { op_dec_b(); } + else { op_dec_w(); + op_writedp(dp + 1, rd.h); } + last_cycle(); + op_writedp(dp, rd.l); +} break; + +//asl_dp +case 0x06: { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp); + if(!regs.p.m) rd.h = op_readdp(dp + 1); + op_io(); + if(regs.p.m) { op_asl_b(); } + else { op_asl_w(); + op_writedp(dp + 1, rd.h); } + last_cycle(); + op_writedp(dp, rd.l); +} break; + +//lsr_dp +case 0x46: { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp); + if(!regs.p.m) rd.h = op_readdp(dp + 1); + op_io(); + if(regs.p.m) { op_lsr_b(); } + else { op_lsr_w(); + op_writedp(dp + 1, rd.h); } + last_cycle(); + op_writedp(dp, rd.l); +} break; + +//rol_dp +case 0x26: { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp); + if(!regs.p.m) rd.h = op_readdp(dp + 1); + op_io(); + if(regs.p.m) { op_rol_b(); } + else { op_rol_w(); + op_writedp(dp + 1, rd.h); } + last_cycle(); + op_writedp(dp, rd.l); +} break; + +//ror_dp +case 0x66: { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp); + if(!regs.p.m) rd.h = op_readdp(dp + 1); + op_io(); + if(regs.p.m) { op_ror_b(); } + else { op_ror_w(); + op_writedp(dp + 1, rd.h); } + last_cycle(); + op_writedp(dp, rd.l); +} break; + +//trb_dp +case 0x14: { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp); + if(!regs.p.m) rd.h = op_readdp(dp + 1); + op_io(); + if(regs.p.m) { op_trb_b(); } + else { op_trb_w(); + op_writedp(dp + 1, rd.h); } + last_cycle(); + op_writedp(dp, rd.l); +} break; + +//tsb_dp +case 0x04: { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp); + if(!regs.p.m) rd.h = op_readdp(dp + 1); + op_io(); + if(regs.p.m) { op_tsb_b(); } + else { op_tsb_w(); + op_writedp(dp + 1, rd.h); } + last_cycle(); + op_writedp(dp, rd.l); +} break; + +//inc_dpx +case 0xf6: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + rd.l = op_readdp(dp + regs.x.w); + if(!regs.p.m) rd.h = op_readdp(dp + regs.x.w + 1); + op_io(); + if(regs.p.m) { op_inc_b(); } + else { op_inc_w(); + op_writedp(dp + regs.x.w + 1, rd.h); } + last_cycle(); + op_writedp(dp + regs.x.w, rd.l); +} break; + +//dec_dpx +case 0xd6: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + rd.l = op_readdp(dp + regs.x.w); + if(!regs.p.m) rd.h = op_readdp(dp + regs.x.w + 1); + op_io(); + if(regs.p.m) { op_dec_b(); } + else { op_dec_w(); + op_writedp(dp + regs.x.w + 1, rd.h); } + last_cycle(); + op_writedp(dp + regs.x.w, rd.l); +} break; + +//asl_dpx +case 0x16: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + rd.l = op_readdp(dp + regs.x.w); + if(!regs.p.m) rd.h = op_readdp(dp + regs.x.w + 1); + op_io(); + if(regs.p.m) { op_asl_b(); } + else { op_asl_w(); + op_writedp(dp + regs.x.w + 1, rd.h); } + last_cycle(); + op_writedp(dp + regs.x.w, rd.l); +} break; + +//lsr_dpx +case 0x56: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + rd.l = op_readdp(dp + regs.x.w); + if(!regs.p.m) rd.h = op_readdp(dp + regs.x.w + 1); + op_io(); + if(regs.p.m) { op_lsr_b(); } + else { op_lsr_w(); + op_writedp(dp + regs.x.w + 1, rd.h); } + last_cycle(); + op_writedp(dp + regs.x.w, rd.l); +} break; + +//rol_dpx +case 0x36: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + rd.l = op_readdp(dp + regs.x.w); + if(!regs.p.m) rd.h = op_readdp(dp + regs.x.w + 1); + op_io(); + if(regs.p.m) { op_rol_b(); } + else { op_rol_w(); + op_writedp(dp + regs.x.w + 1, rd.h); } + last_cycle(); + op_writedp(dp + regs.x.w, rd.l); +} break; + +//ror_dpx +case 0x76: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + rd.l = op_readdp(dp + regs.x.w); + if(!regs.p.m) rd.h = op_readdp(dp + regs.x.w + 1); + op_io(); + if(regs.p.m) { op_ror_b(); } + else { op_ror_w(); + op_writedp(dp + regs.x.w + 1, rd.h); } + last_cycle(); + op_writedp(dp + regs.x.w, rd.l); +} break; + +#endif diff --git a/bsnes/cpu/scpu/core/op_write.b b/bsnes/cpu/scpu/core/op_write.b new file mode 100755 index 0000000..bd50189 --- /dev/null +++ b/bsnes/cpu/scpu/core/op_write.b @@ -0,0 +1,181 @@ +sta_addr(0x8d, regs.p.m, regs.a.w), +stx_addr(0x8e, regs.p.x, regs.x.w), +sty_addr(0x8c, regs.p.x, regs.y.w), +stz_addr(0x9c, regs.p.m, 0x0000) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:if($1) last_cycle(); + op_writedbr(aa.w, $2); + if($1) end; +4:last_cycle(); + op_writedbr(aa.w + 1, $2 >> 8); +} + +sta_addrx(0x9d, regs.p.m, regs.a.w), +stz_addrx(0x9e, regs.p.m, 0x0000) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:op_io(); +4:if($1) last_cycle(); + op_writedbr(aa.w + regs.x.w, $2); + if($1) end; +5:last_cycle(); + op_writedbr(aa.w + regs.x.w + 1, $2 >> 8); +} + +sta_addry(0x99) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:op_io(); +4:if(regs.p.m) last_cycle(); + op_writedbr(aa.w + regs.y.w, regs.a.l); + if(regs.p.m) end; +5:last_cycle(); + op_writedbr(aa.w + regs.y.w + 1, regs.a.h); +} + +sta_long(0x8f) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:aa.b = op_readpc(); +4:if(regs.p.m) last_cycle(); + op_writelong(aa.d, regs.a.l); + if(regs.p.m) end; +5:last_cycle(); + op_writelong(aa.d + 1, regs.a.h); +} + +sta_longx(0x9f) { +1:aa.l = op_readpc(); +2:aa.h = op_readpc(); +3:aa.b = op_readpc(); +4:if(regs.p.m) last_cycle(); + op_writelong(aa.d + regs.x.w, regs.a.l); + if(regs.p.m) end; +5:last_cycle(); + op_writelong(aa.d + regs.x.w + 1, regs.a.h); +} + +sta_dp(0x85, regs.p.m, regs.a.w), +stx_dp(0x86, regs.p.x, regs.x.w), +sty_dp(0x84, regs.p.x, regs.y.w), +stz_dp(0x64, regs.p.m, 0x0000) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:if($1) last_cycle(); + op_writedp(dp, $2); + if($1) end; +4:last_cycle(); + op_writedp(dp + 1, $2 >> 8); +} + +sta_dpx(0x95, regs.p.m, regs.a.w), +sty_dpx(0x94, regs.p.x, regs.y.w), +stz_dpx(0x74, regs.p.m, 0x0000) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:op_io(); +4:if($1) last_cycle(); + op_writedp(dp + regs.x.w, $2); + if($1) end; +5:last_cycle(); + op_writedp(dp + regs.x.w + 1, $2 >> 8); +} + +stx_dpy(0x96) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:op_io(); +4:if(regs.p.x) last_cycle(); + op_writedp(dp + regs.y.w, regs.x.l); + if(regs.p.x) end; +5:last_cycle(); + op_writedp(dp + regs.y.w + 1, regs.x.h); +} + +sta_idp(0x92) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:aa.l = op_readdp(dp); +4:aa.h = op_readdp(dp + 1); +5:if(regs.p.m) last_cycle(); + op_writedbr(aa.w, regs.a.l); + if(regs.p.m) end; +6:last_cycle(); + op_writedbr(aa.w + 1, regs.a.h); +} + +sta_ildp(0x87) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:aa.l = op_readdp(dp); +4:aa.h = op_readdp(dp + 1); +5:aa.b = op_readdp(dp + 2); +6:if(regs.p.m) last_cycle(); + op_writelong(aa.d, regs.a.l); + if(regs.p.m) end; +7:last_cycle(); + op_writelong(aa.d + 1, regs.a.h); +} + +sta_idpx(0x81) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:op_io(); +4:aa.l = op_readdp(dp + regs.x.w); +5:aa.h = op_readdp(dp + regs.x.w + 1); +6:if(regs.p.m) last_cycle(); + op_writedbr(aa.w, regs.a.l); + if(regs.p.m) end; +7:last_cycle(); + op_writedbr(aa.w + 1, regs.a.h); +} + +sta_idpy(0x91) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:aa.l = op_readdp(dp); +4:aa.h = op_readdp(dp + 1); +5:op_io(); +6:if(regs.p.m) last_cycle(); + op_writedbr(aa.w + regs.y.w, regs.a.l); + if(regs.p.m) end; +7:last_cycle(); + op_writedbr(aa.w + regs.y.w + 1, regs.a.h); +} + +sta_ildpy(0x97) { +1:dp = op_readpc(); +2:op_io_cond2(); +3:aa.l = op_readdp(dp); +4:aa.h = op_readdp(dp + 1); +5:aa.b = op_readdp(dp + 2); +6:if(regs.p.m) last_cycle(); + op_writelong(aa.d + regs.y.w, regs.a.l); + if(regs.p.m) end; +7:last_cycle(); + op_writelong(aa.d + regs.y.w + 1, regs.a.h); +} + +sta_sr(0x83) { +1:sp = op_readpc(); +2:op_io(); +3:if(regs.p.m) last_cycle(); + op_writesp(sp, regs.a.l); + if(regs.p.m) end; +4:last_cycle(); + op_writesp(sp + 1, regs.a.h); +} + +sta_isry(0x93) { +1:sp = op_readpc(); +2:op_io(); +3:aa.l = op_readsp(sp); +4:aa.h = op_readsp(sp + 1); +5:op_io(); +6:if(regs.p.m) last_cycle(); + op_writedbr(aa.w + regs.y.w, regs.a.l); + if(regs.p.m) end; +7:last_cycle(); + op_writedbr(aa.w + regs.y.w + 1, regs.a.h); +} diff --git a/bsnes/cpu/scpu/core/op_write.cpp b/bsnes/cpu/scpu/core/op_write.cpp new file mode 100755 index 0000000..d2362e6 --- /dev/null +++ b/bsnes/cpu/scpu/core/op_write.cpp @@ -0,0 +1,293 @@ +#ifdef SCPU_CPP + +//sta_addr +case 0x8d: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.m) last_cycle(); + op_writedbr(aa.w, regs.a.w); + if(regs.p.m) break; + last_cycle(); + op_writedbr(aa.w + 1, regs.a.w >> 8); +} break; + +//stx_addr +case 0x8e: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.x) last_cycle(); + op_writedbr(aa.w, regs.x.w); + if(regs.p.x) break; + last_cycle(); + op_writedbr(aa.w + 1, regs.x.w >> 8); +} break; + +//sty_addr +case 0x8c: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.x) last_cycle(); + op_writedbr(aa.w, regs.y.w); + if(regs.p.x) break; + last_cycle(); + op_writedbr(aa.w + 1, regs.y.w >> 8); +} break; + +//stz_addr +case 0x9c: { + aa.l = op_readpc(); + aa.h = op_readpc(); + if(regs.p.m) last_cycle(); + op_writedbr(aa.w, 0x0000); + if(regs.p.m) break; + last_cycle(); + op_writedbr(aa.w + 1, 0x0000 >> 8); +} break; + +//sta_addrx +case 0x9d: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + if(regs.p.m) last_cycle(); + op_writedbr(aa.w + regs.x.w, regs.a.w); + if(regs.p.m) break; + last_cycle(); + op_writedbr(aa.w + regs.x.w + 1, regs.a.w >> 8); +} break; + +//stz_addrx +case 0x9e: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + if(regs.p.m) last_cycle(); + op_writedbr(aa.w + regs.x.w, 0x0000); + if(regs.p.m) break; + last_cycle(); + op_writedbr(aa.w + regs.x.w + 1, 0x0000 >> 8); +} break; + +//sta_addry +case 0x99: { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + if(regs.p.m) last_cycle(); + op_writedbr(aa.w + regs.y.w, regs.a.l); + if(regs.p.m) break; + last_cycle(); + op_writedbr(aa.w + regs.y.w + 1, regs.a.h); +} break; + +//sta_long +case 0x8f: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + op_writelong(aa.d, regs.a.l); + if(regs.p.m) break; + last_cycle(); + op_writelong(aa.d + 1, regs.a.h); +} break; + +//sta_longx +case 0x9f: { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + if(regs.p.m) last_cycle(); + op_writelong(aa.d + regs.x.w, regs.a.l); + if(regs.p.m) break; + last_cycle(); + op_writelong(aa.d + regs.x.w + 1, regs.a.h); +} break; + +//sta_dp +case 0x85: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.m) last_cycle(); + op_writedp(dp, regs.a.w); + if(regs.p.m) break; + last_cycle(); + op_writedp(dp + 1, regs.a.w >> 8); +} break; + +//stx_dp +case 0x86: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.x) last_cycle(); + op_writedp(dp, regs.x.w); + if(regs.p.x) break; + last_cycle(); + op_writedp(dp + 1, regs.x.w >> 8); +} break; + +//sty_dp +case 0x84: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.x) last_cycle(); + op_writedp(dp, regs.y.w); + if(regs.p.x) break; + last_cycle(); + op_writedp(dp + 1, regs.y.w >> 8); +} break; + +//stz_dp +case 0x64: { + dp = op_readpc(); + op_io_cond2(); + if(regs.p.m) last_cycle(); + op_writedp(dp, 0x0000); + if(regs.p.m) break; + last_cycle(); + op_writedp(dp + 1, 0x0000 >> 8); +} break; + +//sta_dpx +case 0x95: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.m) last_cycle(); + op_writedp(dp + regs.x.w, regs.a.w); + if(regs.p.m) break; + last_cycle(); + op_writedp(dp + regs.x.w + 1, regs.a.w >> 8); +} break; + +//sty_dpx +case 0x94: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.x) last_cycle(); + op_writedp(dp + regs.x.w, regs.y.w); + if(regs.p.x) break; + last_cycle(); + op_writedp(dp + regs.x.w + 1, regs.y.w >> 8); +} break; + +//stz_dpx +case 0x74: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.m) last_cycle(); + op_writedp(dp + regs.x.w, 0x0000); + if(regs.p.m) break; + last_cycle(); + op_writedp(dp + regs.x.w + 1, 0x0000 >> 8); +} break; + +//stx_dpy +case 0x96: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + if(regs.p.x) last_cycle(); + op_writedp(dp + regs.y.w, regs.x.l); + if(regs.p.x) break; + last_cycle(); + op_writedp(dp + regs.y.w + 1, regs.x.h); +} break; + +//sta_idp +case 0x92: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + if(regs.p.m) last_cycle(); + op_writedbr(aa.w, regs.a.l); + if(regs.p.m) break; + last_cycle(); + op_writedbr(aa.w + 1, regs.a.h); +} break; + +//sta_ildp +case 0x87: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + op_writelong(aa.d, regs.a.l); + if(regs.p.m) break; + last_cycle(); + op_writelong(aa.d + 1, regs.a.h); +} break; + +//sta_idpx +case 0x81: { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w); + aa.h = op_readdp(dp + regs.x.w + 1); + if(regs.p.m) last_cycle(); + op_writedbr(aa.w, regs.a.l); + if(regs.p.m) break; + last_cycle(); + op_writedbr(aa.w + 1, regs.a.h); +} break; + +//sta_idpy +case 0x91: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + op_io(); + if(regs.p.m) last_cycle(); + op_writedbr(aa.w + regs.y.w, regs.a.l); + if(regs.p.m) break; + last_cycle(); + op_writedbr(aa.w + regs.y.w + 1, regs.a.h); +} break; + +//sta_ildpy +case 0x97: { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + if(regs.p.m) last_cycle(); + op_writelong(aa.d + regs.y.w, regs.a.l); + if(regs.p.m) break; + last_cycle(); + op_writelong(aa.d + regs.y.w + 1, regs.a.h); +} break; + +//sta_sr +case 0x83: { + sp = op_readpc(); + op_io(); + if(regs.p.m) last_cycle(); + op_writesp(sp, regs.a.l); + if(regs.p.m) break; + last_cycle(); + op_writesp(sp + 1, regs.a.h); +} break; + +//sta_isry +case 0x93: { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp); + aa.h = op_readsp(sp + 1); + op_io(); + if(regs.p.m) last_cycle(); + op_writedbr(aa.w + regs.y.w, regs.a.l); + if(regs.p.m) break; + last_cycle(); + op_writedbr(aa.w + regs.y.w + 1, regs.a.h); +} break; + +#endif diff --git a/bsnes/cpu/scpu/core/opfn.cpp b/bsnes/cpu/scpu/core/opfn.cpp new file mode 100755 index 0000000..72bb757 --- /dev/null +++ b/bsnes/cpu/scpu/core/opfn.cpp @@ -0,0 +1,371 @@ +#ifdef SCPU_CPP + +//op_read +inline void sCPU::op_adc_b() { + int r; + if(regs.p.d) { + uint8 n0 = (regs.a.l ) & 15; + uint8 n1 = (regs.a.l >> 4) & 15; + n0 += (rd.l & 15) + regs.p.c; + if(n0 > 9) { + n0 = (n0 - 10) & 15; + n1++; + } + n1 += ((rd.l >> 4) & 15); + if(n1 > 9) { + n1 = (n1 - 10) & 15; + regs.p.c = 1; + } else { + regs.p.c = 0; + } + r = (n1 << 4) | n0; + } else { + r = regs.a.l + rd.l + regs.p.c; + regs.p.c = r > 0xff; + } + regs.p.n = r & 0x80; + regs.p.v = ~(regs.a.l ^ rd.l) & (regs.a.l ^ r) & 0x80; + regs.p.z = (uint8)r == 0; + regs.a.l = r; +} + +inline void sCPU::op_adc_w() { + int r; + if(regs.p.d) { + uint8 n0 = (regs.a.w ) & 15; + uint8 n1 = (regs.a.w >> 4) & 15; + uint8 n2 = (regs.a.w >> 8) & 15; + uint8 n3 = (regs.a.w >> 12) & 15; + n0 += (rd.w & 15) + regs.p.c; + if(n0 > 9) { + n0 = (n0 - 10) & 15; + n1++; + } + n1 += ((rd.w >> 4) & 15); + if(n1 > 9) { + n1 = (n1 - 10) & 15; + n2++; + } + n2 += ((rd.w >> 8) & 15); + if(n2 > 9) { + n2 = (n2 - 10) & 15; + n3++; + } + n3 += ((rd.w >> 12) & 15); + if(n3 > 9) { + n3 = (n3 - 10) & 15; + regs.p.c = 1; + } else { + regs.p.c = 0; + } + r = (n3 << 12) | (n2 << 8) | (n1 << 4) | n0; + } else { + r = regs.a.w + rd.w + regs.p.c; + regs.p.c = r > 0xffff; + } + regs.p.n = r & 0x8000; + regs.p.v = ~(regs.a.w ^ rd.w) & (regs.a.w ^ r) & 0x8000; + regs.p.z = (uint16)r == 0; + regs.a.w = r; +} + +inline void sCPU::op_and_b() { + regs.a.l &= rd.l; + regs.p.n = regs.a.l & 0x80; + regs.p.z = regs.a.l == 0; +} + +inline void sCPU::op_and_w() { + regs.a.w &= rd.w; + regs.p.n = regs.a.w & 0x8000; + regs.p.z = regs.a.w == 0; +} + +inline void sCPU::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 sCPU::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 sCPU::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 sCPU::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 sCPU::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 sCPU::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 sCPU::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 sCPU::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 sCPU::op_eor_b() { + regs.a.l ^= rd.l; + regs.p.n = regs.a.l & 0x80; + regs.p.z = regs.a.l == 0; +} + +inline void sCPU::op_eor_w() { + regs.a.w ^= rd.w; + regs.p.n = regs.a.w & 0x8000; + regs.p.z = regs.a.w == 0; +} + +inline void sCPU::op_lda_b() { + regs.a.l = rd.l; + regs.p.n = regs.a.l & 0x80; + regs.p.z = regs.a.l == 0; +} + +inline void sCPU::op_lda_w() { + regs.a.w = rd.w; + regs.p.n = regs.a.w & 0x8000; + regs.p.z = regs.a.w == 0; +} + +inline void sCPU::op_ldx_b() { + regs.x.l = rd.l; + regs.p.n = regs.x.l & 0x80; + regs.p.z = regs.x.l == 0; +} + +inline void sCPU::op_ldx_w() { + regs.x.w = rd.w; + regs.p.n = regs.x.w & 0x8000; + regs.p.z = regs.x.w == 0; +} + +inline void sCPU::op_ldy_b() { + regs.y.l = rd.l; + regs.p.n = regs.y.l & 0x80; + regs.p.z = regs.y.l == 0; +} + +inline void sCPU::op_ldy_w() { + regs.y.w = rd.w; + regs.p.n = regs.y.w & 0x8000; + regs.p.z = regs.y.w == 0; +} + +inline void sCPU::op_ora_b() { + regs.a.l |= rd.l; + regs.p.n = regs.a.l & 0x80; + regs.p.z = regs.a.l == 0; +} + +inline void sCPU::op_ora_w() { + regs.a.w |= rd.w; + regs.p.n = regs.a.w & 0x8000; + regs.p.z = regs.a.w == 0; +} + +inline void sCPU::op_sbc_b() { + int r; + if(regs.p.d) { + uint8 n0 = (regs.a.l ) & 15; + uint8 n1 = (regs.a.l >> 4) & 15; + n0 -= ((rd.l ) & 15) + !regs.p.c; + n1 -= ((rd.l >> 4) & 15); + if(n0 > 9) { + n0 += 10; + n1--; + } + if(n1 > 9) { + n1 += 10; + regs.p.c = 0; + } else { + regs.p.c = 1; + } + r = (n1 << 4) | (n0); + } else { + r = regs.a.l - rd.l - !regs.p.c; + regs.p.c = r >= 0; + } + regs.p.n = r & 0x80; + regs.p.v = (regs.a.l ^ rd.l) & (regs.a.l ^ r) & 0x80; + regs.p.z = (uint8)r == 0; + regs.a.l = r; +} + +inline void sCPU::op_sbc_w() { + int r; + if(regs.p.d) { + uint8 n0 = (regs.a.w ) & 15; + uint8 n1 = (regs.a.w >> 4) & 15; + uint8 n2 = (regs.a.w >> 8) & 15; + uint8 n3 = (regs.a.w >> 12) & 15; + n0 -= ((rd.w ) & 15) + !regs.p.c; + n1 -= ((rd.w >> 4) & 15); + n2 -= ((rd.w >> 8) & 15); + n3 -= ((rd.w >> 12) & 15); + if(n0 > 9) { + n0 += 10; + n1--; + } + if(n1 > 9) { + n1 += 10; + n2--; + } + if(n2 > 9) { + n2 += 10; + n3--; + } + if(n3 > 9) { + n3 += 10; + regs.p.c = 0; + } else { + regs.p.c = 1; + } + r = (n3 << 12) | (n2 << 8) | (n1 << 4) | (n0); + } else { + r = regs.a.w - rd.w - !regs.p.c; + regs.p.c = r >= 0; + } + regs.p.n = r & 0x8000; + regs.p.v = (regs.a.w ^ rd.w) & (regs.a.w ^ r) & 0x8000; + regs.p.z = (uint16)r == 0; + regs.a.w = r; +} + +//op_rmw +inline void sCPU::op_inc_b() { + rd.l++; + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void sCPU::op_inc_w() { + rd.w++; + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void sCPU::op_dec_b() { + rd.l--; + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void sCPU::op_dec_w() { + rd.w--; + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void sCPU::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 sCPU::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 sCPU::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 sCPU::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 sCPU::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 sCPU::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 sCPU::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 sCPU::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 sCPU::op_trb_b() { + regs.p.z = (rd.l & regs.a.l) == 0; + rd.l &= ~regs.a.l; +} + +inline void sCPU::op_trb_w() { + regs.p.z = (rd.w & regs.a.w) == 0; + rd.w &= ~regs.a.w; +} + +inline void sCPU::op_tsb_b() { + regs.p.z = (rd.l & regs.a.l) == 0; + rd.l |= regs.a.l; +} + +inline void sCPU::op_tsb_w() { + regs.p.z = (rd.w & regs.a.w) == 0; + rd.w |= regs.a.w; +} + +#endif //ifdef SCPU_CPP diff --git a/bsnes/cpu/scpu/core/scpugen.cpp b/bsnes/cpu/scpu/core/scpugen.cpp new file mode 100755 index 0000000..a2ac5ac --- /dev/null +++ b/bsnes/cpu/scpu/core/scpugen.cpp @@ -0,0 +1,12 @@ +#define CLASS_NAME "sCPU" +#include + +int main() { + generate("op_read.cpp", "op_read.b"); + generate("op_write.cpp", "op_write.b"); + generate("op_rmw.cpp", "op_rmw.b"); + generate("op_pc.cpp", "op_pc.b"); + generate("op_misc.cpp", "op_misc.b"); + + return 0; +} diff --git a/bsnes/cpu/scpu/dma/dma.cpp b/bsnes/cpu/scpu/dma/dma.cpp new file mode 100755 index 0000000..de73d56 --- /dev/null +++ b/bsnes/cpu/scpu/dma/dma.cpp @@ -0,0 +1,271 @@ +#ifdef SCPU_CPP + +void sCPU::dma_add_clocks(unsigned clocks) { + status.dma_clocks += clocks; + add_clocks(clocks); +} + +bool sCPU::dma_addr_valid(uint32 abus) { + //reads from B-bus or S-CPU registers are invalid + if((abus & 0x40ff00) == 0x2100) return false; //$[00-3f|80-bf]:[2100-21ff] + if((abus & 0x40fe00) == 0x4000) return false; //$[00-3f|80-bf]:[4000-41ff] + if((abus & 0x40ffe0) == 0x4200) return false; //$[00-3f|80-bf]:[4200-421f] + if((abus & 0x40ff80) == 0x4300) return false; //$[00-3f|80-bf]:[4300-437f] + return true; +} + +uint8 sCPU::dma_read(uint32 abus) { + if(dma_addr_valid(abus) == false) return 0x00; //does not return S-CPU MDR + return bus.read(abus); +} + +void sCPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) { + if(direction == 0) { + //a->b transfer (to $21xx) + if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) { + //illegal WRAM->WRAM transfer (bus conflict) + //read most likely occurs; no write occurs + //read is irrelevent, as it cannot be observed by software + dma_add_clocks(8); + } else { + dma_add_clocks(4); + uint8 data = dma_read(abus); + dma_add_clocks(4); + bus.write(0x2100 | bbus, data); + } + } else { + //b->a transfer (from $21xx) + if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) { + //illegal WRAM->WRAM transfer (bus conflict) + //no read occurs; write does occur + dma_add_clocks(8); + bus.write(abus, 0x00); //does not write S-CPU MDR + } else { + dma_add_clocks(4); + uint8 data = bus.read(0x2100 | bbus); + dma_add_clocks(4); + if(dma_addr_valid(abus) == true) { + bus.write(abus, data); + } + } + } + + cycle_edge(); +} + +/***** + * address calculation functions + *****/ + +uint8 sCPU::dma_bbus(uint8 i, uint8 index) { + switch(channel[i].xfermode) { default: + case 0: return (channel[i].destaddr); //0 + case 1: return (channel[i].destaddr + (index & 1)); //0,1 + case 2: return (channel[i].destaddr); //0,0 + case 3: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 + case 4: return (channel[i].destaddr + (index & 3)); //0,1,2,3 + case 5: return (channel[i].destaddr + (index & 1)); //0,1,0,1 + case 6: return (channel[i].destaddr); //0,0 [2] + case 7: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 [3] + } +} + +inline uint32 sCPU::dma_addr(uint8 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 sCPU::hdma_addr(uint8 i) { + return (channel[i].srcbank << 16) | (channel[i].hdma_addr++); +} + +inline uint32 sCPU::hdma_iaddr(uint8 i) { + return (channel[i].hdma_ibank << 16) | (channel[i].hdma_iaddr++); +} + +/***** + * DMA functions + *****/ + +uint8 sCPU::dma_enabled_channels() { + uint8 r = 0; + for(unsigned i = 0; i < 8; i++) { + if(channel[i].dma_enabled) r++; + } + return r; +} + +void sCPU::dma_run() { + dma_add_clocks(8); + cycle_edge(); + + for(unsigned i = 0; i < 8; i++) { + if(channel[i].dma_enabled == false) continue; + dma_add_clocks(8); + cycle_edge(); + + unsigned index = 0; + do { + dma_transfer(channel[i].direction, dma_bbus(i, index++), dma_addr(i)); + } while(channel[i].dma_enabled && --channel[i].xfersize); + + channel[i].dma_enabled = false; + } + + status.irq_lock = true; + event.enqueue(2, EventIrqLockRelease); +} + +/***** + * HDMA functions + *****/ + +inline bool sCPU::hdma_active(uint8 i) { + return (channel[i].hdma_enabled && !channel[i].hdma_completed); +} + +inline bool sCPU::hdma_active_after(uint8 i) { + for(unsigned n = i + 1; n < 8; n++) { + if(hdma_active(n) == true) return true; + } + return false; +} + +inline uint8 sCPU::hdma_enabled_channels() { + uint8 r = 0; + for(unsigned i = 0; i < 8; i++) { + if(channel[i].hdma_enabled) r++; + } + return r; +} + +inline uint8 sCPU::hdma_active_channels() { + uint8 r = 0; + for(unsigned i = 0; i < 8; i++) { + if(hdma_active(i) == true) r++; + } + return r; +} + +void sCPU::hdma_update(uint8 i) { + channel[i].hdma_line_counter = dma_read(hdma_addr(i)); + dma_add_clocks(8); + + channel[i].hdma_completed = (channel[i].hdma_line_counter == 0); + channel[i].hdma_do_transfer = !channel[i].hdma_completed; + + if(channel[i].hdma_indirect) { + channel[i].hdma_iaddr = dma_read(hdma_addr(i)) << 8; + dma_add_clocks(8); + + if(!channel[i].hdma_completed || hdma_active_after(i)) { + channel[i].hdma_iaddr >>= 8; + channel[i].hdma_iaddr |= dma_read(hdma_addr(i)) << 8; + dma_add_clocks(8); + } + } +} + +void sCPU::hdma_run() { + dma_add_clocks(8); + + 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 = bool(channel[i].hdma_line_counter & 0x80); + if((channel[i].hdma_line_counter & 0x7f) == 0) { + hdma_update(i); + } else { + dma_add_clocks(8); + } + } + + status.irq_lock = true; + event.enqueue(2, EventIrqLockRelease); +} + +void sCPU::hdma_init_reset() { + for(unsigned i = 0; i < 8; i++) { + channel[i].hdma_completed = false; + channel[i].hdma_do_transfer = false; + } +} + +void sCPU::hdma_init() { + dma_add_clocks(8); + + 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; + hdma_update(i); + } + + status.irq_lock = true; + event.enqueue(2, EventIrqLockRelease); +} + +/***** + * power / reset functions + *****/ + +void sCPU::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].xfersize = 0xffff; + //channel[i].hdma_iaddr = 0xffff; //union with xfersize + channel[i].hdma_ibank = 0xff; + + channel[i].hdma_addr = 0xffff; + channel[i].hdma_line_counter = 0xff; + channel[i].unknown = 0xff; + } +} + +void sCPU::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; + } +} + +#endif //ifdef SCPU_CPP diff --git a/bsnes/cpu/scpu/dma/dma.hpp b/bsnes/cpu/scpu/dma/dma.hpp new file mode 100755 index 0000000..79102f0 --- /dev/null +++ b/bsnes/cpu/scpu/dma/dma.hpp @@ -0,0 +1,71 @@ + 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 variables + bool hdma_completed; + bool hdma_do_transfer; + } channel[8]; + + void dma_add_clocks(unsigned clocks); + bool dma_addr_valid(uint32 abus); + uint8 dma_read(uint32 abus); + void dma_transfer(bool direction, uint8 bbus, uint32 abus); + + uint8 dma_bbus(uint8 i, uint8 index); + uint32 dma_addr(uint8 i); + uint32 hdma_addr(uint8 i); + uint32 hdma_iaddr(uint8 i); + + uint8 dma_enabled_channels(); + void dma_run(); + + bool hdma_active(uint8 i); + bool hdma_active_after(uint8 i); + uint8 hdma_enabled_channels(); + uint8 hdma_active_channels(); + void hdma_update(uint8 i); + void hdma_run(); + void hdma_init_reset(); + void hdma_init(); + + void dma_power(); + void dma_reset(); diff --git a/bsnes/cpu/scpu/memory/memory.cpp b/bsnes/cpu/scpu/memory/memory.cpp new file mode 100755 index 0000000..16faf77 --- /dev/null +++ b/bsnes/cpu/scpu/memory/memory.cpp @@ -0,0 +1,125 @@ +#ifdef SCPU_CPP + +/***** + * These 3 functions control bus timing for the CPU. + * cpu_io is an I/O cycle, and always 6 clock cycles long. + * mem_read / mem_write indicate memory access bus cycles. + * they are either 6, 8, or 12 bus cycles long, depending + * both on location and the $420d.d0 FastROM enable bit. + *****/ + +void sCPU::op_io() { + status.clock_count = 6; + precycle_edge(); + add_clocks(6); + cycle_edge(); +} + +uint8 sCPU::op_read(uint32 addr) { + status.clock_count = bus.speed(addr); + precycle_edge(); + add_clocks(status.clock_count - 4); + regs.mdr = bus.read(addr); + add_clocks(4); + cycle_edge(); + return regs.mdr; +} + +void sCPU::op_write(uint32 addr, uint8 data) { + status.clock_count = bus.speed(addr); + precycle_edge(); + add_clocks(status.clock_count); + regs.mdr = data; + bus.write(addr, regs.mdr); + cycle_edge(); +} + +// + +alwaysinline uint8 sCPU::op_readpc() { + return op_read((regs.pc.b << 16) + regs.pc.w++); +} + +alwaysinline uint8 sCPU::op_readstack() { + if(regs.e) { + regs.s.l++; + } else { + regs.s.w++; + } + return op_read(regs.s.w); +} + +alwaysinline uint8 sCPU::op_readstackn() { + return op_read(++regs.s.w); +} + +alwaysinline uint8 sCPU::op_readaddr(uint32 addr) { + return op_read(addr & 0xffff); +} + +alwaysinline uint8 sCPU::op_readlong(uint32 addr) { + return op_read(addr & 0xffffff); +} + +alwaysinline uint8 sCPU::op_readdbr(uint32 addr) { + return op_read(((regs.db << 16) + addr) & 0xffffff); +} + +alwaysinline uint8 sCPU::op_readpbr(uint32 addr) { + return op_read((regs.pc.b << 16) + (addr & 0xffff)); +} + +alwaysinline uint8 sCPU::op_readdp(uint32 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 sCPU::op_readsp(uint32 addr) { + return op_read((regs.s + (addr & 0xffff)) & 0xffff); +} + +alwaysinline void sCPU::op_writestack(uint8 data) { + op_write(regs.s.w, data); + if(regs.e) { + regs.s.l--; + } else { + regs.s.w--; + } +} + +alwaysinline void sCPU::op_writestackn(uint8 data) { + op_write(regs.s.w--, data); +} + +alwaysinline void sCPU::op_writeaddr(uint32 addr, uint8 data) { + op_write(addr & 0xffff, data); +} + +alwaysinline void sCPU::op_writelong(uint32 addr, uint8 data) { + op_write(addr & 0xffffff, data); +} + +alwaysinline void sCPU::op_writedbr(uint32 addr, uint8 data) { + op_write(((regs.db << 16) + addr) & 0xffffff, data); +} + +alwaysinline void sCPU::op_writepbr(uint32 addr, uint8 data) { + op_write((regs.pc.b << 16) + (addr & 0xffff), data); +} + +alwaysinline void sCPU::op_writedp(uint32 addr, uint8 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 sCPU::op_writesp(uint32 addr, uint8 data) { + op_write((regs.s + (addr & 0xffff)) & 0xffff, data); +} + +#endif //ifdef SCPU_CPP diff --git a/bsnes/cpu/scpu/memory/memory.hpp b/bsnes/cpu/scpu/memory/memory.hpp new file mode 100755 index 0000000..50c3b21 --- /dev/null +++ b/bsnes/cpu/scpu/memory/memory.hpp @@ -0,0 +1,35 @@ + /***** + * 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(); + uint8 op_read(uint32 addr); + void op_write(uint32 addr, uint8 data); + + /***** + * helper memory addressing functions used by CPU core + *****/ + uint8 op_readpc (); + uint8 op_readstack (); + uint8 op_readstackn(); + uint8 op_readaddr (uint32 addr); + uint8 op_readlong (uint32 addr); + uint8 op_readdbr (uint32 addr); + uint8 op_readpbr (uint32 addr); + uint8 op_readdp (uint32 addr); + uint8 op_readsp (uint32 addr); + + void op_writestack (uint8 data); + void op_writestackn(uint8 data); + void op_writeaddr (uint32 addr, uint8 data); + void op_writelong (uint32 addr, uint8 data); + void op_writedbr (uint32 addr, uint8 data); + void op_writepbr (uint32 addr, uint8 data); + void op_writedp (uint32 addr, uint8 data); + void op_writesp (uint32 addr, uint8 data); diff --git a/bsnes/cpu/scpu/mmio/mmio.cpp b/bsnes/cpu/scpu/mmio/mmio.cpp new file mode 100755 index 0000000..7441cb2 --- /dev/null +++ b/bsnes/cpu/scpu/mmio/mmio.cpp @@ -0,0 +1,534 @@ +#ifdef SCPU_CPP + +uint8 sCPU::pio() { return status.pio; } +bool sCPU::joylatch() { return status.joypad_strobe_latch; } + +//WMDATA +uint8 sCPU::mmio_r2180() { + uint8 r = bus.read(0x7e0000 | status.wram_addr); + status.wram_addr = (status.wram_addr + 1) & 0x01ffff; + return r; +} + +//WMDATA +void sCPU::mmio_w2180(uint8 data) { + bus.write(0x7e0000 | status.wram_addr, data); + status.wram_addr = (status.wram_addr + 1) & 0x01ffff; +} + +//WMADDL +void sCPU::mmio_w2181(uint8 data) { + status.wram_addr = (status.wram_addr & 0xffff00) | (data); + status.wram_addr &= 0x01ffff; +} + +//WMADDM +void sCPU::mmio_w2182(uint8 data) { + status.wram_addr = (status.wram_addr & 0xff00ff) | (data << 8); + status.wram_addr &= 0x01ffff; +} + +//WMADDH +void sCPU::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 sCPU::mmio_w4016(uint8 data) { + status.joypad_strobe_latch = !!(data & 1); + + if(status.joypad_strobe_latch == 1) { + snes.input.poll(); + } +} + +//JOYSER0 +//7-2 = MDR +//1-0 = Joypad serial data +// +//TODO: test whether strobe latch of zero returns +//realtime or buffered status of joypadN.b +uint8 sCPU::mmio_r4016() { + uint8 r = regs.mdr & 0xfc; + r |= snes.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 sCPU::mmio_r4017() { + uint8 r = (regs.mdr & 0xe0) | 0x1c; + r |= snes.input.port_read(1) & 3; + return r; +} + +//NMITIMEN +void sCPU::mmio_w4200(uint8 data) { + status.auto_joypad_poll = !!(data & 0x01); + nmitimen_update(data); +} + +//WRIO +void sCPU::mmio_w4201(uint8 data) { + if((status.pio & 0x80) && !(data & 0x80)) { + ppu.latch_counters(); + } + status.pio = data; +} + +//WRMPYA +void sCPU::mmio_w4202(uint8 data) { + status.mul_a = data; +} + +//WRMPYB +void sCPU::mmio_w4203(uint8 data) { + status.mul_b = data; + status.r4216 = status.mul_a * status.mul_b; + + status.alu_lock = true; + event.enqueue(snes.config.cpu.alu_mul_delay, EventAluLockRelease); +} + +//WRDIVL +void sCPU::mmio_w4204(uint8 data) { + status.div_a = (status.div_a & 0xff00) | (data); +} + +//WRDIVH +void sCPU::mmio_w4205(uint8 data) { + status.div_a = (status.div_a & 0x00ff) | (data << 8); +} + +//WRDIVB +void sCPU::mmio_w4206(uint8 data) { + status.div_b = data; + status.r4214 = (status.div_b) ? status.div_a / status.div_b : 0xffff; + status.r4216 = (status.div_b) ? status.div_a % status.div_b : status.div_a; + + status.alu_lock = true; + event.enqueue(snes.config.cpu.alu_div_delay, EventAluLockRelease); +} + +//HTIMEL +void sCPU::mmio_w4207(uint8 data) { + status.hirq_pos = (status.hirq_pos & ~0xff) | (data); + status.hirq_pos &= 0x01ff; +} + +//HTIMEH +void sCPU::mmio_w4208(uint8 data) { + status.hirq_pos = (status.hirq_pos & 0xff) | (data << 8); + status.hirq_pos &= 0x01ff; +} + +//VTIMEL +void sCPU::mmio_w4209(uint8 data) { + status.virq_pos = (status.virq_pos & ~0xff) | (data); + status.virq_pos &= 0x01ff; +} + +//VTIMEH +void sCPU::mmio_w420a(uint8 data) { + status.virq_pos = (status.virq_pos & 0xff) | (data << 8); + status.virq_pos &= 0x01ff; +} + +//DMAEN +void sCPU::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 sCPU::mmio_w420c(uint8 data) { + for(unsigned i = 0; i < 8; i++) { + channel[i].hdma_enabled = data & (1 << i); + } +} + +//MEMSEL +void sCPU::mmio_w420d(uint8 data) { + bus.set_speed(data & 1); +} + +//RDNMI +//7 = NMI acknowledge +//6-4 = MDR +//3-0 = CPU (5a22) version +uint8 sCPU::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 sCPU::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 sCPU::mmio_r4212() { + uint8 r = (regs.mdr & 0x3e); + uint16 vs = ppu.overscan() == false ? 225 : 240; + + //auto joypad polling + if(ppu.vcounter() >= vs && ppu.vcounter() <= (vs + 2))r |= 0x01; + + //hblank + if(ppu.hcounter() <= 2 || ppu.hcounter() >= 1096)r |= 0x40; + + //vblank + if(ppu.vcounter() >= vs)r |= 0x80; + + return r; +} + +//RDIO +uint8 sCPU::mmio_r4213() { + return status.pio; +} + +//RDDIVL +uint8 sCPU::mmio_r4214() { + if(status.alu_lock) return 0; + return status.r4214; +} + +//RDDIVH +uint8 sCPU::mmio_r4215() { + if(status.alu_lock) return 0; + return status.r4214 >> 8; +} + +//RDMPYL +uint8 sCPU::mmio_r4216() { + if(status.alu_lock) return 0; + return status.r4216; +} + +//RDMPYH +uint8 sCPU::mmio_r4217() { + if(status.alu_lock) return 0; + return status.r4216 >> 8; +} + +//TODO: handle reads during joypad polling (v=225-227) +uint8 sCPU::mmio_r4218() { return status.joy1l; } //JOY1L +uint8 sCPU::mmio_r4219() { return status.joy1h; } //JOY1H +uint8 sCPU::mmio_r421a() { return status.joy2l; } //JOY2L +uint8 sCPU::mmio_r421b() { return status.joy2h; } //JOY2H +uint8 sCPU::mmio_r421c() { return status.joy3l; } //JOY3L +uint8 sCPU::mmio_r421d() { return status.joy3h; } //JOY3H +uint8 sCPU::mmio_r421e() { return status.joy4l; } //JOY4L +uint8 sCPU::mmio_r421f() { return status.joy4h; } //JOY4H + +//DMAPx +uint8 sCPU::mmio_r43x0(uint8 i) { + return channel[i].dmap; +} + +//BBADx +uint8 sCPU::mmio_r43x1(uint8 i) { + return channel[i].destaddr; +} + +//A1TxL +uint8 sCPU::mmio_r43x2(uint8 i) { + return channel[i].srcaddr; +} + +//A1TxH +uint8 sCPU::mmio_r43x3(uint8 i) { + return channel[i].srcaddr >> 8; +} + +//A1Bx +uint8 sCPU::mmio_r43x4(uint8 i) { + return channel[i].srcbank; +} + +//DASxL +//union { uint16 xfersize; uint16 hdma_iaddr; }; +uint8 sCPU::mmio_r43x5(uint8 i) { + return channel[i].xfersize; +} + +//DASxH +//union { uint16 xfersize; uint16 hdma_iaddr; }; +uint8 sCPU::mmio_r43x6(uint8 i) { + return channel[i].xfersize >> 8; +} + +//DASBx +uint8 sCPU::mmio_r43x7(uint8 i) { + return channel[i].hdma_ibank; +} + +//A2AxL +uint8 sCPU::mmio_r43x8(uint8 i) { + return channel[i].hdma_addr; +} + +//A2AxH +uint8 sCPU::mmio_r43x9(uint8 i) { + return channel[i].hdma_addr >> 8; +} + +//NTRLx +uint8 sCPU::mmio_r43xa(uint8 i) { + return channel[i].hdma_line_counter; +} + +//??? +uint8 sCPU::mmio_r43xb(uint8 i) { + return channel[i].unknown; +} + +//DMAPx +void sCPU::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 sCPU::mmio_w43x1(uint8 i, uint8 data) { + channel[i].destaddr = data; +} + +//A1TxL +void sCPU::mmio_w43x2(uint8 i, uint8 data) { + channel[i].srcaddr = (channel[i].srcaddr & 0xff00) | (data); +} + +//A1TxH +void sCPU::mmio_w43x3(uint8 i, uint8 data) { + channel[i].srcaddr = (channel[i].srcaddr & 0x00ff) | (data << 8); +} + +//A1Bx +void sCPU::mmio_w43x4(uint8 i, uint8 data) { + channel[i].srcbank = data; +} + +//DASxL +//union { uint16 xfersize; uint16 hdma_iaddr; }; +void sCPU::mmio_w43x5(uint8 i, uint8 data) { + channel[i].xfersize = (channel[i].xfersize & 0xff00) | (data); +} + +//DASxH +//union { uint16 xfersize; uint16 hdma_iaddr; }; +void sCPU::mmio_w43x6(uint8 i, uint8 data) { + channel[i].xfersize = (channel[i].xfersize & 0x00ff) | (data << 8); +} + +//DASBx +void sCPU::mmio_w43x7(uint8 i, uint8 data) { + channel[i].hdma_ibank = data; +} + +//A2AxL +void sCPU::mmio_w43x8(uint8 i, uint8 data) { + channel[i].hdma_addr = (channel[i].hdma_addr & 0xff00) | (data); +} + +//A2AxH +void sCPU::mmio_w43x9(uint8 i, uint8 data) { + channel[i].hdma_addr = (channel[i].hdma_addr & 0x00ff) | (data << 8); +} + +//NTRLx +void sCPU::mmio_w43xa(uint8 i, uint8 data) { + channel[i].hdma_line_counter = data; +} + +//??? +void sCPU::mmio_w43xb(uint8 i, uint8 data) { + channel[i].unknown = data; +} + +void sCPU::mmio_power() { +} + +void sCPU::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.mul_a = 0xff; + status.mul_b = 0xff; + + //$4204-$4206 + status.div_a = 0xffff; + status.div_b = 0xff; + + //$4207-$420a + status.hirq_pos = 0x01ff; + status.virq_pos = 0x01ff; + + //$4214-$4217 + status.r4214 = 0x0000; + status.r4216 = 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; +} + +uint8 sCPU::mmio_read(unsigned addr) { + addr &= 0xffff; + + //APU + if((addr & 0xffc0) == 0x2140) { //$2140-$217f + scheduler.sync_cpusmp(); + 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 sCPU::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + //APU + if((addr & 0xffc0) == 0x2140) { //$2140-$217f + scheduler.sync_cpusmp(); + 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 //ifdef SCPU_CPP diff --git a/bsnes/cpu/scpu/mmio/mmio.hpp b/bsnes/cpu/scpu/mmio/mmio.hpp new file mode 100755 index 0000000..839d0a5 --- /dev/null +++ b/bsnes/cpu/scpu/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/bsnes/cpu/scpu/scpu.cpp b/bsnes/cpu/scpu/scpu.cpp new file mode 100755 index 0000000..2927cd5 --- /dev/null +++ b/bsnes/cpu/scpu/scpu.cpp @@ -0,0 +1,61 @@ +#include <../base.hpp> +#define SCPU_CPP + +#include +priority_queue event(512, bind(&sCPU::queue_event, &cpu)); + +#include "core/core.cpp" +#include "dma/dma.cpp" +#include "memory/memory.cpp" +#include "mmio/mmio.cpp" +#include "timing/timing.cpp" + +void sCPU::power() { + CPU::power(); + + regs.a = regs.x = regs.y = 0x0000; + regs.s = 0x01ff; + + mmio_power(); + dma_power(); + timing_power(); + + reset(); +} + +void sCPU::reset() { + CPU::reset(); + + regs.pc.d = 0x000000; + regs.pc.l = bus.read(0xfffc); + regs.pc.h = bus.read(0xfffd); + + //note: some registers are not fully reset by SNES + 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; + + status.wai_lock = false; + status.interrupt_pending = false; + status.interrupt_vector = 0xfffc; //reset vector address + + mmio_reset(); + dma_reset(); + timing_reset(); + + apu_port[0] = 0x00; + apu_port[1] = 0x00; + apu_port[2] = 0x00; + apu_port[3] = 0x00; +} + +sCPU::sCPU() { +} + +sCPU::~sCPU() { +} diff --git a/bsnes/cpu/scpu/scpu.hpp b/bsnes/cpu/scpu/scpu.hpp new file mode 100755 index 0000000..3be415f --- /dev/null +++ b/bsnes/cpu/scpu/scpu.hpp @@ -0,0 +1,94 @@ +class sCPU : public CPU { +public: + void enter(); + + #include "core/core.hpp" + #include "dma/dma.hpp" + #include "memory/memory.hpp" + #include "mmio/mmio.hpp" + #include "timing/timing.hpp" + + enum DmaState { DmaInactive, DmaRun, DmaCpuSync }; + + struct { + //core + uint8 opcode; + bool in_opcode; + + bool wai_lock; + bool interrupt_pending; + uint16 interrupt_vector; + + unsigned clock_count; + unsigned line_clocks; + + //timing + bool irq_lock; + bool alu_lock; + unsigned dram_refresh_position; + + 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; + + //DMA + unsigned dma_counter; + unsigned dma_clocks; + bool dma_pending; + bool hdma_pending; + bool hdma_mode; //0 = init, 1 = run + DmaState dma_state; + + //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 mul_a, mul_b; + + //$4204-$4206 + uint16 div_a; + uint8 div_b; + + //$4207-$420a + uint16 hirq_pos, virq_pos; + + //$4214-$4217 + uint16 r4214; + uint16 r4216; + + //$4218-$421f + uint8 joy1l, joy1h; + uint8 joy2l, joy2h; + uint8 joy3l, joy3h; + uint8 joy4l, joy4h; + } status; + + void power(); + void reset(); + + sCPU(); + ~sCPU(); +}; diff --git a/bsnes/cpu/scpu/timing/event.cpp b/bsnes/cpu/scpu/timing/event.cpp new file mode 100755 index 0000000..82de924 --- /dev/null +++ b/bsnes/cpu/scpu/timing/event.cpp @@ -0,0 +1,35 @@ +#ifdef SCPU_CPP + +void sCPU::queue_event(unsigned id) { + switch(id) { + //interrupts triggered during (H)DMA do not trigger immediately after + case EventIrqLockRelease: { + status.irq_lock = false; + } break; + + //ALU multiplication / division results are not immediately calculated; + //the exact formula for the calculations are unknown, but this lock at least + //allows emulation to avoid returning to fully computed results too soon. + case EventAluLockRelease: { + status.alu_lock = false; + } break; + + //S-CPU WRAM consists of two 64kbyte DRAM chips, which must be refreshed + //once per scanline to avoid memory decay. + case EventDramRefresh: { + add_clocks(40); + } break; + + //HDMA init routine; occurs once per frame + case EventHdmaInit: { + cycle_edge_state |= EventFlagHdmaInit; + } break; + + //HDMA run routine; occurs once per scanline + case EventHdmaRun: { + cycle_edge_state |= EventFlagHdmaRun; + } break; + } +} + +#endif diff --git a/bsnes/cpu/scpu/timing/irq.cpp b/bsnes/cpu/scpu/timing/irq.cpp new file mode 100755 index 0000000..5b4a559 --- /dev/null +++ b/bsnes/cpu/scpu/timing/irq.cpp @@ -0,0 +1,107 @@ +#ifdef SCPU_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 sCPU::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 = (ppu.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 && ppu.vcounter(10) != (status.virq_pos)) + || (status.hirq_enabled && ppu.hcounter(10) != (status.hirq_pos + 1) * 4) + || (status.virq_pos && ppu.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 sCPU::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; + event.enqueue(2, EventIrqLockRelease); +} + +bool sCPU::rdnmi() { + bool result = status.nmi_line; + if(!status.nmi_hold) { + status.nmi_line = false; + } + return result; +} + +bool sCPU::timeup() { + bool result = status.irq_line; + if(!status.irq_hold) { + status.irq_line = false; + status.irq_transition = false; + } + return result; +} + +bool sCPU::nmi_test() { + if(!status.nmi_transition) return false; + status.nmi_transition = false; + status.wai_lock = false; + return true; +} + +bool sCPU::irq_test() { + if(!status.irq_transition) return false; + status.irq_transition = false; + status.wai_lock = false; + return !regs.p.i; +} + +#endif diff --git a/bsnes/cpu/scpu/timing/joypad.cpp b/bsnes/cpu/scpu/timing/joypad.cpp new file mode 100755 index 0000000..c754c4d --- /dev/null +++ b/bsnes/cpu/scpu/timing/joypad.cpp @@ -0,0 +1,28 @@ +#ifdef SCPU_CPP + +void sCPU::run_auto_joypad_poll() { + uint16 joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0; + for(unsigned i = 0; i < 16; i++) { + uint8 port0 = snes.input.port_read(0); + uint8 port1 = snes.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/bsnes/cpu/scpu/timing/timing.cpp b/bsnes/cpu/scpu/timing/timing.cpp new file mode 100755 index 0000000..c43ed5f --- /dev/null +++ b/bsnes/cpu/scpu/timing/timing.cpp @@ -0,0 +1,166 @@ +#ifdef SCPU_CPP + +#include "event.cpp" +#include "irq.cpp" +#include "joypad.cpp" + +unsigned sCPU::dma_counter() { + return (status.dma_counter + ppu.hcounter()) & 7; +} + +void sCPU::add_clocks(unsigned clocks) { + event.tick(clocks); + unsigned ticks = clocks >> 1; + while(ticks--) { + ppu.tick(); + if((ppu.hcounter() & 2) == 0) { + snes.input.tick(); + } else { + poll_interrupts(); + } + } + scheduler.addclocks_cpu(clocks); +} + +void sCPU::scanline() { + status.dma_counter = (status.dma_counter + status.line_clocks) & 7; + status.line_clocks = ppu.lineclocks(); + + if(ppu.vcounter() == 0) { + //hdma init triggers once every frame + event.enqueue(cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter(), EventHdmaInit); + } + + //dram refresh occurs once every scanline + if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter(); + event.enqueue(status.dram_refresh_position, EventDramRefresh); + + //hdma triggers once every visible scanline + if(ppu.vcounter() <= (ppu.overscan() == false ? 224 : 239)) { + event.enqueue(1104, EventHdmaRun); + } + + if(status.auto_joypad_poll == true && ppu.vcounter() == (ppu.overscan() == false ? 227 : 242)) { + snes.input.poll(); + run_auto_joypad_poll(); + } +} + +//used for H/DMA bus synchronization +void sCPU::precycle_edge() { + if(status.dma_state == DmaCpuSync) { + add_clocks(status.clock_count - (status.dma_clocks % status.clock_count)); + status.dma_state = DmaInactive; + } +} + +//used to test for H/DMA, which can trigger on the edge of every opcode cycle. +void sCPU::cycle_edge() { + while(cycle_edge_state) { + switch(bit::lowest(cycle_edge_state)) { + case EventFlagHdmaInit: { + hdma_init_reset(); + if(hdma_enabled_channels()) { + status.hdma_pending = true; + status.hdma_mode = 0; + } + } break; + + case EventFlagHdmaRun: { + if(hdma_active_channels()) { + status.hdma_pending = true; + status.hdma_mode = 1; + } + } break; + } + + cycle_edge_state = bit::clear_lowest(cycle_edge_state); + } + + //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_state == DmaRun) { + if(status.hdma_pending) { + status.hdma_pending = false; + if(hdma_enabled_channels()) { + dma_add_clocks(8 - dma_counter()); //DMA sync + status.hdma_mode == 0 ? hdma_init() : hdma_run(); + if(!dma_enabled_channels()) status.dma_state = DmaCpuSync; + } + } + + if(status.dma_pending) { + status.dma_pending = false; + if(dma_enabled_channels()) { + dma_add_clocks(8 - dma_counter()); //DMA sync + dma_run(); + status.dma_state = DmaCpuSync; + } + } + } + + if(status.dma_state == DmaInactive) { + if(status.dma_pending || status.hdma_pending) { + status.dma_clocks = 0; + status.dma_state = DmaRun; + } + } +} + +//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 sCPU::last_cycle() { + if(!status.irq_lock) { + status.nmi_pending |= nmi_test(); + status.irq_pending |= irq_test(); + + status.interrupt_pending = (status.nmi_pending || status.irq_pending); + } +} + +void sCPU::timing_power() { +} + +void sCPU::timing_reset() { + event.reset(); + + status.clock_count = 0; + status.line_clocks = ppu.lineclocks(); + + status.irq_lock = false; + status.alu_lock = false; + status.dram_refresh_position = (cpu_version == 1 ? 530 : 538); + event.enqueue(status.dram_refresh_position, EventDramRefresh); + + 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.dma_counter = 0; + status.dma_clocks = 0; + status.dma_pending = false; + status.hdma_pending = false; + status.hdma_mode = 0; + status.dma_state = DmaInactive; + + cycle_edge_state = 0; +} + +#endif diff --git a/bsnes/cpu/scpu/timing/timing.hpp b/bsnes/cpu/scpu/timing/timing.hpp new file mode 100755 index 0000000..092e7f0 --- /dev/null +++ b/bsnes/cpu/scpu/timing/timing.hpp @@ -0,0 +1,41 @@ + enum { + EventNone, + EventIrqLockRelease, + EventAluLockRelease, + EventDramRefresh, + EventHdmaInit, + EventHdmaRun, + + //cycle edge + EventFlagHdmaInit = 1 << 0, + EventFlagHdmaRun = 1 << 1, + }; + unsigned cycle_edge_state; + + //timing.cpp + unsigned dma_counter(); + + void add_clocks(unsigned clocks); + void scanline(); + + alwaysinline void precycle_edge(); + alwaysinline void cycle_edge(); + 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(); + + //event.cpp + void queue_event(unsigned); //priorityqueue callback function diff --git a/bsnes/data/bsnes.Manifest b/bsnes/data/bsnes.Manifest new file mode 100755 index 0000000..4602d4f --- /dev/null +++ b/bsnes/data/bsnes.Manifest @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/bsnes/data/bsnes.desktop b/bsnes/data/bsnes.desktop new file mode 100755 index 0000000..d527e97 --- /dev/null +++ b/bsnes/data/bsnes.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=bsnes +Comment=SNES emulator +Exec=bsnes +Icon=bsnes +Terminal=false +Type=Application +Categories=Game;Emulator; diff --git a/bsnes/data/bsnes.ico b/bsnes/data/bsnes.ico new file mode 100755 index 0000000000000000000000000000000000000000..54acded45d3d1e9b458bfab3c24feeb328e231d9 GIT binary patch literal 22071 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 z zZ~6IK-@pF*DEgQAuLS<%zo`9CF~d}El)r)o^$!Ap%KERAg+}uP{u95z|0IT1DvPKm z4GY{m0Uwo=Xn7!w0DrH4Do5Cna%3IA9uNq)0JsD=12_yY1S|&pIX~*SA=CrqCw~k5 zkS-9R(wI9Kw+sLCpkpwNwj8+W0(OJ$2+|s0 z1iBo^Isb|LXAL;tn#OrLe^hSuf+3aGUA#nQdtd;z3bYJUIyH7~*`s5I?Avu}jHdiMC4Z(aAWgy-&c8?jt}?)RlKV{lXjwi+310}< zoj4nK_x}v%>E(s=TYeGk$!vfh7H}8k`5lZsIlfs+U%llt%H{unYDhadSNc_D0DoHp zCQ$CE@4x1M^xNk;;89i@Z<_AUAJE6a_d36dH<}M{HU(}&-{L&udn200Q1jhd;M|9{ zqaSEXXj~sO{jYd{Hz(jUVAAg(zt?uYgtbOUu%q^YzdWE3_5I(#5X5o*S>C`64}P2j z5Co{fJ32j4E?mTCGZ=yw1sf0jF%4tRwC z1_5J$NzNd@`1A+$*)zESXEuN~;1D1fa0Flm_~ALp%)CGIM}ME+>>H=T&~p5R{%PF* znvs9-8}M2Lye9sF+uvzRG5;XV?`92U$o#=}GP8Y+Lwfn|8uvJ^&sq%l7ui$N+Lq{4T<$Z?T`>S9YKZ>3W8%q5E9%{;)7EVB5cQu zPwu|6(}pMOg^||tSyDbr;-qA9M+Fg?HxHE*L~e2oKaBYloAx;sdW?cTlM?a;yIAzJ z1|CI#0tbqEj*hl>#3$|OoV}Hu#pJf?x;;-zY z;gCNQApI-0r~D8Pwy*u5ed@f27%kFujybRf#eR(k*h9}P*8wWNV;kkr0JhlxpU(LH zyB=ox|0BfRgW4Rr%$)@GI{{%+Yhl>;NB1}j0OyQ zjkG<(1?RYL{(=MAHr#*2HrNybTm*b~f4(+E39q_tXg=hRw!@kAjlTB-?Ea2zu%!yP z1$YiX_h+Zt+czcn;LBJCcGd$z0cn6MfCGSef5$c&2kfEyPv5llm9WY4tG}E7U&Vv` z=>YX#vGXlX!GD$KkA{Qo&4BOL`DcDjAiwkvKWgWD@gZMVz*pP+XWOkHrM{QP|1}Kq zR{|6Ornke7@O~aJ|Nol*-%Ss;H~+%eVy^ZHUzrD=z;wQ z3ZnEGj3f@2r{XZalbbMfRLEi&L2yC{X%_U8piZI(!N4{QgfcKvVLJl_4FypUwn>yA z&l#rR6`hQu+Y&;6seKCNFEdy8_`m){M9?EGEqdTFKR_@tJZy(Ne*EPO{k!x}okHjE zPXSYFxT!VFFfu-_``xr?VA{Dkkaqf7bSgcKsgdR^t=OQqY+3TB^PPD3hnD3?D4+~b z2Pj4=*u9Xp^O6X4A{6qu4|oE&j!aOEMf_+&Fjk`Tgof3t-z|dpZva#C@agl?YcqKY zue*)Tf6?)FYW%+oY0m+s(oM-R{!*-6XFQipeK6;;t?MH=*RCC~hIB>K(oW79CT-k? zO}!_Rcb~nGM~D@YiU^&Foy7qCU^iem%hT2p(oLCi=i zY7_0c_!+iQAMBfokIM7r_}M*Yxhx>v4Zxqq#wa^qJj5pg(7Asx+>O{VJuRwV-owTY z>CpQQGsp3onQ_1M4r%w#ust#!!>#qL_+R_pcb*F!)6lyY0bP+?e|t^$wY>i6Z}1C& orimcq=pHZZvkre+J;NsJC(A?-;{XbLNB3QO0K-0?(xB=7AElyM(EtDd literal 0 HcmV?d00001 diff --git a/bsnes/data/bsnes.png b/bsnes/data/bsnes.png new file mode 100755 index 0000000000000000000000000000000000000000..02986ba80bf0a5b5e46d370a4653dae60373f139 GIT binary patch literal 1368 zcmV-e1*iInP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOG= z05>AI00h`cL_t(&-tCxKY}8d0#(&?PAwsLN*a3-!$Px{h5Xvsn4m3zqL@}~x z#6Uv~#u#H16Jx+F(Ff^^1`Ha6B7%w%2@hhTM5hH=tSBn0D1lI|trSbxN@wozfsqzy z)0xg6%fo!hB=hjU_nz-8-#Pa}7FlGGMHU%A8Sk*xLXlH8Mov?}hG0Yx4F-o~s&EPW zta`4iy($SrR`@$`l*$;5)Y@M4hAj!7)oXvw)9I^&fSG!(w5Nblq;QIBcEw?G-5gid z;RGUO{z-{9K`uSswx^RQ`eU{`78C)O?t;9Pz5uZ(UE={H4Q@2v!b}}Tz`^T6o!cF9 z@ld49#&!sS$oqaEFe$^Vb&?+ImUvqcpb_{I?-#m?ik*RW;@+z*FA=wwfG5&PuLe)| z`V#%)3k%00eJm+I1sQHYTHs=_(%?pkBzl(lStgbVU6Vo$RMpIO`M#*A5ZEXwH_hW- zQ7rayyJWOA)XZ@o0MB4%ZJG}oeQ|NGq~~+fmH@K_zNqNFKnVcU{;s9K#x_n*czB0+ ze1V{M`!qlyq*9AUJBxsE<8nXJJGQqr+?m;XHOrcs?hG7`^R z#N8PA2zQ~()vV;t|35_~TXB4Z=R-ztIvHQHGQU}u5-|JLC761Ap6V5i76cUVg7Y=- z9lfb!9iHuoc|c#(Mtz7}#6*l`zu+0HQ0g6x;j8W~lRe2YSeL*-p z%FH&Wv2?QoehQF49LMfHDS6kHQ5J>Xsb&ahB$!n2_cqJKox-Z&Qa zqFd`}K!e0DW~>KRIoDMDe*E|cW%g+c#vjA%9#>JZqxEU@2fC497~(dd56~SW1e`)t z0|$Zc`He%&AaUVP&rIMAiN5I%Xq*P#mRKlp))yD|A|9`j6l!MtcA&(Sm+uXHoc5jt zz^hWmd&bO|EXNs(M*qgF6VeX6e^<_}Q!Ew_{CUrN3cM$7h6VBSo)-apVPQXuMz6i% z53JrZKPcmq1XAt=LEQ)T1?-c3{GTwxdC&TQynoazeGv$PM2`0?$)py%XRG%Nq$6R* zQ14mcJ+&zsea3sf@}6mSUaGJB>vXCSF=Hq&EZK=8%{prdMHX3f a1pWnBso!rU2NnGQ0000 + + + + +

bsnes™ Usage Documentation


+ +bsnes is a Super Nintendo / Super Famicom emulator that strives to provide +the most faithful emulation experience possible. It focuses on accuracy and +clean code; over speed and features. +
+ +

Modes of Operation


+ +bsnes is capable of running both in its default multi-user mode, as well as +in single-user mode.
+
+ +In multi-user mode, configuration data is stored inside the user's home +directory. On Windows, this is located at "%APPDATA%/.bsnes". On other operating +systems, this is located at "~/.bsnes".
+
+ +To enable single-user mode, create a blank "bsnes.cfg" file inside the same +folder as the bsnes executable. bsnes will then use this file to store +configuration data. +
+ +

Supported Filetypes


+ +SFC, SMC, SWC, FIG: SNES cartridge — ROM image.
+BS: Satellaview BS-X flash cartridge — EEPROM image.
+ST: Sufami Turbo cartridge — ROM image.
+SRM, PSR: non-volatile memory, often used to save game data — (P)SRAM image.
+RTC: real-time clock non-volatile memory.
+UPS: patch data, used to dynamically modify cartridge of same base filename upon load.
+CHT: plain-text list of "Game Genie" / "Pro Action Replay" codes. +
+ +

Known Limitations


+ +Cartridge co-processors: certain cartridges contain special co-processor chips to enhance +their functionality. Some of these are either partially or completely unsupported. A message box +warning will pop up when attempting to load such a cartridge.
+
+ +Satellaview BS-X emulation: this hardware is only partially supported. As a result, +most BS-X software will not function correctly.
+
+ +Savestates: due to the design of bsnes, it is not plausible to +implement support for savestate and/or rewind functionality.
+
+ +Netplay: internet multiplay is not currently supported nor planned. +
+ +

Contributors

+• Andreas Naive
+• anomie
+• Derrick Sobodash
+• DMV27
+• FirebrandX
+• FitzRoy
+• GIGO
+• Jonas Quinn
+• kode54
+• krom
+• Matthew Callis
+• Nach
+• neviksti
+• Overload
+• RedDwarf
+• Richard Bannister
+• Shay Green
+• tetsuo55
+• TRAC
+• zones
+ + + diff --git a/bsnes/data/joypad.png b/bsnes/data/joypad.png new file mode 100755 index 0000000000000000000000000000000000000000..43d7c7d92849a2f1573b2a3324c5cd8e87e53834 GIT binary patch literal 102778 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

YyFJx(qPSu)%ZGJ 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�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 SJ&R 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

?m%dv`yQ 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 O0000 + + + + +

bsnes™ Reference License


+Copyright © 2004–2009 byuu
+All rights reserved
+
+ +

1. Definitions


+ +The terms "reproduce", "reproduction", "distribute" and "distribution" have the +same meaning here as under U.S. copyright law.

+ +"The software" means this software package as a whole, including, but not +limited to, this license, binaries, source code, documentation, and data.

+ +"You" means the licensee of the software.

+ +"The licensor" means the copyright holder of the software, byuu. +
+ +

2. Grant of Rights


+ +Subject to the terms of this license, the licensor grants you a +non-transferable, non-exclusive, worldwide, royalty-free copyright license to +reproduce the software for non-commercial use only, provided the software +remains unmodified, and there is no charge for the software itself, nor for the +medium upon which the software is distributed. The reproduction of modified or +derivative works of the software is strictly prohibited without the express +consent of the licensor. +
+ +

3. Limitations


+ +This license does not grant you any rights to use the licensor's name, logo or +trademarks.
+
+ +The software is provided "as is", and any express or implied warranties, +including, but not limited to, the implied warranties of merchantability and +fitness for a particular purpose are disclaimed. In no event shall the licensor +be liable for any direct, indirect, incidental, special, exemplary, or +consequential damages (including, but not limited to, procurement of sbustitute +goods or services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, strict +liability, or tort (including negligence or otherwise) arising in any way out of +the use of the software, even if advised of the possibility of such damage.
+
+ +In the event that this license is determined to be invalid or unenforceable, the +Grant of Rights will become null and void, and no rights shall be granted to the +licensee, within the scope of U.S. copyright law. +
+ +

4. Exemptions


+ +The software includes the work of other copyrights holders, which is licensed +under different agreements, and exempt from this license. Below is a complete +list of all such software, and their respective copyright holders and licenses. +Note that explicit permission has been granted to the licensor to use included +software which is ordinarily not compatible with this license, such as the GPL. +
+ + + + + + + + + + + + + + + + + + + +
NameLicenseAuthor(s)
Cx4 emulatoranomie, Kris Bleakley, Nach, zsKnight
DSP-1 emulatorAndreas Naive, John Weidman, Kris Bleakley, neviksti
DSP-2 emulatorKris Bleakley
DSP-3 emulatorJohn Weidman, Kris Bleakley, Lancer, z80 gaiden
DSP-4 emulatorDreamer Nom, John Weidman, Kris Bleakley, Nach, z80 gaiden
S-DD1 decompressorPublic DomainAndreas Naive
S-DSP emulatorLGPL 2.1Shay Green
SPC7110 decompressorPublic Domainneviksti
ST-0010 emulatorFeather, John Weidman, Kris Bleakley, Matthew Kendora
Qt toolkitLGPL 2.1Nokia
HQ2x filterLGPL 2.1MaxST
JMA decompressorGPL 2NSRT team
NTSC filterLGPL 2.1Shay Green
zlib decompressorzlib licensezlib team
+ + + diff --git a/bsnes/data/logo.png b/bsnes/data/logo.png new file mode 100755 index 0000000000000000000000000000000000000000..e78ef27fc2162e5ea94ac1f85bdc42f8dc73a93c GIT binary patch literal 16733 zcmYkkb97|e^FG|Mt%+^hwkEbGwllG9+nP9;iEZ1~#7;W)n|tr)`&;kowYtwA`}E$Y zPMzBI)brGdQ2Z{50E+_)000oArNop008q`ZV=HLLuWv(HMwPE0$RDzjVt~(oKKY%$ zk^lfeZp>8EexhX?1oGxfW3GLc!+ zPSDW*ou4S0DhXYYlKoU9`;EzDufgm zFSZm->2!;5Ha^mV&Y{RzYB5MG|GR(^8>zeKwm_tDPPo`{yIw^ znx<|Htf487B!zMVP64wol;-_C3;cB=(wpTCJy=so$PgmNjeQU8e};FLZNQ`CO3nkR z8KbNrcjY96qplmPZq)FWCqc!;;npUEWd@RaE59Kt>8nfx2JKW*b&pa8|7TTZ@B(h7 zXQ(ifgNWD29Ak{KrEQN;RlE*)-l_{=++9!XVUo(X9%aNI9c@(88Kr+mbP(bjlNF%< zTRo^F)hPN%_8^Fs&mnCopb}4}SMT8F@ffNJ_zOA3?kFjlkSSL`FE)wSuCLPs5}&ue zA?mG)!$1ie##{M+o)fkap$5aF7yZ50#o?et%73{M9tp0n5+;u>?-r_1gT-QWxD>RC z!iTerBO%bpp~Ue&e>2t2G-pE>d)6RJTW$f$%LdSj*7Iutg>#=S;S8z0NV6H#wtSY@ z9LO(T(sr309V~eoP zE7G28s{V^C2jBe-nvt{g!xUiuH5BzYlrtD?-AB`)Zo$$f@K;H1N{S$ z!7~|4yf-;uDV<#~9jeYwLpp`yb~8|qyXXIwY%5$ALol((VgL>2U}`aF#0y_jLixO=oVl zzJpIQT{_H)`q#OWwSGY*_S;sQl_Nap!(02S{k**jJ=&lB1Cv~$+VL4*l9#D4y%WE^ zqw~M#68NQyU6fj@f(3r2UxEP$*v7X$Mcp{|qLMe8A3yY_0#U zR1VNo`^8^Dcs(}bKTN?UqwdTF^Zgi53dw(q9nBIm_OKF(nPaxdYv zNS2comK2jFY*{zgZxZ?!eDU5}>=|!cMM&IRq!HfB{26$ssdBj&Y~eQcg4rgsSJwEf zDPmk?SB@JB0b5MMngmLqw@*JxmNYWg2!=(ciN=taB109+s1!^QMzvI&Oxt$iLuVS- zl)}f_(CHpZ?EL=b2=NB$j+RA(zkF=6vuIhe;h#n1{@#JbKYCpIUC2{xx#m3g+(@ez zj0rR?0Zs(`5?ou9bD+5>_c_mVjwPc$N*{@w{w*Sm^y+Jh+z7_s*qN{zct3BOqn$!{ z-bMs#JZ(WEHtwj+7Md$bw8wC|)MZpv|sPsC4vy=ruP{lI= z&p|w~d^x&x>FtvLwCZkK|)yQAM)Xa`OeBihv=s3em?fCQ*=b2jS$JoOQltj#8~ozIOmn{UJCsE z9Y~RS3W6L9r)AtKb0VZjoq*0ZIQKhOZriyeb*Qkz*17E?|H|vP+{I6&6Exo>tQ0$Q z+(Ba7UyP0`?inMo8kLJId6f5$*+>fh5XDsoDhX0y#bYV@vDFlUh#;4=;>mQOpq6AH@Y$lKn=ktE!c)W9-ze4G~GfU@wBA)Pj37!=4 zNvWriO65gTI}9$5u!n(C84c9p*K;X3wkpO$%HrZgM;RGwpN9?2T`|l=h(W zBv7yOVG&2GZ|1D2df(CQV>WmVHQ~ViN2dwMsZUcl$=Aq}!&e!Mlu%WR^fdO?>86}H z$&lCkcyG&|;XPYzpctTLH+a;~TIx|x)<&G1A;Of@L=7 z;xg`;z$T}=_ptB1R>HeNz5;K_)6622G8u6k)|XrEJ)Utn4rxP-Kc8m;RhvQI?$mU} zBTHe!HP(aYuXotVjdrRDL5uca;(2u58vE}<4 z7l20ub_J28argi+h>xK?=~geLdlGpu2Vs$>F&iN`$)P^ISUeSKAP=RItGHYSlswlm$vsm-oe zB}}&DPndf%mRiIRiIk>jv0X73DT@3^*Hh;@ z5&IBk?0qG%A6sae^J;7QuFSqbFq0=&gV4>)sbVNLf9s#Hx{Mcoq!H0mPG@Pq;U**` zMk+W{>I6X|)dk3%_EQZr22f zSfOkP7D0jHvhC*$d!8)sw<8}aW&0|KhGgo%*oI2EuI+|+;tGOQf70X={N^TWxF*qY zYC4oncn8ztQGy6MwsJ4_Etqh@D7CrK3H)i!Y`@5k>_|uF&GY;^+0MYDMfq}}fJMHn z;~%@XLp~g3Bh1wDP{w<+WZ#%Lz3sm%Hi6 zbf|r6a_MULMrnW|kX2Mt4ph^-KYQ0>z5o3A_COoSA)_0`{wcdk1=HBRHU|NO<2PCO z0uNcBe@J+B5oVkM@ZBT|yF`8QqM9@+Re=iAVk%!5;gQ!p#rqxwZf=#vpSP=z*5 z79Lr(2i+aq8fYHNTW00fNk0E76*@th9k|eiG~cC1iEqe6v2m=+i3=c60t-Tz6^K#R z3c!`S40g@3XpbLQtxCE|OG6kQ2{7{n?0|DYk3v%6)^uV^y{_;MIRLH4qgGcbT$I!~ zU(pRiqQV9OBwML$$qhhjUU$RpDZe&aV^nk!fCu|wll(0y`!3z4~VGDp+PGmOqc+L9=> zc_%a&{?Mnw^s|VMjKdg99vDfRfp}s+bCgQ(i}>9#?Xk(kAbaQZM1~y3;Z#~K+4lGw zy@lm2jzGHD^3H`nOa#Y(%`-F_{0&d$={b}(x6rfp+dxBZHoAI-lwa3sewOswgNnCY zYi137sLk-?hhT5U2|Uq(F>+$1(!;iWua~!OXJ2Sp;sPmB7ARPqlc4(y^H6@~?w$@C z#IgBerFz8^U8{qUgi0GAZZ>ADrCbKoP)%wpIp-QPOKtKn-Vem%ST+ahAFT1>yZlj{ zEvkG{Phbu+_Yqctx#u%nE7-@W3-EmI$!d_Hb2oTT`AUZ8^PX{FZp(x+dpp&2=?lx4 zj29gS5pq@z&{b#E6g2lk)6(KB^{oJ8xo2t5S|IyV`eGpPgnNIfMV4IfwlT)4hyv0X zNOU%A8VNc#!}$25db4-pw-#dtXl(pFY=<;)bg@0<+h;@DZxKH^%inN-;ZM5+?q$eA z5q3+s?>fu#XX|3iKT)RSFvL8);aj!k_b4_hl@QPm`bzcIhWcs!6~Fxg9WhXvU9RIq ztva|3WG!-a9BR@3+Vd2CFq$D=lo~_!=n*Gd3b!CWYf}FVMmJQ;0LFtT&drqe9>!1W z{gqRxPF&DW&plhsnw6~654Z49wD>_ba+D0W)3=Mu0IgpIq5~uA&R7)!`R*4Ck)Rtf zXDQ&OTU}P9e)1-Cd~w8ir7VFeq;Jj+n#n#YMY0Q z@D0Yqoo9NJ*D9oD^oEK6_?7a-;&vFg?&A6f(m!~-q{!TCmtr|mw$)x zAabB?5#E8dAPzki%7bq^1keKIT>9FQMTXwh)f0Hm*ahr#>E$;S1>92C8Fl_jH>rOw zkVkveeXf;4nz4O|7a48|@y3o!tZ2xY9_G4kK)evaL+msYWiae7iK_d%lO+mTM-$iC zf30sC&@z7*|6OuWPGhsvGYq3gxxC4iTiiMzz^oi%p4A3om9X0qlF=h2cCdEQ^8RN)VfT0E4l2{sl+Z|%t6 zZQxF}p8-#`Z`rX7%B@se0xX$7kyZ2XENCcbo*&||>-G!Jo@)rP8yU$*yVlxld)VJP z(uNjBX04u6X;gxW1Tm=Jj{Ht-2Z%+C0S=& zqCylJ&Bu;|x z=HbIb<3V;j^o#m*TN~nKI{z~(t}ZCU=s2w#m$gYsLr$h8TAx_vLYnUfwQZX<&gQ@x zL=fwC%edq3;S-p6ng?Px+pF4>%4gdT20MT=w-i;puK69NwNgq;j^XaG0eM*2uT5lN zX)ne*-|bnoK--S+e#r8-nNHh4|&{an|U#kS;ypwoa=8Ux@HuLVNY?c-jzThqVOT~`SA zyL0Ww@zZVWkQErLZyIT8f>pipAh2)QmfQQ23nF7Dm1s9TQ$lo8Tl+822HKjOUU3`BL6}Y)2gj>^ zGiEk6W81g*;vZ;Y4T43QU0&F77ZRcF>w9KlA};YGJx z_+Q(|c#Amz;D1N>Q|Y*bv3=hOXg9TYrF)ye*NU$sTu0^<r#t_o?gfooDYY_&0aH)#uW;7Q*mOv-aY|ofU8E=Bt0PE=S`eRaXx6%)v<9< zOCi|dY2l&%!kevjCfy5|^J?tTU#US%j^%(^+9%NQ>^%6<{CpRa2IeaFK2d+QcWDGy z^$1Q$S);&1LX=TKk-zuk6mlAmJ=A9iL10p7*df^oj;cOR%*XC4Pm{z%>IXv#FBF$6O* z2*07QWAxCa7exq%vRWenO>J%(@v14;ou5^bIgzSclip}o{5vE*9>f(X?S@0C8WKaD zhyitri#Vg=qJSF4*VAQqdO#BrRQC5&g*~D!A+TnlOU#0O6V5@po8>B_8LRx7X;-K0 zMKsVe0d?&$n=(aW)6Bnedli9BLjdHXR>#I_C5ZMoUmXqM@x!zgxPtsbj>hq3m9cI% zzdz|&5Fm22v^YDRVpw#`6+!?ouWZuU2|Zh80l@7rd36ea2idPW&}q~>TYTMbHCO?G z8*#S?*Vy<@)AUwOLgO_0&7e1yy=PjP25?IfH&bHkoB1!8uyu!uV0tNF?XJ$@nG3`3 zf&V9y>0YUeASl_hP*U;%3RjbV;9D1S06s-!-^P@fdl>F1yIGk->x$nT7_yQ za*C;E$xqeZ2(H4RE%f3kV{O@QkF%ws{sWqN**3)S$p=iCkC5|^``hRK(@n`&S?|U; z#z8Gj7jTb&yr@M&(Dja9{xxt*PhVTzB(5~IPB4Bd=c}eFa0Z~;Ct`X0n25A+hf@%0 z!7iHU#Dd`qGG6`|=|j8j>FwP52joTp@s@wkh-moKcqheJr>4;M9UB#U1QvVws3LYn zf6`haE6;RQe&dAN?Ww0L#tg4}GuCpAUeWt|rBtwZo;^@=2MXiGVs+4LWG=WPKR`~P zHXoGc-4~v2#0ykW^u9BA5|(Q0r1T^=z~`zHDd+MmvfK%QmNpJ|C<=nU5>MtU8&fbS zmkU0)!8x8^W6569@A3~)uPrNUy?*cORBxY7sIZJfgJn@e>TmTiiwTq>!fL|TqX8^A zDcFmc%02MT{lrT7tWs1)h@1K<&#EEldK}PU#P#hVB420NUJqA&2()h|y3I@al1gVg z_W%dH{sHqm(h97h#%#ik-0$OhyV~B>E1pw>u_p?smTC9yv!yoNm+guY$vit}H%Wwb z1I{GE_~$p#DeHb$KaG?Uz1UC>lgOuvhDq2~bNkw7WuVgVW#{E=czYQPMpmtpTT=78 zt4kjBRV76H^=V5w56fry)kh*cx6SYkQ*^fJ6oReS%)*ewpD~h~j{4gZW3S3n@09~~ zdrnym$Wd%-<^&bEr~Y0;*wdua)|YOMD1+2p6TBD+QgfOk*rA!(%c$NSKg&b~3W@M$ z&RWyGPb~bzX*+i&jr)=Op&Q-cc8(yZGspDT0dzUR&3P#r36_XW*}wyeRt^Otqo&|D zojK~{vQc(?dmyY)jkw0XPhaJEJciY;JmV%hqXJVPGy?vgbJp$ZJUn23%YL$}f*cSp3xQ{=}xb(@{t zk6iCCmH_h0b-m7vixGvJ&%%XfF}8RS-Uk-MXLzsO1~HQJS_{1|lO#ZK{0QG2dR0MY z_1uRFt{9y@+-(%8?Hfb8B1Ne_!s$81%Q;3Mvbx<{VwvSKLH+S7k>Gc^iOm^|iFy{g zlkDs6eh$Msc7%BgfR1VQRjQ>c#7;Z0>zz=AlR|fk10|RCxoanWrfp5Tpqr3SO`9R3 zGD~2qS)5BRwYdLV=P9pp`F#pcowB|uR2L=>5X&`B;|n4(8&2x>+XcDM5Vl#eG zNssa2Pqg)-IE6q;+1xlMb}CS&9Q86;gXD#PYKnvlbcgp@($P79wef3$2Njr5je7Sm z&EolFL_hcfL=_FwH_%#t2mofH(tRox!d+tVUrhN%I);t6 zsVQXWg-5l}vdq#z?YZ(qef1`fZlf6@h!ipFxmAN67)x9wgW6O<7A4~{Rg?3=DpzM^ zT|EwE2LUl;KS(13D+`kEFk-+YuCUgXJCPZwI55w>A(y)m=+D z31JNkzz}v<1JdN#oGFLs)`&TiNgy}{$jiAy7|#re)xBvf&$>+lvX$G1ImYsI+#M+noVyG1m%hdPC;OrB+jApXJ-nn3xdx5V?>biPl$eh$RB={xqP zX0#B`7fo)UPj4_R?nFRZUy_?pm2uDn5jL(DWYe+0-&zGtxbEJ_>I8u82LDW#!If$&Yv^76 zkoSziWisL29Pq`&c*v9Xff!m|MCg+tc-5+r7S%@%c!a!VZ$zFhfcg&EI=6H!MpJc0 zqf<(<)D9nEdAq&mR?4aQ!3Vlz;JhHUc z0NtWZ^&`<;{OtltM|Dim;I!ekgtSbvXM=ds5hK7@hB^Vzk#Q^P6YB z#o@l@_s<~@Z-^upT(USVdqnwZ{lu5J3mSt(&sz0{>a5x7oZbd^cb zBCKw?Mh~E^UVBUUH!DaTX-0ziA)Rn*5WCs__@GeOHc*j&c2=v=IhQXWrXH-ZRuiS{ z0Wd_qCy8z)ScU}#Kfd-YspM=akfm;b*3Jx#rT?O!9g7kC$!v|8=f~nY5o?1OmUDkU zTElO3$Q|cdtKN;V%q-}l+>rwk_#)Lbc95q+Ox< z)!xw!Qoy- zhid3#Y{!j5MBrTp;qv8*!Ecq4N_9pL7X*{BjYrcqD254hS&L|xT7irgiN&tLjNU7?sv0Y!p{td;c7y~8#&pYb_l7Kn z7S9Tbczz@16$iBTN=^vPBbq4eQ**u76zCr9EG8OC!RW0AkV%F~GKeZ6PEjgYjCen_ z=5<(=ek_NuGozFRU-9agyBvSaEav2U-uF+?PSeH-A#iKDtA#8TYlmeP4;8y$ zEI>H3)FVJA;HH$CwMn{Ra-f`o`NR)`aPVu~Ztrp0`160Ny3-#GDitkaw?9w{e1BHb zu@M*=F>Cw_u+*gLB!|C%b>>ME_#%@1jr=9Us6!+mbGNbi+4rm6uAdQ#=F4W&6g>IF z5j~)s@{rd#>e*v+(KuMqAqXx?1#C_wu2`?=NrN{T%}vwKsHC1-nCUbdn{aL34l#W# zM~|bgF1~Q#34+}{fsGDm1JY(A1Nf_a9@O>73Gs9U&eOP6uxii}+ST3}n;4JHGchSy zRtjy}&v=ZM_o|xXUy*PC20fA6yK&Kh;be6^cFtgYZVW)5dqFwtN zQy>rMinDf!gr!Md9w&mRxPI+OqbUqsS-*1yXY_+z_CYOVXlZHoh@dd@K z0ns{_r6HqkygQ(4cie82c)YXXi=N>KbBSzxF(M!s43Li;5Mt#N+N_~p* zV?}KFM)uXVNvJH`Lu>hn@WUepSJ)31o>y=9*4A%Mv*rid!R=qE+uM|iW&`-GLQb&B zyvDItcObOzodvOPt8&hUz$pg;dbo_Y06fS~&4P4Nysxl(fBi6q>?PZiFGERBp@*T~ zu(UQA3|Hsbj)!9QlR1+w?-v{${kJXB!_kPuHkP{3X)!@j`$k&hW02J(W=yQKtl{qU zQ!kXlDql1c#-&Td{^-GbrP%&aV!_x4dNVu4{Zw%BRs09exO1l8`yFIDztQfnZCd%$3zc`*vj(E%g*VNY{ZOn zm62gw-MXJrCG~7z2nfZqS!(k^yaIZZ=@Rkhug0zf0+VOCZwlrTw?q!92>nL}vt~9k zJ&VJ7uPCb2fj`Il_4c?oQsyVaZ0bc~dt384em($~0>90_rLsn@qoqz-lP&LYt1Nx= z#26BP5q;$yi|GPGb2eR63QAxA1LUV#z)ICD!LYdP$VB7|L}{G5&zpcjmazH?Xv~kE z`>bCYHeCeY5By?K?G>w$MVi`9Tf3xaE3iD{L!v#~-=d+|qQ8j*cgj~ULqhdfJt->4 zA`IQ>%@mF&Y~;;m21c6R9X+h?+>AjapSos?@WfyXe~H9K@5DPt3K8%}*K%xu$G~bB zlOdoTnW?(Lob{X{2}K;~K_RO*NbQvt5_x<(X593sM8FJE!20c9qh5_xoS1^m(@Ci%OnjH&}Yg*gVl;`np1jfB&L{9 zo7*=x>+<)ow~Et9UOwIWxw)7WQbDJX7u}rejpJnOa{7mWR+ok9j6&iX5}U=*8B0q_ zCWg+WW4#^ZN4$yE5w11lr zB2m{-eub6MzI%M^>xvl`T$j~&@3;yG_Y&YZ_6-bt!@!XwF9IU8?)yw9qa_Nf-Xy1L z!6Opuyigz+BblS4dz9FLAQ?wyYk_cAS@-swNQdq6-&Y~IQ3Ru2?d~I*l(j%4VC?SQ zOH0mB#w4wTFiYax?KLLGDwuV`qp(+YN|{#J9;?E|iDKhiKK+kL~#;%?c!d+VB3Z`(UL0m64oP z!-X7y=U2t5q3la;<2cjC8AMWC)8KUR5SHNTdy?I|RFTc}qFWw1X5kwFs^g^Yb&atX%NVtKTic}Qibh6FJEyIu-cE2yCCK5M_Evv{-x1c zVFk&H9F`v)9oBgq$Af4aV6D2bEtj+$O(VSvUJm+(4>%sZ;`KjmkEe4(ex{+ty(Hj9 z+@Z`5WSkUdz2dwO_yvNgm8p`*k0G)oaXg@?+z(P@BX5UmOeTE6*?YZDL>Zq$ zwjV6YK|MPC+u6qe&R+Gd)1I@M^EsgKj@xD-j{zjZVFKfft$k_c)ry-dS{|WdLnh__ zh!4V|U<(sT8^IqU4}gI!jVlRd(F8>E(cx37N-KLBxA!ci7T;@|x7wy~7C@9v+)~jm z<1b5)+!oCHevZ44xZu`$v`Zi?u{G8%y(0S&a;V!(aK}hEV7_X6Q0=)-8l!)n<*t+=a?@uD9m-wz8%sP+2 zK)&JB`g;PVPE$!N*PDEKItF#Jb_kqR&^TsD7c$lF9!^%DhN{jda@SaQorD$VXJm79z9dcD$&xs6`Oae4ij~UBeUV;n=ubT_Xl)kSG}iuA_(2Dv%Ks*Q-nUML z@U>D&&|7`efgK;FZL_$?oozIr@ppC_tS)&QB@w=n>*E@bF)slpmzX@gQH-5G5W<$6+#^>7(ZPrSG{=C zToz*2p~7Xtb~j}aHGc6`x-dR>;@h>X>o^##y_#CUhWAgpay)bQ`ebWw_jKhOzcW;c zL%6D``*ZorbEf2h0lCG16z_mmf%kmg{GHPga?BQdW6@Jx#;l+^7Mm7)WyL~J|8)H& zfql&CD*S?O0HEkQtrHylqeGV>&*vcF1N6DJh}->FZ*Rhc~N3OgiV;?;vklh%%mS#&Y2# z)K+5f)DFNF9JQKdp`ypN5iu~Dwks*=ri?k^t5N+h>In~y$!nR`17yAC_vKI1*_Vp5 zM=8crFmDHUU(F1X2srZA#H4eWI3wP?nshY-XzhKD_8^PDzfuns{MzT!WRK0zvQ>8S zuVCdz8%@fAL?QR314#*j(&XOH@#XxpU_Y$xa3y-?(uqkvngx(nHnhEY+2tS-y| z?vv{ks8Id)baKR@k#BSoJ$Um3hM#Ny$Q9{#8cm$V=dW|qGw|qVXoY+bKB+Kv1*cTs zGRDQuIWk}1dn#KZGz>9by7eVt;L(VU;)=k9sis3;i->lPD7cLu}3Rg15j zO9$gGO1r0vC%a!ul>&Zc`{Em!vF0K1OWad*HDCA7lAxy8NwSlPJge8^m(fh4T^=T~ zUT(WRQ8^uMI~gI>!23Ks9}Xlv*+ElL0^1BX>B0~u?RIx*>`>|LhO5omg$XQT2Kwux zIm1JI-VS_k@FZ%QI5ui}oh6XSDuWvp-6lmm0e-tMb%#cBl(qt9+@vt560l{2lRnrd zsn3BYXPScN_6ST$+|smjEA}s%kJ--^D1m1g&fBB7*RHgxTuYaK;IS5_pFu3~3$TLi zaUQkoxz`h14zkufAyan}p}y4>cY+ae(6E1L4urA6;7$0YSpQhYxN@HJsbfG9)cs}d zf9=4RMA$Sc4~bI*6C&i#|3Jf=Q?G8d6i~&albg_n<%gF+BJ+Q2`D^+EqfZp6)q*RG z{&;1sl}P>8koc~`fMdx6q-s#R#`#-#rM}#FlP_~SW^|kJC!s|9V6lC*5{9s-?XTm-% z=&cq=Z~js`8_8AmhheDdBR4rN2L|a$ zE7b+x5J`&RV-w<=tUspwipcUP@se;zpp&Ao=8cW8y*6y0!H!DW5z&l$L9X|~A7z-j zd?FcB$Z9GdxG4C#R)Xpt#t=Rt=8=9uIQ_-s zR)O~=#2b834V`~7_0b!aRGME`pkDhTlNV%Q9sVln{}OtTm3TNCMN*f_sTo&mc$R5D zNG7+1dTOg0{A)+QyypZz<)a{(qU|4Iws40(9gJA~=kT{3?aa4i3Gpse5j75_j}h7p z=M}=s@QRmi0lBeOjOS4b6K7nR{mXVA~1QH`}9HIkUY zSv=iSJ%HQV%K-Bc*q##M=|viddclL+Y}uPGKx(QsWpkxT&tIenpQrDb90Mi2d7Z+` zYMPB}%(A^{5UA!2fGC2_w1e(@gQu>O#o`Ytpi9tPUq!NKU8u1`DgC(~P4D8>Fm2E( zek*xxD3hZH$+)o&kQqv>;7%WV;^H_#`g7CnwBOr1Fkdvy$Id-Mx#}=MT$U6h#uJP< z#gv~d<&#~0%LHb~I%|p1-dq4{i?&KpvnQo;`_1E>qf$HRDewi5re;Z}Q2^NI@_fdB zt#>L1(~RP9VWTmc6M?Er?vxTp{+ahBGXaM}rv3IU({?hmzX%|PUw*IU- zJib8saF%QrK|1-$!dLDqi_!A(mbF%_>jK%L8w=?M#!$sG`fuCIm&PW5Rq;bnryxBaB5^|u~tjY5GYze4t4xd`U03uG&=`-%0D2JO; zjrtQlwDAoBmdRG-y`)Q^^vTINh(biM0k0*5+s#~l_0V<@ayBp2eTe8*fTQG|mTbIZ zq|+mSG2T9ZY!r4wc@Y(pWclJkgux~(>uxh2ct5&02K(xN45~M+;DoGZ!GM|Ge&9!1 zL=Nm>rSHSh&Ee6@z02hrxc1(_Un2oIka;7hBJiXgAQ| z-ks&^x%){ms?b1N#3%1brKWJ1U!P&lJ* zBoKv3s)cF9%SgYbV~~DJLPX!cOj-4r5~o50NUd2Ol0E}5oUShfQ3$6g;vV8O zIxQ*%JBM|w-;Dksse7wTaL@CF+p7QGsAhB_CZ-|jP55F5Y#dvRj4r9g>p%D!uvf- z;4lrTBciS=-}m}vHM@q<`e$pZ!A6UAA_tW9JVo1w8F-g&Bo0GzQJIk!8YJRkNED}w zi3^lEga%{RYA}0+nV5T_OXlx+Tnuw!h$0+p;F|uxdW^mteO=?YrxR@RfwIxt?Op5C z%95v<85prR2DwAS@)Z$;TG32fw1cE?6Oq49D!k>9fP>AA^hxHf!$l<0-?+zGA}XU% zu!v(5E4y@mH4Z-$Zo3Pg`l$aJcT!~0HSEN{-L;X;{bV4l1?axwT!fryEXDRi8GK1% z?b-5l!UVu_$%*Xnm12iHE%P2)POeyg3@%HEo0J+%Z+UrezkC?%{Q;>!?Q(0?2GI== z(h{`ibdE;P#w+ra8Y>n$QwYjwb{>QNWA~Hq`SJ*d9JXRAH;OE-V6|2Zb(<%c70uO$zzvy~y&Lcq8l-cYd%`)iGW%{qi`0@x zFtPW1i0a;zkehe&`zLL1DwvAYVdLg#*RR5ngzXAQ480r+pRV)$w{fJ-N+%j4qS=PHs&&8T3 zmuE7NM?c5Mk&UtFzqromc6QM99H!vzmHopDlHEymsqC>H&s)O-Wb_teZ=yE*o=w!Q zfGe_F&|JOP|IlV(2ETM#_)BXn;gziuxkqL0q0=%k=du5#BPaJ+{nF2mR7*ytrb^kd zVlg_Fs+nKlpO0DbfG{e*vrEalFe>r~Q&dklA6DLWv=+>_&+w^l`NY}}{GgFJ8gI^y z$gZ3Z)WcB^=L~EfrvA|dp1xAqq-XeTSO>1E!qi_(;H*waylTqNjqDo)Rp%GI9^qO; z34z4uCP3H7`4s)tURg2UoU?zHWo^-^&ig$n9ev3akQO8;ygSevs|IdMR9IX?mo~&O zZEDMf2ehU8)kiEi;Ga&#KlEXncA3BJoU|mapYi<@Z=xreuw;TFGKQgT`zs#za2hk~ zO+D=L-YeFg(dIo1d4OQr$L)ogUAq3$4MT?DS#8ANlf`=L)z`vt=$9LH->%4>GeJIs%tBkG50Zx*ebOh z?yu$*8yN^e(XC1?!8v-r%ohUaR;VKIjYCC&%0c@a}8Zh!0pt#(2wD*5|xO$_eVO?j)T6EfkEQw<-`EL{jB-ve!>!9%H9!n*e7q* zLCp3uTL*va^b&2BFR7m1bW*7gr{zG^gmH;C)k+E5r@wWqY(Exc4~xnyK^XRv5H$RK z=;$Qmw#~~cyf*N60yyq~Z-3U928gi;3hzyVoA0g^R0|7Jh(P)~mTym=pL`OhsaFNwpRscm9tyEm zoa_FasMfi!CN?%Ax9F8g&f2NCKt3!71Q5kWxz?nGi?%>J?NxvprB)z=6EG6VLe|y` z&)?kw`3XpD1}ub&lS=zc9HtsLchTIMZi5uR!Fz9L_0m`yqy z7VBVn11BcUF@%gN8fNR+qpmpYJda*2`GAWsmtAE*j`PjWtd*LU13CD3O&-P8u51DGKceR6A!IVTuOBe7X0HeM6_$fyq=ZaqI8O^jwy-wiK?)vqpuk2TPm=Tn30 zt#!Py2!P;`LsLwGfw3DH7J~vrS3|;wSAyGfV(4!vkc#Up`N}~o{{MQ@|LxlTf3GJf di2nq`AUFTv;#FMq^}YmvwD@ +#define ADSP_CPP + +#include "adsp_tables.cpp" + +void aDSP::enter() { loop: + run(); + goto loop; +} + +uint8 aDSP::readb(uint16 addr) { + return spcram[addr]; +} + +void aDSP::writeb(uint16 addr, uint8 data) { + spcram[addr] = data; +} + +uint16 aDSP::readw(uint16 addr) { + return (readb(addr + 0)) | (readb(addr + 1) << 8); +} + +void aDSP::writew(uint16 addr, uint16 data) { + writeb(addr + 0, data); + writeb(addr + 1, data >> 8); +} + +uint8 aDSP::read(uint8 addr) { + addr &= 127; +int v = addr >> 4; +int n = addr & 15; + + switch(addr) { + case 0x00: case 0x10: case 0x20: case 0x30: + case 0x40: case 0x50: case 0x60: case 0x70: + return voice[v].VOLL; + case 0x01: case 0x11: case 0x21: case 0x31: + case 0x41: case 0x51: case 0x61: case 0x71: + return voice[v].VOLR; + case 0x02: case 0x12: case 0x22: case 0x32: + case 0x42: case 0x52: case 0x62: case 0x72: + return voice[v].PITCH; + case 0x03: case 0x13: case 0x23: case 0x33: + case 0x43: case 0x53: case 0x63: case 0x73: + return voice[v].PITCH >> 8; + case 0x04: case 0x14: case 0x24: case 0x34: + case 0x44: case 0x54: case 0x64: case 0x74: + return voice[v].SRCN; + case 0x05: case 0x15: case 0x25: case 0x35: + case 0x45: case 0x55: case 0x65: case 0x75: + return voice[v].ADSR1; + case 0x06: case 0x16: case 0x26: case 0x36: + case 0x46: case 0x56: case 0x66: case 0x76: + return voice[v].ADSR2; + case 0x07: case 0x17: case 0x27: case 0x37: + case 0x47: case 0x57: case 0x67: case 0x77: + return voice[v].GAIN; + case 0x08: case 0x18: case 0x28: case 0x38: + case 0x48: case 0x58: case 0x68: case 0x78: + return voice[v].ENVX; + case 0x09: case 0x19: case 0x29: case 0x39: + case 0x49: case 0x59: case 0x69: case 0x79: + return voice[v].OUTX; + + case 0x0f: case 0x1f: case 0x2f: case 0x3f: + case 0x4f: case 0x5f: case 0x6f: case 0x7f: + return status.FIR[v]; + + case 0x0c: return status.MVOLL; + case 0x1c: return status.MVOLR; + case 0x2c: return status.EVOLL; + case 0x3c: return status.EVOLR; + case 0x4c: return status.KON; + case 0x5c: return status.KOFF; + case 0x6c: return status.FLG; + case 0x7c: return status.ENDX; + + case 0x0d: return status.EFB; + case 0x2d: return status.PMON; + case 0x3d: return status.NON; + case 0x4d: return status.EON; + case 0x5d: return status.DIR; + case 0x6d: return status.ESA; + case 0x7d: return status.EDL; + } + + return dspram[addr]; +} + +void aDSP::write(uint8 addr, uint8 data) { +//0x80-0xff is a read-only mirror of 0x00-0x7f + if(addr & 0x80)return; + +int v = addr >> 4; +int n = addr & 15; + + switch(addr) { + case 0x00: case 0x10: case 0x20: case 0x30: + case 0x40: case 0x50: case 0x60: case 0x70: + voice[v].VOLL = data; + break; + case 0x01: case 0x11: case 0x21: case 0x31: + case 0x41: case 0x51: case 0x61: case 0x71: + voice[v].VOLR = data; + break; + case 0x02: case 0x12: case 0x22: case 0x32: + case 0x42: case 0x52: case 0x62: case 0x72: + voice[v].PITCH &= 0xff00; + voice[v].PITCH |= data; + break; + case 0x03: case 0x13: case 0x23: case 0x33: + case 0x43: case 0x53: case 0x63: case 0x73: + voice[v].PITCH &= 0x00ff; + voice[v].PITCH |= data << 8; + break; + case 0x04: case 0x14: case 0x24: case 0x34: + case 0x44: case 0x54: case 0x64: case 0x74: + voice[v].SRCN = data; + break; + case 0x05: case 0x15: case 0x25: case 0x35: + case 0x45: case 0x55: case 0x65: case 0x75: + voice[v].ADSR1 = data; + voice[v].AdjustEnvelope(); + break; + case 0x06: case 0x16: case 0x26: case 0x36: + case 0x46: case 0x56: case 0x66: case 0x76: + voice[v].ADSR2 = data; + //sustain_level = 0-7, 7 is a special case handled by ATTACK envx mode + voice[v].env_sustain = (voice[v].ADSR_sus_level() + 1) << 8; + voice[v].AdjustEnvelope(); + break; + case 0x07: case 0x17: case 0x27: case 0x37: + case 0x47: case 0x57: case 0x67: case 0x77: + voice[v].GAIN = data; + voice[v].AdjustEnvelope(); + break; + case 0x08: case 0x18: case 0x28: case 0x38: + case 0x48: case 0x58: case 0x68: case 0x78: + voice[v].ENVX = data; + break; + case 0x09: case 0x19: case 0x29: case 0x39: + case 0x49: case 0x59: case 0x69: case 0x79: + voice[v].OUTX = data; + break; + + case 0x0f: case 0x1f: case 0x2f: case 0x3f: + case 0x4f: case 0x5f: case 0x6f: case 0x7f: + status.FIR[v] = data; + break; + + case 0x0c: status.MVOLL = data; break; + case 0x1c: status.MVOLR = data; break; + case 0x2c: status.EVOLL = data; break; + case 0x3c: status.EVOLR = data; break; + + case 0x4c: + status.KON = data; + status.kon = data; + break; + case 0x5c: + status.KOFF = data; + break; + case 0x6c: + status.FLG = data; + status.noise_rate = rate_table[data & 0x1f]; + break; + + case 0x7c: + //read-only register, writes clear all bits of ENDX + status.ENDX = 0; + break; + + case 0x0d: status.EFB = data; break; + case 0x2d: status.PMON = data; break; + case 0x3d: status.NON = data; break; + case 0x4d: status.EON = data; break; + case 0x5d: status.DIR = data; break; + case 0x6d: status.ESA = data; break; + case 0x7d: status.EDL = data; break; + } + + dspram[addr] = data; +} + +void aDSP::power() { + spcram = r_smp->get_spcram_handle(); + memset(dspram, 0x00, 128); + + for(int v = 0; v < 8; v++) { + voice[v].VOLL = 0; + voice[v].VOLR = 0; + voice[v].PITCH = 0; + voice[v].SRCN = 0; + voice[v].ADSR1 = 0; + voice[v].ADSR2 = 0; + voice[v].GAIN = 0; + + status.FIR[v] = 0; + } + + status.FLG = 0xe0; + status.MVOLL = status.MVOLR = 0; + status.EVOLL = status.EVOLR = 0; + status.ENDX = 0; + status.EFB = 0; + status.PMON = 0; + status.NON = 0; + status.EON = 0; + status.DIR = 0; + status.ESA = 0; + status.EDL = 0; + + status.echo_length = 0; + + reset(); +} + +void aDSP::reset() { + status.KON = 0x00; + status.KOFF = 0x00; + status.FLG |= 0xe0; + + status.kon = 0x00; + status.esa = 0x00; + + status.noise_ctr = 0; + status.noise_rate = 0; + status.noise_sample = 0x4000; + + status.echo_index = 0; + status.fir_buffer_index = 0; + + for(int v = 0; v < 8; v++) { + voice[v].ENVX = 0; + voice[v].OUTX = 0; + + voice[v].pitch_ctr = 0; + + voice[v].brr_index = 0; + voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2)); + voice[v].brr_looped = false; + voice[v].brr_data[0] = 0; + voice[v].brr_data[1] = 0; + voice[v].brr_data[2] = 0; + voice[v].brr_data[3] = 0; + voice[v].brr_data_index = 0; + + voice[v].envx = 0; + voice[v].env_ctr = 0; + voice[v].env_rate = 0; + voice[v].env_state = SILENCE; + voice[v].env_mode = DIRECT; + + status.fir_buffer[0][v] = 0; + status.fir_buffer[1][v] = 0; + } + + dsp_counter = 0; +} + +void aDSP::run() { +uint8 pmon; +int32 sample; +int32 msamplel, msampler; +int32 esamplel, esampler; +int32 fir_samplel, fir_sampler; + pmon = status.PMON & ~status.NON & ~1; + + if((dsp_counter++ & 1) == 0) { + for(uint v = 0; v < 8; v++) { + if(status.soft_reset()) { + if(voice[v].env_state != SILENCE) { + voice[v].env_state = SILENCE; + voice[v].AdjustEnvelope(); + } + } + if(status.KOFF & (1 << v)) { + if(voice[v].env_state != SILENCE && voice[v].env_state != RELEASE) { + voice[v].env_state = RELEASE; + voice[v].AdjustEnvelope(); + } + } + if(status.kon & (1 << v)) { + voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2)); + voice[v].brr_index = -9; + voice[v].brr_looped = false; + voice[v].brr_data[0] = 0; + voice[v].brr_data[1] = 0; + voice[v].brr_data[2] = 0; + voice[v].brr_data[3] = 0; + voice[v].envx = 0; + voice[v].env_state = ATTACK; + voice[v].AdjustEnvelope(); + } + } + status.ENDX &= ~status.kon; + status.kon = 0; + } + +/***** + * update noise + *****/ + status.noise_ctr += status.noise_rate; + if(status.noise_ctr >= 0x7800) { + status.noise_ctr -= 0x7800; + status.noise_sample = (status.noise_sample >> 1) | (((status.noise_sample << 14) ^ (status.noise_sample << 13)) & 0x4000); + } + + msamplel = msampler = 0; + esamplel = esampler = 0; + +/***** + * process voice channels + *****/ + for(int v = 0; v < 8; v++) { + if(voice[v].brr_index < -1) { + voice[v].brr_index++; + voice[v].OUTX = voice[v].outx = 0; + voice[v].ENVX = 0; + continue; + } + + if(voice[v].brr_index >= 0) { + if(pmon & (1 << v)) { + voice[v].pitch_ctr += (voice[v].pitch_rate() * (voice[v - 1].outx + 0x8000)) >> 15; + } else { + voice[v].pitch_ctr += voice[v].pitch_rate(); + } + } else { + voice[v].pitch_ctr = 0x3000; + voice[v].brr_index = 0; + } + + /***** + * decode BRR samples + *****/ + while(voice[v].pitch_ctr >= 0) { + voice[v].pitch_ctr -= 0x1000; + + voice[v].brr_data_index++; + voice[v].brr_data_index &= 3; + + if(voice[v].brr_index == 0) { + voice[v].brr_header = readb(voice[v].brr_ptr); + + if(voice[v].brr_header_flags() == BRR_END) { + status.ENDX |= (1 << v); + voice[v].env_state = SILENCE; + voice[v].AdjustEnvelope(); + } + } + +#define S(x) voice[v].brr_data[(voice[v].brr_data_index + (x)) & 3] + if(voice[v].env_state != SILENCE) { + sample = readb(voice[v].brr_ptr + 1 + (voice[v].brr_index >> 1)); + if(voice[v].brr_index & 1) { + sample = sclip<4>(sample); + } else { + sample = sclip<4>(sample >> 4); + } + + if(voice[v].brr_header_shift() <= 12) { + sample = (sample << voice[v].brr_header_shift() >> 1); + } else { + sample &= ~0x7ff; + } + + switch(voice[v].brr_header_filter()) { + case 0: //direct + break; + case 1: //15/16 + sample += S(-1) + ((-S(-1)) >> 4); + break; + case 2: //61/32 - 15/16 + sample += (S(-1) << 1) + ((-((S(-1) << 1) + S(-1))) >> 5) + - S(-2) + (S(-2) >> 4); + break; + case 3: //115/64 - 13/16 + sample += (S(-1) << 1) + ((-(S(-1) + (S(-1) << 2) + (S(-1) << 3))) >> 6) + - S(-2) + (((S(-2) << 1) + S(-2)) >> 4); + break; + } + + S(0) = sample = sclip<15>(sclamp<16>(sample)); + } else { + S(0) = sample = 0; + } + + if(++voice[v].brr_index > 15) { + voice[v].brr_index = 0; + if(voice[v].brr_header_flags() & BRR_END) { + if(voice[v].brr_header_flags() & BRR_LOOP) { + status.ENDX |= (1 << v); + } + voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2) + 2); + voice[v].brr_looped = true; + } else { + voice[v].brr_ptr += 9; + } + } + } + + /***** + * volume envelope adjust + *****/ + voice[v].env_ctr += voice[v].env_rate; + + if(voice[v].env_ctr >= 0x7800) { + voice[v].env_ctr -= 0x7800; + switch(voice[v].env_mode) { + case DIRECT: + voice[v].env_rate = 0; + break; + case LINEAR_DEC: + voice[v].envx -= 32; + if(voice[v].envx <= 0) { + voice[v].envx = 0; + voice[v].env_rate = 0; + voice[v].env_mode = DIRECT; + } + break; + case LINEAR_INC: + voice[v].envx += 32; + if(voice[v].envx >= 0x7ff) { + voice[v].envx = 0x7ff; + voice[v].env_rate = 0; + voice[v].env_mode = DIRECT; + if(voice[v].ADSR_enabled() && voice[v].env_state == ATTACK) { + voice[v].env_state = ((voice[v].env_sustain == 0x800) ? SUSTAIN : DECAY); + voice[v].AdjustEnvelope(); + } + } + break; + case EXP_DEC: + //multiply by 255/256ths + voice[v].envx -= ((voice[v].envx - 1) >> 8) + 1; + if(voice[v].ADSR_enabled() && voice[v].env_state == DECAY && voice[v].envx <= voice[v].env_sustain) { + voice[v].env_state = SUSTAIN; + voice[v].AdjustEnvelope(); + } else if(voice[v].envx <= 0) { + voice[v].envx = 0; + voice[v].env_rate = 0; + voice[v].env_mode = DIRECT; + } + break; + case BENT_INC: + if(voice[v].envx < 0x600) { + voice[v].envx += 32; + } else { + voice[v].envx += 8; + + if(voice[v].envx >= 0x7ff) { + voice[v].envx = 0x7ff; + voice[v].env_rate = 0; + voice[v].env_mode = DIRECT; + } + } + break; + case FAST_ATTACK: + voice[v].envx += 0x400; + if(voice[v].envx >= 0x7ff) { + voice[v].envx = 0x7ff; + + //attack raises to max envx. if sustain is also set to max envx, skip decay phase + voice[v].env_state = ((voice[v].env_sustain == 0x800) ? SUSTAIN : DECAY); + voice[v].AdjustEnvelope(); + } + break; + case RELEASE_DEC: + voice[v].envx -= 8; + if(voice[v].envx <= 0) { + voice[v].env_state = SILENCE; + voice[v].AdjustEnvelope(); + } + break; + } + } + + voice[v].ENVX = voice[v].envx >> 4; + + /***** + * gaussian interpolation / noise + *****/ + if(status.NON & (1 << v)) { + sample = sclip<15>(status.noise_sample); + } else { + int32 d = voice[v].pitch_ctr >> 4; //-256 <= sample <= -1 + sample = ((gaussian_table[ -1 - d] * S(-3)) >> 11); + sample += ((gaussian_table[255 - d] * S(-2)) >> 11); + sample += ((gaussian_table[512 + d] * S(-1)) >> 11); + sample = sclip <15>(sample); + sample += ((gaussian_table[256 + d] * S( 0)) >> 11); + sample = sclamp<15>(sample); + } +#undef S + + /***** + * envelope / volume adjust + *****/ + sample = (sample * voice[v].envx) >> 11; + voice[v].outx = sample << 1; + voice[v].OUTX = sample >> 7; + + if(!status.mute()) { + msamplel += ((sample * voice[v].VOLL) >> 7) << 1; + msampler += ((sample * voice[v].VOLR) >> 7) << 1; + } + + if((status.EON & (1 << v)) && status.echo_write()) { + esamplel += ((sample * voice[v].VOLL) >> 7) << 1; + esampler += ((sample * voice[v].VOLR) >> 7) << 1; + } + } + +/***** + * echo (FIR) adjust + *****/ +#define F(c,x) status.fir_buffer[c][(status.fir_buffer_index + (x)) & 7] + status.fir_buffer_index++; + F(0,0) = readw((status.esa << 8) + status.echo_index + 0); + F(1,0) = readw((status.esa << 8) + status.echo_index + 2); + + fir_samplel = (F(0,-0) * status.FIR[7] + + F(0,-1) * status.FIR[6] + + F(0,-2) * status.FIR[5] + + F(0,-3) * status.FIR[4] + + F(0,-4) * status.FIR[3] + + F(0,-5) * status.FIR[2] + + F(0,-6) * status.FIR[1] + + F(0,-7) * status.FIR[0]); + + fir_sampler = (F(1,-0) * status.FIR[7] + + F(1,-1) * status.FIR[6] + + F(1,-2) * status.FIR[5] + + F(1,-3) * status.FIR[4] + + F(1,-4) * status.FIR[3] + + F(1,-5) * status.FIR[2] + + F(1,-6) * status.FIR[1] + + F(1,-7) * status.FIR[0]); +#undef F + +/***** + * update echo buffer + *****/ + if(status.echo_write()) { + esamplel += (fir_samplel * status.EFB) >> 14; + esampler += (fir_sampler * status.EFB) >> 14; + + esamplel = sclamp<16>(esamplel); + esampler = sclamp<16>(esampler); + + writew((status.esa << 8) + status.echo_index + 0, esamplel); + writew((status.esa << 8) + status.echo_index + 2, esampler); + } + + status.echo_index += 4; + if(status.echo_index >= status.echo_length) { + status.echo_index = 0; + status.echo_length = (status.EDL & 0x0f) << 11; + } + +//ESA read occurs at roughly 22/32th sample +//ESA fetch occurs at roughly 29/32th sample +//as this is not a subsample-level S-DSP emulator, +//simulate ~25/32th delay by caching ESA for one +//complete sample ... + status.esa = status.ESA; + +/***** + * main output adjust + *****/ + if(!status.mute()) { + msamplel = (msamplel * status.MVOLL) >> 7; + msampler = (msampler * status.MVOLR) >> 7; + + msamplel += (fir_samplel * status.EVOLL) >> 14; + msampler += (fir_sampler * status.EVOLR) >> 14; + + msamplel = sclamp<16>(msamplel); + msampler = sclamp<16>(msampler); + } + + snes.audio.update(msamplel, msampler); + scheduler.addclocks_dsp(32 * 3 * 8); +} + +aDSP::aDSP() {} +aDSP::~aDSP() {} diff --git a/bsnes/dsp/adsp/adsp.hpp b/bsnes/dsp/adsp/adsp.hpp new file mode 100755 index 0000000..3d99a19 --- /dev/null +++ b/bsnes/dsp/adsp/adsp.hpp @@ -0,0 +1,172 @@ +class aDSP : public DSP { +private: +uint8 dspram[128]; +uint8 *spcram; + +uint32 dsp_counter; + +enum { BRR_END = 1, BRR_LOOP = 2 }; + +uint8 readb (uint16 addr); +uint16 readw (uint16 addr); +void writeb(uint16 addr, uint8 data); +void writew(uint16 addr, uint16 data); + +public: +static const uint16 rate_table[32]; +static const int16 gaussian_table[512]; + +enum EnvelopeStates { + ATTACK, + DECAY, + SUSTAIN, + RELEASE, + SILENCE +}; + +enum EnvelopeModes { + DIRECT, + LINEAR_DEC, + EXP_DEC, + LINEAR_INC, + BENT_INC, + + FAST_ATTACK, + RELEASE_DEC +}; + +private: +struct Status { +//$0c,$1c + int8 MVOLL, MVOLR; +//$2c,$3c + int8 EVOLL, EVOLR; +//$4c,$5c + uint8 KON, KOFF; +//$6c + uint8 FLG; +//$7c + uint8 ENDX; +//$0d + int8 EFB; +//$2d,$3d,$4d + uint8 PMON, NON, EON; +//$5d + uint8 DIR; +//$6d,$7d + uint8 ESA, EDL; + +//$xf + int8 FIR[8]; + +//internal variables + uint8 kon, esa; + + int16 noise_ctr, noise_rate; + uint16 noise_sample; + + uint16 echo_index, echo_length; + int16 fir_buffer[2][8]; + uint8 fir_buffer_index; + +//functions + bool soft_reset() { return !!(FLG & 0x80); } + bool mute() { return !!(FLG & 0x40); } + bool echo_write() { return !(FLG & 0x20); } +} status; + +struct Voice { +//$x0-$x1 + int8 VOLL, VOLR; +//$x2-$x3 + int16 PITCH; +//$x4 + uint8 SRCN; +//$x5-$x7 + uint8 ADSR1, ADSR2, GAIN; +//$x8-$x9 + uint8 ENVX, OUTX; + +//internal variables + int16 pitch_ctr; + + int8 brr_index; + uint16 brr_ptr; + uint8 brr_header; + bool brr_looped; + + int16 brr_data[4]; + uint8 brr_data_index; + + int16 envx; + uint16 env_ctr, env_rate, env_sustain; + enum EnvelopeStates env_state; + enum EnvelopeModes env_mode; + + int16 outx; + +//functions + int16 pitch_rate() { return PITCH & 0x3fff; } + + uint8 brr_header_shift() { return brr_header >> 4; } + uint8 brr_header_filter() { return (brr_header >> 2) & 3; } + uint8 brr_header_flags() { return brr_header & 3; } + + bool ADSR_enabled() { return !!(ADSR1 & 0x80); } + uint8 ADSR_decay() { return (ADSR1 >> 4) & 7; } + uint8 ADSR_attack() { return ADSR1 & 15; } + uint8 ADSR_sus_level() { return ADSR2 >> 5; } + uint8 ADSR_sus_rate() { return ADSR2 & 31; } + + void AdjustEnvelope() { + if(env_state == SILENCE) { + env_mode = DIRECT; + env_rate = 0; + envx = 0; + } else if(env_state == RELEASE) { + env_mode = RELEASE_DEC; + env_rate = 0x7800; + } else if(ADSR_enabled()) { + switch(env_state) { + case ATTACK: + env_rate = rate_table[(ADSR_attack() << 1) + 1]; + env_mode = (env_rate == 0x7800) ? FAST_ATTACK : LINEAR_INC; + break; + case DECAY: + env_rate = rate_table[(ADSR_decay() << 1) + 0x10]; + env_mode = EXP_DEC; + break; + case SUSTAIN: + env_rate = rate_table[ADSR_sus_rate()]; + env_mode = (env_rate == 0) ? DIRECT : EXP_DEC; + break; + } + } else if(GAIN & 0x80) { + switch(GAIN & 0x60) { + case 0x00: env_mode = LINEAR_DEC; break; + case 0x20: env_mode = EXP_DEC; break; + case 0x40: env_mode = LINEAR_INC; break; + case 0x60: env_mode = BENT_INC; break; + } + env_rate = rate_table[GAIN & 0x1f]; + } else { + env_mode = DIRECT; + env_rate = 0; + envx = (GAIN & 0x7f) << 4; + } + } +} voice[8]; + +public: + void enter(); + void run(); + + uint8 read (uint8 addr); + void write(uint8 addr, uint8 data); + + void power(); + void reset(); + + aDSP(); + ~aDSP(); +}; diff --git a/bsnes/dsp/adsp/adsp_tables.cpp b/bsnes/dsp/adsp/adsp_tables.cpp new file mode 100755 index 0000000..e9f1b7e --- /dev/null +++ b/bsnes/dsp/adsp/adsp_tables.cpp @@ -0,0 +1,77 @@ +#ifdef ADSP_CPP + +const uint16 aDSP::rate_table[32] = { + 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C, + 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180, + 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00, + 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800 +}; + +const int16 aDSP::gaussian_table[512] = { + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x005, 0x005, 0x005, 0x005, + 0x006, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007, 0x008, + 0x008, 0x008, 0x009, 0x009, 0x009, 0x00A, 0x00A, 0x00A, + 0x00B, 0x00B, 0x00B, 0x00C, 0x00C, 0x00D, 0x00D, 0x00E, + 0x00E, 0x00F, 0x00F, 0x00F, 0x010, 0x010, 0x011, 0x011, + 0x012, 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x016, + 0x017, 0x017, 0x018, 0x018, 0x019, 0x01A, 0x01B, 0x01B, + 0x01C, 0x01D, 0x01D, 0x01E, 0x01F, 0x020, 0x020, 0x021, + 0x022, 0x023, 0x024, 0x024, 0x025, 0x026, 0x027, 0x028, + 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, 0x030, + 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, + 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x040, 0x041, 0x042, + 0x043, 0x045, 0x046, 0x047, 0x049, 0x04A, 0x04C, 0x04D, + 0x04E, 0x050, 0x051, 0x053, 0x054, 0x056, 0x057, 0x059, + 0x05A, 0x05C, 0x05E, 0x05F, 0x061, 0x063, 0x064, 0x066, + 0x068, 0x06A, 0x06B, 0x06D, 0x06F, 0x071, 0x073, 0x075, + 0x076, 0x078, 0x07A, 0x07C, 0x07E, 0x080, 0x082, 0x084, + 0x086, 0x089, 0x08B, 0x08D, 0x08F, 0x091, 0x093, 0x096, + 0x098, 0x09A, 0x09C, 0x09F, 0x0A1, 0x0A3, 0x0A6, 0x0A8, + 0x0AB, 0x0AD, 0x0AF, 0x0B2, 0x0B4, 0x0B7, 0x0BA, 0x0BC, + 0x0BF, 0x0C1, 0x0C4, 0x0C7, 0x0C9, 0x0CC, 0x0CF, 0x0D2, + 0x0D4, 0x0D7, 0x0DA, 0x0DD, 0x0E0, 0x0E3, 0x0E6, 0x0E9, + 0x0EC, 0x0EF, 0x0F2, 0x0F5, 0x0F8, 0x0FB, 0x0FE, 0x101, + 0x104, 0x107, 0x10B, 0x10E, 0x111, 0x114, 0x118, 0x11B, + 0x11E, 0x122, 0x125, 0x129, 0x12C, 0x130, 0x133, 0x137, + 0x13A, 0x13E, 0x141, 0x145, 0x148, 0x14C, 0x150, 0x153, + 0x157, 0x15B, 0x15F, 0x162, 0x166, 0x16A, 0x16E, 0x172, + 0x176, 0x17A, 0x17D, 0x181, 0x185, 0x189, 0x18D, 0x191, + 0x195, 0x19A, 0x19E, 0x1A2, 0x1A6, 0x1AA, 0x1AE, 0x1B2, + 0x1B7, 0x1BB, 0x1BF, 0x1C3, 0x1C8, 0x1CC, 0x1D0, 0x1D5, + 0x1D9, 0x1DD, 0x1E2, 0x1E6, 0x1EB, 0x1EF, 0x1F3, 0x1F8, + 0x1FC, 0x201, 0x205, 0x20A, 0x20F, 0x213, 0x218, 0x21C, + 0x221, 0x226, 0x22A, 0x22F, 0x233, 0x238, 0x23D, 0x241, + 0x246, 0x24B, 0x250, 0x254, 0x259, 0x25E, 0x263, 0x267, + 0x26C, 0x271, 0x276, 0x27B, 0x280, 0x284, 0x289, 0x28E, + 0x293, 0x298, 0x29D, 0x2A2, 0x2A6, 0x2AB, 0x2B0, 0x2B5, + 0x2BA, 0x2BF, 0x2C4, 0x2C9, 0x2CE, 0x2D3, 0x2D8, 0x2DC, + 0x2E1, 0x2E6, 0x2EB, 0x2F0, 0x2F5, 0x2FA, 0x2FF, 0x304, + 0x309, 0x30E, 0x313, 0x318, 0x31D, 0x322, 0x326, 0x32B, + 0x330, 0x335, 0x33A, 0x33F, 0x344, 0x349, 0x34E, 0x353, + 0x357, 0x35C, 0x361, 0x366, 0x36B, 0x370, 0x374, 0x379, + 0x37E, 0x383, 0x388, 0x38C, 0x391, 0x396, 0x39B, 0x39F, + 0x3A4, 0x3A9, 0x3AD, 0x3B2, 0x3B7, 0x3BB, 0x3C0, 0x3C5, + 0x3C9, 0x3CE, 0x3D2, 0x3D7, 0x3DC, 0x3E0, 0x3E5, 0x3E9, + 0x3ED, 0x3F2, 0x3F6, 0x3FB, 0x3FF, 0x403, 0x408, 0x40C, + 0x410, 0x415, 0x419, 0x41D, 0x421, 0x425, 0x42A, 0x42E, + 0x432, 0x436, 0x43A, 0x43E, 0x442, 0x446, 0x44A, 0x44E, + 0x452, 0x455, 0x459, 0x45D, 0x461, 0x465, 0x468, 0x46C, + 0x470, 0x473, 0x477, 0x47A, 0x47E, 0x481, 0x485, 0x488, + 0x48C, 0x48F, 0x492, 0x496, 0x499, 0x49C, 0x49F, 0x4A2, + 0x4A6, 0x4A9, 0x4AC, 0x4AF, 0x4B2, 0x4B5, 0x4B7, 0x4BA, + 0x4BD, 0x4C0, 0x4C3, 0x4C5, 0x4C8, 0x4CB, 0x4CD, 0x4D0, + 0x4D2, 0x4D5, 0x4D7, 0x4D9, 0x4DC, 0x4DE, 0x4E0, 0x4E3, + 0x4E5, 0x4E7, 0x4E9, 0x4EB, 0x4ED, 0x4EF, 0x4F1, 0x4F3, + 0x4F5, 0x4F6, 0x4F8, 0x4FA, 0x4FB, 0x4FD, 0x4FF, 0x500, + 0x502, 0x503, 0x504, 0x506, 0x507, 0x508, 0x50A, 0x50B, + 0x50C, 0x50D, 0x50E, 0x50F, 0x510, 0x511, 0x511, 0x512, + 0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517, + 0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519 +}; + +#endif //ifdef ADSP_CPP diff --git a/bsnes/dsp/dsp.hpp b/bsnes/dsp/dsp.hpp new file mode 100755 index 0000000..1e36b51 --- /dev/null +++ b/bsnes/dsp/dsp.hpp @@ -0,0 +1,13 @@ +class DSP { +public: + virtual void enter() = 0; + + virtual uint8 read(uint8 addr) = 0; + virtual void write(uint8 addr, uint8 data) = 0; + + virtual void power() = 0; + virtual void reset() = 0; + + DSP() {} + virtual ~DSP() {} +}; diff --git a/bsnes/dsp/sdsp/brr.cpp b/bsnes/dsp/sdsp/brr.cpp new file mode 100755 index 0000000..3e30b02 --- /dev/null +++ b/bsnes/dsp/sdsp/brr.cpp @@ -0,0 +1,62 @@ +#ifdef SDSP_CPP + +void sDSP::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 //ifdef SDSP_CPP diff --git a/bsnes/dsp/sdsp/counter.cpp b/bsnes/dsp/sdsp/counter.cpp new file mode 100755 index 0000000..97ac6fa --- /dev/null +++ b/bsnes/dsp/sdsp/counter.cpp @@ -0,0 +1,52 @@ +#ifdef SDSP_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 sDSP::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 sDSP::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 sDSP::counter_tick() { + state.counter--; + if(state.counter < 0) state.counter = counter_range - 1; +} + +//return true if counter event should trigger + +inline bool sDSP::counter_poll(unsigned rate) { + if(rate == 0) return false; + return (((unsigned)state.counter + counter_offset[rate]) % counter_rate[rate]) == 0; +} + +#endif //ifdef SDSP_CPP diff --git a/bsnes/dsp/sdsp/echo.cpp b/bsnes/dsp/sdsp/echo.cpp new file mode 100755 index 0000000..36e7d92 --- /dev/null +++ b/bsnes/dsp/sdsp/echo.cpp @@ -0,0 +1,135 @@ +#ifdef SDSP_CPP + +int sDSP::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 sDSP::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 sDSP::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 sDSP::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 sDSP::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 sDSP::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 sDSP::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 sDSP::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 sDSP::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 sDSP::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 + snes.audio.update(outl, outr); +} + +void sDSP::echo_28() { + state.t_echo_disabled = REG(flg); +} + +void sDSP::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 sDSP::echo_30() { + //write right echo + echo_write(1); +} + +#endif //ifdef SDSP_CPP diff --git a/bsnes/dsp/sdsp/envelope.cpp b/bsnes/dsp/sdsp/envelope.cpp new file mode 100755 index 0000000..4508b19 --- /dev/null +++ b/bsnes/dsp/sdsp/envelope.cpp @@ -0,0 +1,62 @@ +#ifdef SDSP_CPP + +void sDSP::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 //ifdef SDSP_CPP diff --git a/bsnes/dsp/sdsp/gaussian.cpp b/bsnes/dsp/sdsp/gaussian.cpp new file mode 100755 index 0000000..2e3b3a3 --- /dev/null +++ b/bsnes/dsp/sdsp/gaussian.cpp @@ -0,0 +1,54 @@ +#ifdef SDSP_CPP + +const int16 sDSP::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 sDSP::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 //ifdef SDSP_CPP diff --git a/bsnes/dsp/sdsp/misc.cpp b/bsnes/dsp/sdsp/misc.cpp new file mode 100755 index 0000000..2201c37 --- /dev/null +++ b/bsnes/dsp/sdsp/misc.cpp @@ -0,0 +1,35 @@ +#ifdef SDSP_CPP + +void sDSP::misc_27() { + state.t_pmon = REG(pmon) & ~1; //voice 0 doesn't support PMON +} + +void sDSP::misc_28() { + state.t_non = REG(non); + state.t_eon = REG(eon); + state.t_dir = REG(dir); +} + +void sDSP::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 sDSP::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 //ifdef SDSP_CPP diff --git a/bsnes/dsp/sdsp/sdsp.cpp b/bsnes/dsp/sdsp/sdsp.cpp new file mode 100755 index 0000000..d972f2d --- /dev/null +++ b/bsnes/dsp/sdsp/sdsp.cpp @@ -0,0 +1,326 @@ +/* + S-DSP emulator + license: LGPLv2 + + Note: this is basically a C++ cothreaded implementation of Shay Green's (blargg's) S-DSP emulator. + The actual algorithms, timing information, tables, variable names, etc were all from him. +*/ + +#include <../base.hpp> +#define SDSP_CPP + +#define REG(n) state.regs[r_##n] +#define VREG(n) state.regs[v.vidx + v_##n] + +#if !defined(USE_STATE_MACHINE) + #define phase_start() while(true) { + #define phase(n) + #define tick() scheduler.addclocks_dsp(3 * 8) + #define phase_end() } +#else + #define phase_start() switch(phase_index) { + #define phase(n) case n: + #define tick() scheduler.addclocks_dsp(3 * 8); break + #define phase_end() } phase_index = (phase_index + 1) & 31; +#endif + +#include "gaussian.cpp" +#include "counter.cpp" +#include "envelope.cpp" +#include "brr.cpp" +#include "misc.cpp" +#include "voice.cpp" +#include "echo.cpp" + +/* timing */ + +void sDSP::enter() { + phase_start() + + phase(0) + voice_5(voice[0]); + voice_2(voice[1]); + tick(); + + phase(1) + voice_6(voice[0]); + voice_3(voice[1]); + tick(); + + phase(2) + voice_7(voice[0]); + voice_4(voice[1]); + voice_1(voice[3]); + tick(); + + phase(3) + voice_8(voice[0]); + voice_5(voice[1]); + voice_2(voice[2]); + tick(); + + phase(4) + voice_9(voice[0]); + voice_6(voice[1]); + voice_3(voice[2]); + tick(); + + phase(5) + voice_7(voice[1]); + voice_4(voice[2]); + voice_1(voice[4]); + tick(); + + phase(6) + voice_8(voice[1]); + voice_5(voice[2]); + voice_2(voice[3]); + tick(); + + phase(7) + voice_9(voice[1]); + voice_6(voice[2]); + voice_3(voice[3]); + tick(); + + phase(8) + voice_7(voice[2]); + voice_4(voice[3]); + voice_1(voice[5]); + tick(); + + phase(9) + voice_8(voice[2]); + voice_5(voice[3]); + voice_2(voice[4]); + tick(); + + phase(10) + voice_9(voice[2]); + voice_6(voice[3]); + voice_3(voice[4]); + tick(); + + phase(11) + voice_7(voice[3]); + voice_4(voice[4]); + voice_1(voice[6]); + tick(); + + phase(12) + voice_8(voice[3]); + voice_5(voice[4]); + voice_2(voice[5]); + tick(); + + phase(13) + voice_9(voice[3]); + voice_6(voice[4]); + voice_3(voice[5]); + tick(); + + phase(14) + voice_7(voice[4]); + voice_4(voice[5]); + voice_1(voice[7]); + tick(); + + phase(15) + voice_8(voice[4]); + voice_5(voice[5]); + voice_2(voice[6]); + tick(); + + phase(16) + voice_9(voice[4]); + voice_6(voice[5]); + voice_3(voice[6]); + tick(); + + phase(17) + voice_1(voice[0]); + voice_7(voice[5]); + voice_4(voice[6]); + tick(); + + phase(18) + voice_8(voice[5]); + voice_5(voice[6]); + voice_2(voice[7]); + tick(); + + phase(19) + voice_9(voice[5]); + voice_6(voice[6]); + voice_3(voice[7]); + tick(); + + phase(20) + voice_1(voice[1]); + voice_7(voice[6]); + voice_4(voice[7]); + tick(); + + phase(21) + voice_8(voice[6]); + voice_5(voice[7]); + voice_2(voice[0]); + tick(); + + phase(22) + voice_3a(voice[0]); + voice_9(voice[6]); + voice_6(voice[7]); + echo_22(); + tick(); + + phase(23) + voice_7(voice[7]); + echo_23(); + tick(); + + phase(24) + voice_8(voice[7]); + echo_24(); + tick(); + + phase(25) + voice_3b(voice[0]); + voice_9(voice[7]); + echo_25(); + tick(); + + phase(26) + echo_26(); + tick(); + + phase(27) + misc_27(); + echo_27(); + tick(); + + phase(28) + misc_28(); + echo_28(); + tick(); + + phase(29) + misc_29(); + echo_29(); + tick(); + + phase(30) + misc_30(); + voice_3c(voice[0]); + echo_30(); + tick(); + + phase(31) + voice_4(voice[0]); + voice_1(voice[2]); + tick(); + + phase_end() +} + +/* register interface for S-SMP $00f2,$00f3 */ + +uint8 sDSP::read(uint8 addr) { + return state.regs[addr]; +} + +void sDSP::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 sDSP::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 sDSP::reset() { + REG(flg) = 0xe0; + + state.noise = 0x4000; + state.echo_hist_pos = 0; + state.every_other_sample = 1; + state.echo_offset = 0; + state.counter = 0; + + phase_index = 0; +} + +sDSP::sDSP() { + static_assert= 32 / 8>(); //int >= 32-bits + static_assert<(int8_t)0x80 == -0x80>(); //8-bit sign extension + static_assert<(int16_t)0x8000 == -0x8000>(); //16-bit sign extension + static_assert<(uint16_t)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); +} + +sDSP::~sDSP() { +} diff --git a/bsnes/dsp/sdsp/sdsp.hpp b/bsnes/dsp/sdsp/sdsp.hpp new file mode 100755 index 0000000..45c0848 --- /dev/null +++ b/bsnes/dsp/sdsp/sdsp.hpp @@ -0,0 +1,166 @@ +class sDSP : public DSP { +public: + void enter(); + + uint8 read(uint8 addr); + void write(uint8 addr, uint8 data); + + void power(); + void reset(); + + sDSP(); + ~sDSP(); + +private: + //USE_STATE_MACHINE variable + unsigned phase_index; + + //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 + env_mode_t env_mode; + int env; //current envelope level + int t_envx_out; + int hidden_env; //used by GAIN mode 7, very obscure quirk + } voice[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(); +}; diff --git a/bsnes/dsp/sdsp/voice.cpp b/bsnes/dsp/sdsp/voice.cpp new file mode 100755 index 0000000..c42ce94 --- /dev/null +++ b/bsnes/dsp/sdsp/voice.cpp @@ -0,0 +1,174 @@ +#ifdef SDSP_CPP + +inline void sDSP::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 sDSP::voice_1(voice_t &v) { + state.t_dir_addr = (state.t_dir << 8) + (state.t_srcn << 2); + state.t_srcn = VREG(srcn); +} + +void sDSP::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 sDSP::voice_3(voice_t &v) { + voice_3a(v); + voice_3b(v); + voice_3c(v); +} + +void sDSP::voice_3a(voice_t &v) { + state.t_pitch += (VREG(pitchh) & 0x3f) << 8; +} + +void sDSP::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 sDSP::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 sDSP::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 sDSP::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 sDSP::voice_6(voice_t &v) { + state.outx_buf = state.t_output >> 8; +} + +void sDSP::voice_7(voice_t &v) { + //update ENDX + REG(endx) = (uint8)state.endx_buf; + state.envx_buf = v.t_envx_out; +} + +void sDSP::voice_8(voice_t &v) { + //update OUTX + VREG(outx) = (uint8)state.outx_buf; +} + +void sDSP::voice_9(voice_t &v) { + //update ENVX + VREG(envx) = (uint8)state.envx_buf; +} + +#endif //ifdef SDSP_CPP diff --git a/bsnes/interface.hpp b/bsnes/interface.hpp new file mode 100755 index 0000000..09b75ff --- /dev/null +++ b/bsnes/interface.hpp @@ -0,0 +1,24 @@ +#include "cheat/cheat.hpp" + +#include "memory/memory.hpp" +#include "memory/smemory/smemory.hpp" + +#include "cpu/cpu.hpp" +#include "cpu/scpu/scpu.hpp" + +#include "ppu/ppu.hpp" +#include "ppu/bppu/bppu.hpp" + +#include "smp/smp.hpp" +#include "smp/ssmp/ssmp.hpp" + +#include "dsp/dsp.hpp" +#include "dsp/sdsp/sdsp.hpp" + +extern BUSCORE bus; +extern CPUCORE cpu; +extern SMPCORE smp; +extern DSPCORE dsp; +extern PPUCORE ppu; + +#include "snes/snes.hpp" diff --git a/bsnes/lib/libco/fiber.c b/bsnes/lib/libco/fiber.c new file mode 100755 index 0000000..02ef5bc --- /dev/null +++ b/bsnes/lib/libco/fiber.c @@ -0,0 +1,51 @@ +/* + libco.win (2008-01-28) + authors: Nach, byuu + license: public domain +*/ + +#define LIBCO_C +#include "libco.h" +#define WINVER 0x0400 +#define _WIN32_WINNT 0x0400 +#define WIN32_LEAN_AND_MEAN +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local cothread_t co_active_ = 0; + +static void __stdcall co_thunk(void *coentry) { + ((void (*)(void))coentry)(); +} + +cothread_t co_active() { + if(!co_active_) { + ConvertThreadToFiber(0); + co_active_ = GetCurrentFiber(); + } + return co_active_; +} + +cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { + if(!co_active_) { + ConvertThreadToFiber(0); + co_active_ = GetCurrentFiber(); + } + return (cothread_t)CreateFiber(heapsize, co_thunk, (void*)coentry); +} + +void co_delete(cothread_t cothread) { + DeleteFiber(cothread); +} + +void co_switch(cothread_t cothread) { + co_active_ = cothread; + SwitchToFiber(cothread); +} + +#ifdef __cplusplus +} +#endif diff --git a/bsnes/lib/libco/libco.c b/bsnes/lib/libco/libco.c new file mode 100755 index 0000000..604b37d --- /dev/null +++ b/bsnes/lib/libco/libco.c @@ -0,0 +1,21 @@ +/* + libco + auto-selection module + license: public domain +*/ + +#if defined(__GNUC__) && defined(__i386__) + #include "x86.c" +#elif defined(__GNUC__) && defined(__amd64__) && !defined(__MINGW64__) + #include "x86-64.c" +#elif defined(__MINGW64__) + #include "fiber.c" +#elif defined(__GNUC__) + #include "sjlj.c" +#elif defined(_MSC_VER) && defined(_M_IX86) + #include "x86.c" +#elif defined(_MSC_VER) && defined(_M_AMD64) + #include "fiber.c" +#else + #error "libco: unsupported processor, compiler or operating system" +#endif diff --git a/bsnes/lib/libco/libco.h b/bsnes/lib/libco/libco.h new file mode 100755 index 0000000..d8348c4 --- /dev/null +++ b/bsnes/lib/libco/libco.h @@ -0,0 +1,34 @@ +/* + libco + version: 0.13 rc2 (2008-01-28) + license: public domain +*/ + +#ifndef LIBCO_H +#define LIBCO_H + +#ifdef LIBCO_C + #ifdef LIBCO_MP + #define thread_local __thread + #else + #define thread_local + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* cothread_t; + +cothread_t co_active(); +cothread_t co_create(unsigned int, void (*)(void)); +void co_delete(cothread_t); +void co_switch(cothread_t); + +#ifdef __cplusplus +} +#endif + +/* ifndef LIBCO_H */ +#endif diff --git a/bsnes/lib/libco/ppc.s b/bsnes/lib/libco/ppc.s new file mode 100755 index 0000000..d7f6b75 --- /dev/null +++ b/bsnes/lib/libco/ppc.s @@ -0,0 +1,478 @@ +;***** +;libco.ppc (2007-11-29) +;author: Vas Crabb +;license: public domain +; +;cross-platform PowerPC implementation of libco +;special thanks to byuu for writing the original version +; +;[ABI compatibility] +;- gcc; mac os x; ppc +; +;[nonvolatile registers] +;- GPR1, GPR13 - GPR31 +;- FPR14 - FPR31 +;- V20 - V31 +;- VRSAVE, CR2 - CR4 +; +;[volatile registers] +;- GPR0, GPR2 - GPR12 +;- FPR0 - FPR13 +;- V0 - V19 +;- LR, CTR, XER, CR0, CR1, CR5 - CR7 +;***** + + +;Declare some target-specific stuff + + .section __TEXT,__text,regular,pure_instructions + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .machine ppc + + +;Constants + + .cstring + .align 2 + +_sysctl_altivec: + .ascii "hw.optional.altivec\0" + + +;Declare space for variables + +.lcomm _co_environ,4,2 ;bit 0 = initialised, bit 1 = have Altivec/VMX +.lcomm _co_primary_buffer,1024,2 ;buffer (will be zeroed by loader) + + .data + .align 2 + +_co_active_context: + .long _co_primary_buffer + + + .text + .align 2 + + +;Declare exported names + +.globl _co_active +.globl _co_create +.globl _co_delete +.globl _co_switch + + +;***** +;extern "C" cothread_t co_active(); +;return = GPR3 +;***** + +_co_active: + mflr r0 ;GPR0 = return address + bcl 20,31,L_co_active$spb +L_co_active$spb: + mflr r2 ;GPR2 set for position-independance + addis r3,r2,ha16(_co_active_context-L_co_active$spb) ;get value in GPR3 + lwz r3,lo16(_co_active_context-L_co_active$spb)(r3) + mtlr r0 ;LR = return address + blr ;return + + +;***** +;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)()); +;GPR3 = heapsize +;GPR4 = coentry +;return = GPR3 +;***** + +_co_create: + mflr r0 ;GPR0 = return address + stmw r30,-8(r1) ;save GPR30 and GPR31 + stw r0,8(r1) ;save return address + stwu r1,-(2*4+16+24)(r1) ;allocate 16 bytes for locals/parameters + +;create heap space (stack + register storage) + addi r31,r3,1024-24 ;subtract space for linkage + mr r30,r4 ;GPR30 = coentry + addi r3,r3,1024 ;allocate extra memory for contextual info + bl L_malloc$stub ;GPR3 = malloc(heapsize + 1024) + add r4,r3,r31 ;GPR4 points to top-of-stack + rlwinm r5,r4,0,0,27 ;force 16-byte alignment + +;store thread entry point + registers, so that first call to co_switch will execute coentry + stw r30,8(r5) ;store entry point + addi r6,0,2+19+18*2+12*4+1 ;clear for CR, old GPR1, 19 GPRs, 18 FPRs, 12 VRs, VRSAVE + addi r0,0,0 + addi r7,0,4 ;start at 4(GPR5) + mtctr r6 +L_co_create$clear_loop: + stwx r0,r5,r7 ;clear a word + addi r7,r7,-4 ;increment pointer + bdnz L_co_create$clear_loop ;loop + stwu r5,-448(r5) ;store top of stack + +;initialize context memory heap and return + stw r5,0(r3) ;*cothread_t = stack heap pointer (GPR1) + lwz r1,0(r1) ;deallocate stack frame + lwz r8,8(r1) ;fetch return address + lmw r30,-8(r1) ;restore GPR30 and GPR31 + mtlr r8 ;return address in LR + blr ;return + + +;***** +;extern "C" void co_delete(cothread_t cothread); +;GPR3 = cothread +;***** + +_co_delete: + b L_free$stub ;free(GPR3) + + +;***** +;extern "C" void co_switch(cothread_t cothread); +;GPR3 = cothread +;***** +; +;Frame looks like: +; +;Old New Value +; 8(r1) 456(r1) Saved LR +; 4(r1) 452(r1) Saved CR +; 0(r1) 448(r1) Old GPR1 +; -4(r1) 444(r1) Saved GPR31 +; -8(r1) 440(r1) Saved GPR30 +;... ... ... +; -72(r1) 376(r1) Saved GPR14 +; -76(r1) 372(r1) Saved GPR13 +; -80(r1) 368(r1) Saved VRSAVE +; -84(r1) 364(r1) +++ +; -88(r1) 360(r1) Saved FPR31 +; -92(r1) 356(r1) +++ +; -96(r1) 352(r1) Saved FPR30 +;... ... ... +;-212(r1) 236(r1) +++ +;-216(r1) 232(r1) Saved FPR15 +;-220(r1) 228(r1) +++ +;-224(r1) 224(r1) Saved FPR14 +;-228(r1) 220(r1) +++ value +;-232(r1) 216(r1) +++ len +;-236(r1) 212(r1) +++ +;-240(r1) 208(r1) Saved VR31 +;-244(r1) 204(r1) +++ +;-248(r1) 200(r1) +++ +;-252(r1) 196(r1) +++ +;-256(r1) 192(r1) Saved VR30 +;... ... ... +;-388(r1) 60(r1) +++ +;-392(r1) 56(r1) +++ +;-396(r1) 52(r1) +++ +;-400(r1) 48(r1) Saved VR21 +;-404(r1) 44(r1) +++ +;-408(r1) 40(r1) +++ Param 5 (GPR7) +;-412(r1) 36(r1) +++ Param 4 (GPR6) +;-416(r1) 32(r1) Saved VR20 Param 3 (GPR5) +;-420(r1) 28(r1) - Param 2 (GPR4) +;-424(r1) 24(r1) - Param 1 (GPR3) +;-428(r1) 20(r1) - Reserved +;-432(r1) 16(r1) - Reserved +;-436(r1) 12(r1) - Reserved +;-440(r1) 8(r1) - New LR +;-444(r1) 4(r1) - New CR +;-448(r1) 0(r1) Saved GPR1 + + +_co_switch: + stmw r13,-76(r1) ;save preserved GPRs + stfd f14,-224(r1) ;save preserved FPRs + stfd f15,-216(r1) + stfd f16,-208(r1) + stfd f17,-200(r1) + stfd f18,-192(r1) + stfd f19,-184(r1) + stfd f20,-176(r1) + stfd f21,-168(r1) + stfd f22,-160(r1) + stfd f23,-152(r1) + stfd f24,-144(r1) + stfd f25,-136(r1) + stfd f26,-128(r1) + stfd f27,-120(r1) + stfd f28,-112(r1) + stfd f29,-104(r1) + stfd f30,-96(r1) + stfd f31,-88(r1) + mflr r0 ;save return address + stw r0,8(r1) + mfcr r2 ;save condition codes + stw r2,4(r1) + stwu r1,-448(r1) ;create stack frame (save 19 GPRs, 18 FRPs, 12 VRs, VRSAVE) + + mr r30,r3 ;save new context pointer + bcl 20,31,L_co_switch$spb ;get address of co_active_context +L_co_switch$spb: + mflr r31 + + addis r29,r31,ha16(_co_environ-L_co_switch$spb) ;get environment flags + lwz r8,lo16(_co_environ-L_co_switch$spb)(r29) + andis. r9,r8,0x8000 ;is it initialised? + bne+ L_co_switch$initialised + + addi r0,0,4 ;len = sizeof(int) + stw r0,216(r1) + addis r3,r31,ha16(_sysctl_altivec-L_co_switch$spb) ;GPR3 = "hw.optional.altivec" + addi r3,r3,lo16(_sysctl_altivec-L_co_switch$spb) + addi r4,r1,220 ;GPR4 = &value + addi r5,r1,216 ;GPR5 = &len + addi r6,0,0 ;newp = 0 + addi r7,0,0 ;newlen = 0 + bl L_sysctlbyname$stub ;call sysctlbyname + lwz r2,220(r1) ;fetch result + addis r8,0,0x8000 ;set initialised bit + cmpwi cr5,r3,0 ;assume error means not present + cmpwi cr6,r2,0 ;test result + blt- cr5,L_co_switch$store_environ + beq cr6,L_co_switch$store_environ + oris r8,r8,0x4000 ;set the flag to say we have it! +L_co_switch$store_environ: + stw r8,lo16(_co_environ-L_co_switch$spb)(r29) ;store environment flags +L_co_switch$initialised: + + andis. r10,r8,0x4000 ;do we have Altivec/VMX? + beq L_co_switch$save_no_vmx + mfspr r11,256 ;save VRSAVE + andi. r0,r11,0x0FFF ;short-circuit if it's zero + stw r11,368(r1) + beq L_co_switch$save_no_vmx + andi. r0,r11,0x0800 ;check bit 20 + addi r2,0,32 ;starting index + beq L_co_switch$save_skip_vr20 + stvx v20,r1,r2 ;save VR20 +L_co_switch$save_skip_vr20: + addi r2,r2,16 ;stride + andi. r0,r11,0x0400 ;check bit 21 + beq L_co_switch$save_skip_vr21 + stvx v21,r1,r2 ;save VR21 +L_co_switch$save_skip_vr21: + addi r2,r2,16 ;stride + andi. r0,r11,0x0200 ;check bit 22 + beq L_co_switch$save_skip_vr22 + stvx v22,r1,r2 ;save VR22 +L_co_switch$save_skip_vr22: + addi r2,r2,16 ;stride + andi. r0,r11,0x0100 ;check bit 23 + beq L_co_switch$save_skip_vr23 + stvx v23,r1,r2 ;save VR23 +L_co_switch$save_skip_vr23: + addi r2,r2,16 ;stride + andi. r0,r11,0x0080 ;check bit 24 + beq L_co_switch$save_skip_vr24 + stvx v24,r1,r2 ;save VR24 +L_co_switch$save_skip_vr24: + addi r2,r2,16 ;stride + andi. r0,r11,0x0040 ;check bit 25 + beq L_co_switch$save_skip_vr25 + stvx v25,r1,r2 ;save VR25 +L_co_switch$save_skip_vr25: + addi r2,r2,16 ;stride + andi. r0,r11,0x0020 ;check bit 26 + beq L_co_switch$save_skip_vr26 + stvx v26,r1,r2 ;save VR26 +L_co_switch$save_skip_vr26: + addi r2,r2,16 ;stride + andi. r0,r11,0x0010 ;check bit 27 + beq L_co_switch$save_skip_vr27 + stvx v27,r1,r2 ;save VR27 +L_co_switch$save_skip_vr27: + addi r2,r2,16 ;stride + andi. r0,r11,0x0008 ;check bit 28 + beq L_co_switch$save_skip_vr28 + stvx v28,r1,r2 ;save VR28 +L_co_switch$save_skip_vr28: + addi r2,r2,16 ;stride + andi. r0,r11,0x0004 ;check bit 29 + beq L_co_switch$save_skip_vr29 + stvx v29,r1,r2 ;save VR29 +L_co_switch$save_skip_vr29: + addi r2,r2,16 ;stride + andi. r0,r11,0x0002 ;check bit 30 + beq L_co_switch$save_skip_vr30 + stvx v30,r1,r2 ;save VR30 +L_co_switch$save_skip_vr30: + addi r2,r2,16 ;stride + andi. r0,r11,0x0001 ;check bit 31 + beq L_co_switch$save_skip_vr31 + stvx v31,r1,r2 ;save VR31 +L_co_switch$save_skip_vr31: +L_co_switch$save_no_vmx: + + addis r4,r31,ha16(_co_active_context-L_co_switch$spb) ;save current context + lwz r5,lo16(_co_active_context-L_co_switch$spb)(r4) + stw r30,lo16(_co_active_context-L_co_switch$spb)(r4);set new context + stw r1,0(r5) ;save current stack pointer + lwz r1,0(r30) ;get new stack pointer + + andis. r10,r8,0x4000 ;do we have Altivec/VMX? + beq L_co_switch$restore_no_vmx + lwz r11,368(r1) ;restore VRSAVE + andi. r0,r11,0x0FFF ;short-circuit if it's zero + mtspr 256,r11 + beq L_co_switch$restore_no_vmx + andi. r0,r11,0x0800 ;check bit 20 + addi r2,0,32 ;starting index + beq L_co_switch$restore_skip_vr20 + lvx v20,r1,r2 ;restore VR20 +L_co_switch$restore_skip_vr20: + addi r2,r2,16 ;stride + andi. r0,r11,0x0400 ;check bit 21 + beq L_co_switch$restore_skip_vr21 + lvx v21,r1,r2 ;restore VR21 +L_co_switch$restore_skip_vr21: + addi r2,r2,16 ;stride + andi. r0,r11,0x0200 ;check bit 22 + beq L_co_switch$restore_skip_vr22 + lvx v22,r1,r2 ;restore VR22 +L_co_switch$restore_skip_vr22: + addi r2,r2,16 ;stride + andi. r0,r11,0x0100 ;check bit 23 + beq L_co_switch$restore_skip_vr23 + lvx v23,r1,r2 ;restore VR23 +L_co_switch$restore_skip_vr23: + addi r2,r2,16 ;stride + andi. r0,r11,0x0080 ;check bit 24 + beq L_co_switch$restore_skip_vr24 + lvx v24,r1,r2 ;restore VR24 +L_co_switch$restore_skip_vr24: + addi r2,r2,16 ;stride + andi. r0,r11,0x0040 ;check bit 25 + beq L_co_switch$restore_skip_vr25 + lvx v25,r1,r2 ;restore VR25 +L_co_switch$restore_skip_vr25: + addi r2,r2,16 ;stride + andi. r0,r11,0x0020 ;check bit 26 + beq L_co_switch$restore_skip_vr26 + lvx v26,r1,r2 ;restore VR26 +L_co_switch$restore_skip_vr26: + addi r2,r2,16 ;stride + andi. r0,r11,0x0010 ;check bit 27 + beq L_co_switch$restore_skip_vr27 + lvx v27,r1,r2 ;restore VR27 +L_co_switch$restore_skip_vr27: + addi r2,r2,16 ;stride + andi. r0,r11,0x0008 ;check bit 28 + beq L_co_switch$restore_skip_vr28 + lvx v28,r1,r2 ;restore VR28 +L_co_switch$restore_skip_vr28: + addi r2,r2,16 ;stride + andi. r0,r11,0x0004 ;check bit 29 + beq L_co_switch$restore_skip_vr29 + lvx v29,r1,r2 ;restore VR29 +L_co_switch$restore_skip_vr29: + addi r2,r2,16 ;stride + andi. r0,r11,0x0002 ;check bit 30 + beq L_co_switch$restore_skip_vr30 + lvx v30,r1,r2 ;restore VR30 +L_co_switch$restore_skip_vr30: + addi r2,r2,16 ;stride + andi. r0,r11,0x0001 ;check bit 31 + beq L_co_switch$restore_skip_vr31 + lvx v31,r1,r2 ;restore VR31 +L_co_switch$restore_skip_vr31: +L_co_switch$restore_no_vmx: + + lwz r1,0(r1) ;deallocate stack frame + lwz r6,8(r1) ;return address in GPR6 + lwz r7,4(r1) ;condition codes in GPR7 + addi r0,0,0 ;make thread main crash if it returns + lmw r13,-76(r1) ;restore preserved GPRs + lfd f14,-224(r1) ;restore preserved FPRs + lfd f15,-216(r1) + lfd f16,-208(r1) + lfd f17,-200(r1) + lfd f18,-192(r1) + lfd f19,-184(r1) + lfd f20,-176(r1) + lfd f21,-168(r1) + lfd f22,-160(r1) + lfd f23,-152(r1) + lfd f24,-144(r1) + lfd f25,-136(r1) + lfd f26,-128(r1) + lfd f27,-120(r1) + lfd f28,-112(r1) + lfd f29,-104(r1) + lfd f30,-96(r1) + lfd f31,-88(r1) + mtlr r0 + mtctr r6 ;restore return address + mtcrf 32,r7 ;restore preserved condition codes + mtcrf 16,r7 + mtcrf 8,r7 + bctr ;return + + + +;Import external functions + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_malloc$stub: + .indirect_symbol _malloc + mflr r0 + bcl 20,31,L_malloc$spb +L_malloc$spb: + mflr r11 + addis r11,r11,ha16(L_malloc$lazy_ptr-L_malloc$spb) + mtlr r0 + lwzu r12,lo16(L_malloc$lazy_ptr-L_malloc$spb)(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_malloc$lazy_ptr: + .indirect_symbol _malloc + .long dyld_stub_binding_helper + + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_free$stub: + .indirect_symbol _free + mflr r0 + bcl 20,31,L_free$spb +L_free$spb: + mflr r11 + addis r11,r11,ha16(L_free$lazy_ptr-L_free$spb) + mtlr r0 + lwzu r12,lo16(L_free$lazy_ptr-L_free$spb)(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_free$lazy_ptr: + .indirect_symbol _free + .long dyld_stub_binding_helper + + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_sysctlbyname$stub: + .indirect_symbol _sysctlbyname + mflr r0 + bcl 20,31,L_sysctlbyname$spb +L_sysctlbyname$spb: + mflr r11 + addis r11,r11,ha16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb) + mtlr r0 + lwzu r12,lo16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb)(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_sysctlbyname$lazy_ptr: + .indirect_symbol _sysctlbyname + .long dyld_stub_binding_helper + + +;This needs to be here! + + .subsections_via_symbols + diff --git a/bsnes/lib/libco/ppc64.s b/bsnes/lib/libco/ppc64.s new file mode 100755 index 0000000..2fb048d --- /dev/null +++ b/bsnes/lib/libco/ppc64.s @@ -0,0 +1,513 @@ +;***** +;libco.ppc64 (2007-12-05) +;author: Vas Crabb +;license: public domain +; +;cross-platform 64-bit PowerPC implementation of libco +;special thanks to byuu for writing the original version +; +;[ABI compatibility] +;- gcc; mac os x; ppc64 +; +;[nonvolatile registers] +;- GPR1, GPR13 - GPR31 +;- FPR14 - FPR31 +;- V20 - V31 +;- VRSAVE, CR2 - CR4 +; +;[volatile registers] +;- GPR0, GPR2 - GPR12 +;- FPR0 - FPR13 +;- V0 - V19 +;- LR, CTR, XER, CR0, CR1, CR5 - CR7 +;***** + + +;Declare some target-specific stuff + + .section __TEXT,__text,regular,pure_instructions + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .machine ppc64 + + +;Constants + + .cstring + .align 3 + +_sysctl_altivec: + .ascii "hw.optional.altivec\0" + + +;Declare space for variables + +.lcomm _co_environ,4,2 ;bit 0 = initialised, bit 1 = have Altivec/VMX +.lcomm _co_primary_buffer,1024,3 ;buffer (will be zeroed by loader) + + .data + .align 3 + +_co_active_context: + .quad _co_primary_buffer + + + .text + .align 2 + + +;Declare exported names + +.globl _co_active +.globl _co_create +.globl _co_delete +.globl _co_switch + + +;***** +;extern "C" cothread_t co_active(); +;return = GPR3 +;***** + +_co_active: + mflr r0 ;GPR0 = return address + bcl 20,31,L_co_active$spb +L_co_active$spb: + mflr r2 ;GPR2 set for position-independance + addis r3,r2,ha16(_co_active_context-L_co_active$spb) ;get value in GPR3 + ld r3,lo16(_co_active_context-L_co_active$spb)(r3) + mtlr r0 ;LR = return address + blr ;return + + +;***** +;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)()); +;GPR3 = heapsize +;GPR4 = coentry +;return = GPR3 +;***** + +_co_create: + mflr r0 ;GPR0 = return address + std r30,-16(r1) ;save GPR30 and GPR31 + std r31,-8(r1) + std r0,16(r1) ;save return address + stdu r1,-(2*8+16+48)(r1) ;allocate 16 bytes for locals/parameters + +;create heap space (stack + register storage) + addi r31,r3,1024-48 ;subtract space for linkage + mr r30,r4 ;GPR30 = coentry + addi r3,r3,1024 ;allocate extra memory for contextual info + bl L_malloc$stub ;GPR3 = malloc(heapsize + 1024) + add r4,r3,r31 ;GPR4 points to top-of-stack + rldicr r5,r4,0,59 ;force 16-byte alignment + +;store thread entry point + registers, so that first call to co_switch will execute coentry + std r30,16(r5) ;store entry point + addi r6,0,2+19+18+12*2+1 ;clear for CR, old GPR1, 19 GPRs, 18 FPRs, 12 VRs, VRSAVE + addi r0,0,0 + addi r7,0,8 ;start at 8(GPR5) + mtctr r6 +L_co_create$clear_loop: + stdx r0,r5,r7 ;clear a double + addi r7,r7,-8 ;increment pointer + bdnz L_co_create$clear_loop ;loop + stdu r5,-544(r5) ;store top of stack + +;initialize context memory heap and return + addis r9,0,0x8000 ;GPR13 not set (system TLS) + std r5,0(r3) ;*cothread_t = stack heap pointer (GPR1) + stw r9,8(r3) ;this is a flag word + ld r1,0(r1) ;deallocate stack frame + ld r8,16(r1) ;fetch return address + ld r30,-16(r1) ;restore GPR30 and GPR31 + ld r31,-8(r1) + mtlr r8 ;return address in LR + blr ;return + + +;***** +;extern "C" void co_delete(cothread_t cothread); +;GPR3 = cothread +;***** + +_co_delete: + b L_free$stub ;free(GPR3) + + +;***** +;extern "C" void co_switch(cothread_t cothread); +;GPR3 = cothread +;***** +; +;Frame looks like: +; +;Old New Value +; 16(r1) 560(r1) Saved LR +; 8(r1) 552(r1) Saved CR +; 0(r1) 544(r1) Old GPR1 +; -8(r1) 536(r1) Saved GPR31 +; -16(r1) 528(r1) Saved GPR30 +;... ... ... +;-144(r1) 400(r1) Saved GPR14 +;-152(r1) 392(r1) Saved GPR13 +;-160(r1) 384(r1) Saved FPR31 +;-168(r1) 376(r1) Saved FPR30 +;... ... ... +;-288(r1) 256(r1) Saved FPR15 +;-296(r1) 248(r1) Saved FPR14 +;-304(r1) 240(r1) Saved VRSAVE +;-312(r1) 232(r1) +++ value +;-320(r1) 224(r1) Saved VR31 len +;-328(r1) 216(r1) +++ +;-336(r1) 208(r1) Saved VR30 +;... ... ... +;-456(r1) 88(r1) +++ +;-464(r1) 80(r1) Saved VR22 Param 5 (GPR7) +;-472(r1) 72(r1) +++ Param 4 (GPR6) +;-480(r1) 64(r1) Saved VR21 Param 3 (GPR5) +;-488(r1) 56(r1) +++ Param 2 (GPR4) +;-496(r1) 48(r1) Saved VR20 Param 1 (GPR3) +;-504(r1) 40(r1) - Reserved +;-512(r1) 32(r1) - Reserved +;-520(r1) 24(r1) - Reserved +;-528(r1) 16(r1) - New LR +;-536(r1) 8(r1) - New CR +;-544(r1) 0(r1) Saved GPR1 + + +_co_switch: + std r13,-152(r1) ;save preserved GPRs + std r14,-144(r1) + std r15,-136(r1) + std r16,-128(r1) + std r17,-120(r1) + std r18,-112(r1) + std r19,-104(r1) + std r20,-96(r1) + std r21,-88(r1) + std r22,-80(r1) + std r23,-72(r1) + std r24,-64(r1) + std r25,-56(r1) + std r26,-48(r1) + std r27,-40(r1) + std r28,-32(r1) + std r29,-24(r1) + std r30,-16(r1) + std r31,-8(r1) + mflr r0 ;save return address + std r0,16(r1) + mfcr r2 ;save condition codes + stw r2,8(r1) + stdu r1,-544(r1) ;create stack frame (save 19 GPRs, 18 FRPs, 12 VRs, VRSAVE) + stfd f14,248(r1) ;save preserved FPRs + stfd f15,256(r1) + stfd f16,264(r1) + stfd f17,272(r1) + stfd f18,280(r1) + stfd f19,288(r1) + stfd f20,296(r1) + stfd f21,304(r1) + stfd f22,312(r1) + stfd f23,320(r1) + stfd f24,328(r1) + stfd f25,336(r1) + stfd f26,344(r1) + stfd f27,352(r1) + stfd f28,360(r1) + stfd f29,368(r1) + stfd f30,376(r1) + stfd f31,384(r1) + + mr r30,r3 ;save new context pointer + bcl 20,31,L_co_switch$spb ;get address of co_active_context +L_co_switch$spb: + mflr r31 + + addis r29,r31,ha16(_co_environ-L_co_switch$spb) ;get environment flags + lwz r8,lo16(_co_environ-L_co_switch$spb)(r29) + andis. r9,r8,0x8000 ;is it initialised? + bne+ L_co_switch$initialised + + addi r0,0,4 ;len = sizeof(int) + std r0,224(r1) + addis r3,r31,ha16(_sysctl_altivec-L_co_switch$spb) ;GPR3 = "hw.optional.altivec" + addi r3,r3,lo16(_sysctl_altivec-L_co_switch$spb) + addi r4,r1,232 ;GPR4 = &value + addi r5,r1,224 ;GPR5 = &len + addi r6,0,0 ;newp = 0 + addi r7,0,0 ;newlen = 0 + bl L_sysctlbyname$stub ;call sysctlbyname + lwz r2,232(r1) ;fetch result + addis r8,0,0x8000 ;set initialised bit + cmpdi cr5,r3,0 ;assume error means not present + cmpwi cr6,r2,0 ;test result + blt- cr5,L_co_switch$store_environ + beq cr6,L_co_switch$store_environ + oris r8,r8,0x4000 ;set the flag to say we have it! +L_co_switch$store_environ: + stw r8,lo16(_co_environ-L_co_switch$spb)(r29) ;store environment flags +L_co_switch$initialised: + + andis. r10,r8,0x4000 ;do we have Altivec/VMX? + beq L_co_switch$save_no_vmx + mfspr r11,256 ;save VRSAVE + andi. r0,r11,0x0FFF ;short-circuit if it's zero + stw r11,240(r1) + beq L_co_switch$save_no_vmx + andi. r0,r11,0x0800 ;check bit 20 + addi r2,0,48 ;starting index + beq L_co_switch$save_skip_vr20 + stvx v20,r1,r2 ;save VR20 +L_co_switch$save_skip_vr20: + addi r2,r2,16 ;stride + andi. r0,r11,0x0400 ;check bit 21 + beq L_co_switch$save_skip_vr21 + stvx v21,r1,r2 ;save VR21 +L_co_switch$save_skip_vr21: + addi r2,r2,16 ;stride + andi. r0,r11,0x0200 ;check bit 22 + beq L_co_switch$save_skip_vr22 + stvx v22,r1,r2 ;save VR22 +L_co_switch$save_skip_vr22: + addi r2,r2,16 ;stride + andi. r0,r11,0x0100 ;check bit 23 + beq L_co_switch$save_skip_vr23 + stvx v23,r1,r2 ;save VR23 +L_co_switch$save_skip_vr23: + addi r2,r2,16 ;stride + andi. r0,r11,0x0080 ;check bit 24 + beq L_co_switch$save_skip_vr24 + stvx v24,r1,r2 ;save VR24 +L_co_switch$save_skip_vr24: + addi r2,r2,16 ;stride + andi. r0,r11,0x0040 ;check bit 25 + beq L_co_switch$save_skip_vr25 + stvx v25,r1,r2 ;save VR25 +L_co_switch$save_skip_vr25: + addi r2,r2,16 ;stride + andi. r0,r11,0x0020 ;check bit 26 + beq L_co_switch$save_skip_vr26 + stvx v26,r1,r2 ;save VR26 +L_co_switch$save_skip_vr26: + addi r2,r2,16 ;stride + andi. r0,r11,0x0010 ;check bit 27 + beq L_co_switch$save_skip_vr27 + stvx v27,r1,r2 ;save VR27 +L_co_switch$save_skip_vr27: + addi r2,r2,16 ;stride + andi. r0,r11,0x0008 ;check bit 28 + beq L_co_switch$save_skip_vr28 + stvx v28,r1,r2 ;save VR28 +L_co_switch$save_skip_vr28: + addi r2,r2,16 ;stride + andi. r0,r11,0x0004 ;check bit 29 + beq L_co_switch$save_skip_vr29 + stvx v29,r1,r2 ;save VR29 +L_co_switch$save_skip_vr29: + addi r2,r2,16 ;stride + andi. r0,r11,0x0002 ;check bit 30 + beq L_co_switch$save_skip_vr30 + stvx v30,r1,r2 ;save VR30 +L_co_switch$save_skip_vr30: + addi r2,r2,16 ;stride + andi. r0,r11,0x0001 ;check bit 31 + beq L_co_switch$save_skip_vr31 + stvx v31,r1,r2 ;save VR31 +L_co_switch$save_skip_vr31: +L_co_switch$save_no_vmx: + + addis r4,r31,ha16(_co_active_context-L_co_switch$spb) ;save current context + ld r5,lo16(_co_active_context-L_co_switch$spb)(r4) + std r30,lo16(_co_active_context-L_co_switch$spb)(r4);set new context + std r1,0(r5) ;save current stack pointer + ld r1,0(r30) ;get new stack pointer + lwz r12,8(r30) ;have we already set GPR13 (system TLS)? + andis. r0,r12,0x8000 + beq+ L_co_switch$gpr13_set + std r13,392(r1) + xoris r12,r12,0x8000 + stw r12,8(r30) +L_co_switch$gpr13_set: + + andis. r10,r8,0x4000 ;do we have Altivec/VMX? + beq L_co_switch$restore_no_vmx + lwz r11,240(r1) ;restore VRSAVE + andi. r0,r11,0x0FFF ;short-circuit if it's zero + mtspr 256,r11 + beq L_co_switch$restore_no_vmx + andi. r0,r11,0x0800 ;check bit 20 + addi r2,0,48 ;starting index + beq L_co_switch$restore_skip_vr20 + lvx v20,r1,r2 ;restore VR20 +L_co_switch$restore_skip_vr20: + addi r2,r2,16 ;stride + andi. r0,r11,0x0400 ;check bit 21 + beq L_co_switch$restore_skip_vr21 + lvx v21,r1,r2 ;restore VR21 +L_co_switch$restore_skip_vr21: + addi r2,r2,16 ;stride + andi. r0,r11,0x0200 ;check bit 22 + beq L_co_switch$restore_skip_vr22 + lvx v22,r1,r2 ;restore VR22 +L_co_switch$restore_skip_vr22: + addi r2,r2,16 ;stride + andi. r0,r11,0x0100 ;check bit 23 + beq L_co_switch$restore_skip_vr23 + lvx v23,r1,r2 ;restore VR23 +L_co_switch$restore_skip_vr23: + addi r2,r2,16 ;stride + andi. r0,r11,0x0080 ;check bit 24 + beq L_co_switch$restore_skip_vr24 + lvx v24,r1,r2 ;restore VR24 +L_co_switch$restore_skip_vr24: + addi r2,r2,16 ;stride + andi. r0,r11,0x0040 ;check bit 25 + beq L_co_switch$restore_skip_vr25 + lvx v25,r1,r2 ;restore VR25 +L_co_switch$restore_skip_vr25: + addi r2,r2,16 ;stride + andi. r0,r11,0x0020 ;check bit 26 + beq L_co_switch$restore_skip_vr26 + lvx v26,r1,r2 ;restore VR26 +L_co_switch$restore_skip_vr26: + addi r2,r2,16 ;stride + andi. r0,r11,0x0010 ;check bit 27 + beq L_co_switch$restore_skip_vr27 + lvx v27,r1,r2 ;restore VR27 +L_co_switch$restore_skip_vr27: + addi r2,r2,16 ;stride + andi. r0,r11,0x0008 ;check bit 28 + beq L_co_switch$restore_skip_vr28 + lvx v28,r1,r2 ;restore VR28 +L_co_switch$restore_skip_vr28: + addi r2,r2,16 ;stride + andi. r0,r11,0x0004 ;check bit 29 + beq L_co_switch$restore_skip_vr29 + lvx v29,r1,r2 ;restore VR29 +L_co_switch$restore_skip_vr29: + addi r2,r2,16 ;stride + andi. r0,r11,0x0002 ;check bit 30 + beq L_co_switch$restore_skip_vr30 + lvx v30,r1,r2 ;restore VR30 +L_co_switch$restore_skip_vr30: + addi r2,r2,16 ;stride + andi. r0,r11,0x0001 ;check bit 31 + beq L_co_switch$restore_skip_vr31 + lvx v31,r1,r2 ;restore VR31 +L_co_switch$restore_skip_vr31: +L_co_switch$restore_no_vmx: + + lfd f14,248(r1) ;restore preserved FPRs + lfd f15,256(r1) + lfd f16,264(r1) + lfd f17,272(r1) + lfd f18,280(r1) + lfd f19,288(r1) + lfd f20,296(r1) + lfd f21,304(r1) + lfd f22,312(r1) + lfd f23,320(r1) + lfd f24,328(r1) + lfd f25,336(r1) + lfd f26,344(r1) + lfd f27,352(r1) + lfd f28,360(r1) + lfd f29,368(r1) + lfd f30,376(r1) + lfd f31,384(r1) + addi r0,0,0 ;make thread main crash if it returns + ld r1,0(r1) ;deallocate stack frame + ld r6,16(r1) ;return address in GPR6 + lwz r7,8(r1) ;condition codes in GPR7 + ld r13,-152(r1) ;restore preserved GPRs + ld r14,-144(r1) + ld r15,-136(r1) + ld r16,-128(r1) + ld r17,-120(r1) + ld r18,-112(r1) + ld r19,-104(r1) + ld r20,-96(r1) + ld r21,-88(r1) + ld r22,-80(r1) + ld r23,-72(r1) + ld r24,-64(r1) + ld r25,-56(r1) + ld r26,-48(r1) + ld r27,-40(r1) + ld r28,-32(r1) + ld r29,-24(r1) + ld r30,-16(r1) + ld r31,-8(r1) + mtlr r0 + mtctr r6 ;restore return address + mtcrf 32,r7 ;restore preserved condition codes + mtcrf 16,r7 + mtcrf 8,r7 + bctr ;return + + + +;Import external functions + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_malloc$stub: + .indirect_symbol _malloc + mflr r0 + bcl 20,31,L_malloc$spb +L_malloc$spb: + mflr r11 + addis r11,r11,ha16(L_malloc$lazy_ptr-L_malloc$spb) + mtlr r0 + ldu r12,lo16(L_malloc$lazy_ptr-L_malloc$spb)(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_malloc$lazy_ptr: + .indirect_symbol _malloc + .quad dyld_stub_binding_helper + + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_free$stub: + .indirect_symbol _free + mflr r0 + bcl 20,31,L_free$spb +L_free$spb: + mflr r11 + addis r11,r11,ha16(L_free$lazy_ptr-L_free$spb) + mtlr r0 + ldu r12,lo16(L_free$lazy_ptr-L_free$spb)(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_free$lazy_ptr: + .indirect_symbol _free + .quad dyld_stub_binding_helper + + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_sysctlbyname$stub: + .indirect_symbol _sysctlbyname + mflr r0 + bcl 20,31,L_sysctlbyname$spb +L_sysctlbyname$spb: + mflr r11 + addis r11,r11,ha16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb) + mtlr r0 + ldu r12,lo16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb)(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_sysctlbyname$lazy_ptr: + .indirect_symbol _sysctlbyname + .quad dyld_stub_binding_helper + + +;This needs to be here! + + .subsections_via_symbols + diff --git a/bsnes/lib/libco/sjlj.c b/bsnes/lib/libco/sjlj.c new file mode 100755 index 0000000..8b72b61 --- /dev/null +++ b/bsnes/lib/libco/sjlj.c @@ -0,0 +1,102 @@ +/* + libco.sjlj (2008-01-28) + author: Nach + license: public domain +*/ + +/* + * Note this was designed for UNIX systems. Based on ideas expressed in a paper + * by Ralf Engelschall. + * For SJLJ on other systems, one would want to rewrite springboard() and + * co_create() and hack the jmb_buf stack pointer. + */ + +#define LIBCO_C +#include "libco.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + sigjmp_buf context; + void (*coentry)(void); + void *stack; +} cothread_struct; + +static thread_local cothread_struct co_primary; +static thread_local cothread_struct *creating, *co_running = 0; + +static void springboard(int ignored) { + if(sigsetjmp(creating->context, 0)) { + co_running->coentry(); + } +} + +cothread_t co_active() { + if(!co_running) co_running = &co_primary; + return (cothread_t)co_running; +} + +cothread_t co_create(unsigned int size, void (*coentry)(void)) { + if(!co_running) co_running = &co_primary; + + cothread_struct *thread = (cothread_struct*)malloc(sizeof(cothread_struct)); + if(thread) { + struct sigaction handler; + struct sigaction old_handler; + + stack_t stack; + stack_t old_stack; + + thread->coentry = thread->stack = 0; + + stack.ss_flags = 0; + stack.ss_size = size; + thread->stack = stack.ss_sp = malloc(size); + if(stack.ss_sp && !sigaltstack(&stack, &old_stack)) { + handler.sa_handler = springboard; + handler.sa_flags = SA_ONSTACK; + sigemptyset(&handler.sa_mask); + creating = thread; + + if(!sigaction(SIGUSR1, &handler, &old_handler)) { + if(!raise(SIGUSR1)) { + thread->coentry = coentry; + } + sigaltstack(&old_stack, 0); + sigaction(SIGUSR1, &old_handler, 0); + } + } + + if(thread->coentry != coentry) { + co_delete(thread); + thread = 0; + } + } + + return (cothread_t)thread; +} + +void co_delete(cothread_t cothread) { + if(cothread) { + if(((cothread_struct*)cothread)->stack) { + free(((cothread_struct*)cothread)->stack); + } + free(cothread); + } +} + +void co_switch(cothread_t cothread) { + if(!sigsetjmp(co_running->context, 0)) { + co_running = (cothread_struct*)cothread; + siglongjmp(co_running->context, 1); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/bsnes/lib/libco/ucontext.c b/bsnes/lib/libco/ucontext.c new file mode 100755 index 0000000..17472f6 --- /dev/null +++ b/bsnes/lib/libco/ucontext.c @@ -0,0 +1,67 @@ +/* + libco.ucontext (2008-01-28) + author: Nach + license: public domain +*/ + +/* + * WARNING: the overhead of POSIX ucontext is very high, + * assembly versions of libco or libco_sjlj should be much faster + * + * This library only exists for two reasons: + * 1 - as an initial test for the viability of a ucontext implementation + * 2 - to demonstrate the power and speed of libco over existing implementations, + * such as pth (which defaults to wrapping ucontext on unix targets) + * + * Use this library only as a *last resort* + */ + +#define LIBCO_C +#include "libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local ucontext_t co_primary; +static thread_local ucontext_t *co_running = 0; + +cothread_t co_active() { + if(!co_running) co_running = &co_primary; + return (cothread_t)co_running; +} + +cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { + if(!co_running) co_running = &co_primary; + ucontext_t *thread = (ucontext_t*)malloc(sizeof(ucontext_t)); + if(thread) { + if((!getcontext(thread) && !(thread->uc_stack.ss_sp = 0)) && (thread->uc_stack.ss_sp = malloc(heapsize))) { + thread->uc_link = co_running; + thread->uc_stack.ss_size = heapsize; + makecontext(thread, coentry, 0); + } else { + co_delete((cothread_t)thread); + thread = 0; + } + } + return (cothread_t)thread; +} + +void co_delete(cothread_t cothread) { + if(cothread) { + if(((ucontext_t*)cothread)->uc_stack.ss_sp) { free(((ucontext_t*)cothread)->uc_stack.ss_sp); } + free(cothread); + } +} + +void co_switch(cothread_t cothread) { + ucontext_t *old_thread = co_running; + co_running = (ucontext_t*)cothread; + swapcontext(old_thread, co_running); +} + +#ifdef __cplusplus +} +#endif diff --git a/bsnes/lib/libco/x86-64.c b/bsnes/lib/libco/x86-64.c new file mode 100755 index 0000000..2e2a113 --- /dev/null +++ b/bsnes/lib/libco/x86-64.c @@ -0,0 +1,81 @@ +/* + libco.x86-64 (2008-01-28) + author: byuu + license: public domain +*/ + +#define LIBCO_C +#include "libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local long co_active_buffer[32]; +static thread_local cothread_t co_active_ = 0; + +static void crash() { + assert(0); /* called only if cothread_t entrypoint returns */ +} + +cothread_t co_active() { + if(!co_active_) co_active_ = &co_active_buffer; + return co_active_; +} + +cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { + cothread_t handle; + assert(sizeof(long) == 8); + if(!co_active_) co_active_ = &co_active_buffer; + size += 128; /* allocate additional space for storage */ + size &= ~15; /* align stack to 16-byte boundary */ + + if(handle = (cothread_t)calloc(size, 1)) { + long *p = (long*)((char*)handle + size); /* seek to top of stack */ + *--p = (long)crash; /* crash if entrypoint returns */ + *--p = (long)entrypoint; /* start of function */ + *(long*)handle = (long)p; /* stack pointer */ + } + + return handle; +} + +void co_delete(cothread_t handle) { + free(handle); +} + +void co_switch(cothread_t to) { + register long stack = *(long*)to; /* stack[0] = "to" thread entry point */ + register cothread_t from = co_active_; + co_active_ = to; + + __asm__ __volatile__( + "movq %%rsp,(%1) \n\t" /* save old stack pointer */ + "movq (%0),%%rsp \n\t" /* load new stack pointer */ + "addq $8,%%rsp \n\t" /* "pop" return address off stack */ + + "movq %%rbp, 8(%1) \n\t" /* backup non-volatile registers */ + "movq %%rbx,16(%1) \n\t" + "movq %%r12,24(%1) \n\t" + "movq %%r13,32(%1) \n\t" + "movq %%r14,40(%1) \n\t" + "movq %%r15,48(%1) \n\t" + + "movq 8(%0),%%rbp \n\t" /* restore non-volatile registers */ + "movq 16(%0),%%rbx \n\t" + "movq 24(%0),%%r12 \n\t" + "movq 32(%0),%%r13 \n\t" + "movq 40(%0),%%r14 \n\t" + "movq 48(%0),%%r15 \n\t" + + "jmp *(%2) \n\t" /* jump into "to" thread */ + : /* no outputs */ + : "r" (to), "r" (from), "r" (stack) + ); +} + +#ifdef __cplusplus +} +#endif diff --git a/bsnes/lib/libco/x86.c b/bsnes/lib/libco/x86.c new file mode 100755 index 0000000..3a5507f --- /dev/null +++ b/bsnes/lib/libco/x86.c @@ -0,0 +1,110 @@ +/* + libco.x86 (2008-01-28) + author: byuu + license: public domain +*/ + +#define LIBCO_C +#include "libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local long co_active_buffer[32]; +static thread_local cothread_t co_active_ = 0; + +static void crash() { + assert(0); /* called only if cothread_t entrypoint returns */ +} + +cothread_t co_active() { + if(!co_active_) co_active_ = &co_active_buffer; + return co_active_; +} + +cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { + cothread_t handle; + assert(sizeof(long) == 4); + if(!co_active_) co_active_ = &co_active_buffer; + size += 128; /* allocate additional space for storage */ + size &= ~15; /* align stack to 16-byte boundary */ + + if(handle = (cothread_t)calloc(size, 1)) { + long *p = (long*)((char*)handle + size); /* seek to top of stack */ + *--p = (long)crash; /* crash if entrypoint returns */ + *--p = (long)entrypoint; /* start of function */ + *(long*)handle = (long)p; /* stack pointer */ + } + + return handle; +} + +void co_delete(cothread_t handle) { + free(handle); +} + +#if defined(__GNUC__) + +void co_switch(cothread_t to) { + register long stack = *(long*)to; /* stack[0] = "to" thread entry point */ + register cothread_t from = co_active_; + co_active_ = to; + + __asm__ __volatile__( + "movl %%esp,(%1) \n\t" /* save old stack pointer */ + "movl (%0),%%esp \n\t" /* load new stack pointer */ + "addl $4,%%esp \n\t" /* "pop" return address off stack */ + + "movl %%ebp, 4(%1) \n\t" /* backup non-volatile registers */ + "movl %%esi, 8(%1) \n\t" + "movl %%edi,12(%1) \n\t" + "movl %%ebx,16(%1) \n\t" + + "movl 4(%0),%%ebp \n\t" /* restore non-volatile registers */ + "movl 8(%0),%%esi \n\t" + "movl 12(%0),%%edi \n\t" + "movl 16(%0),%%ebx \n\t" + + "jmp *(%2) \n\t" /* jump into "to" thread */ + : /* no outputs */ + : "r" (to), "r" (from), "r" (stack) + ); +} + +#elif defined(_MSC_VER) + +__declspec(naked) __declspec(noinline) +static void __fastcall co_swap(register cothread_t to, register cothread_t from) { + /* ecx = to, edx = from */ + __asm { + mov [edx],esp + mov esp,[ecx] + pop eax + + mov [edx+ 4],ebp + mov [edx+ 8],esi + mov [edx+12],edi + mov [edx+16],ebx + + mov ebp,[ecx+ 4] + mov esi,[ecx+ 8] + mov edi,[ecx+12] + mov ebx,[ecx+16] + + jmp eax + } +} + +void co_switch(cothread_t handle) { + register cothread_t co_prev_ = co_active_; + co_swap(co_active_ = handle, co_prev_); +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/bsnes/lib/libfilter/colortable.cpp b/bsnes/lib/libfilter/colortable.cpp new file mode 100755 index 0000000..17b12e5 --- /dev/null +++ b/bsnes/lib/libfilter/colortable.cpp @@ -0,0 +1,138 @@ +Colortable colortable; + +void Colortable::set_format(Format format_) { format = format_; } +void Colortable::set_contrast(signed contrast_) { contrast = contrast_; } +void Colortable::set_brightness(signed brightness_) { brightness = brightness_; } +void Colortable::set_gamma(signed gamma_) { gamma = gamma_; } + +void Colortable::enable_gamma_ramp(bool value) { gamma_ramp = value; } +void Colortable::enable_sepia(bool value) { sepia = value; } +void Colortable::enable_grayscale(bool value) { grayscale = value; } +void Colortable::enable_invert(bool value) { invert = value; } + +void Colortable::update() { + double kr = 0.2126, kb = 0.0722, kg = (1.0 - kr - kb); //luminance weights + + for(unsigned i = 0; i < 32768; i++) { + unsigned color //bgr555->rgb888 conversion + = ((i & 0x001f) << 19) | ((i & 0x001c) << 14) + | ((i & 0x03e0) << 6) | ((i & 0x0380) << 1) + | ((i & 0x7c00) >> 7) | ((i & 0x7000) >> 12); + + signed l; + signed r = (color >> 16) & 0xff; + signed g = (color >> 8) & 0xff; + signed b = (color ) & 0xff; + + if(gamma_ramp == true) { + r = gamma_ramp_table[r >> 3]; + g = gamma_ramp_table[g >> 3]; + b = gamma_ramp_table[b >> 3]; + } + + if(contrast != 0) { + r = contrast_adjust(r); + g = contrast_adjust(g); + b = contrast_adjust(b); + } + + if(brightness != 0) { + r = brightness_adjust(r); + g = brightness_adjust(g); + b = brightness_adjust(b); + } + + if(gamma != 100) { + r = gamma_adjust(r); + g = gamma_adjust(g); + b = gamma_adjust(b); + } + + if(sepia == true) { + l = (signed)((double)r * kr + (double)g * kg + (double)b * kb); + l = max(0, min(255, l)); + + r = (signed)((double)l * (1.0 + 0.300)); + g = (signed)((double)l * (1.0 - 0.055)); + b = (signed)((double)l * (1.0 - 0.225)); + + r = max(0, min(255, r)); + g = max(0, min(255, g)); + b = max(0, min(255, b)); + } + + if(grayscale == true) { + l = (signed)((double)r * kr + (double)g * kg + (double)b * kb); + l = max(0, min(255, l)); + r = g = b = l; + } + + if(invert == true) { + r ^= 0xff; + g ^= 0xff; + b ^= 0xff; + } + + switch(format) { + case RGB555: { + r >>= 3; + g >>= 3; + b >>= 3; + table[i] = (r << 10) | (g << 5) | (b); + } break; + + case RGB565: { + r >>= 3; + g >>= 2; + b >>= 3; + table[i] = (r << 11) | (g << 5) | (b); + } break; + + case RGB888: { + table[i] = (r << 16) | (g << 8) | (b); + } break; + + default: { + table[i] = ~0; + } break; + } + } +} + +Colortable::Colortable() { + table = new uint32_t[32768]; + contrast = 0; + brightness = 0; + gamma = 100; + + gamma_ramp = false; + sepia = false; + grayscale = false; + invert = false; +} + +Colortable::~Colortable() { + delete[] table; +} + +const uint8_t Colortable::gamma_ramp_table[32] = { + 0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c, + 0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78, + 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, + 0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff, +}; + +uint8_t Colortable::contrast_adjust(uint8_t input) { + signed result = input - contrast + (2 * contrast * input + 127) / 255; + return max(0, min(255, result)); +} + +uint8_t Colortable::brightness_adjust(uint8_t input) { + signed result = input + brightness; + return max(0, min(255, result)); +} + +uint8_t Colortable::gamma_adjust(uint8_t input) { + signed result = (signed)(pow(((double)input / 255.0), (double)gamma / 100.0) * 255.0 + 0.5); + return max(0, min(255, result)); +} diff --git a/bsnes/lib/libfilter/colortable.hpp b/bsnes/lib/libfilter/colortable.hpp new file mode 100755 index 0000000..d5dd831 --- /dev/null +++ b/bsnes/lib/libfilter/colortable.hpp @@ -0,0 +1,44 @@ +class Colortable { +public: + enum Format { + RGB555, + RGB565, + RGB888, + }; + + const inline uint32_t operator[](uint16_t index) const { return table[index]; } + + void set_format(Format); + void set_contrast(signed); + void set_brightness(signed); + void set_gamma(signed); + void enable_gamma_ramp(bool); + void enable_sepia(bool); + void enable_grayscale(bool); + void enable_invert(bool); + + void update(); + + Colortable(); + ~Colortable(); + +private: + uint32_t *table; + Format format; + + signed contrast; + signed brightness; + signed gamma; + + bool gamma_ramp; + bool sepia; + bool grayscale; + bool invert; + + static const uint8_t gamma_ramp_table[32]; + uint8_t contrast_adjust(uint8_t input); + uint8_t brightness_adjust(uint8_t input); + uint8_t gamma_adjust(uint8_t input); +}; + +extern Colortable colortable; diff --git a/bsnes/lib/libfilter/direct.cpp b/bsnes/lib/libfilter/direct.cpp new file mode 100755 index 0000000..6afe0e3 --- /dev/null +++ b/bsnes/lib/libfilter/direct.cpp @@ -0,0 +1,30 @@ +DirectFilter filter_direct; + +void DirectFilter::render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height +) { + pitch >>= 1; + outpitch >>= 2; + + for(unsigned y = 0; y < height; y++) { + if(width == 512 && line[y] == 256) { + for(unsigned x = 0; x < 256; x++) { + uint16_t p = *input++; + *output++ = colortable[p]; + *output++ = colortable[p]; + } + input += 256; + } else { + for(unsigned x = 0; x < width; x++) { + uint16_t p = *input++; + *output++ = colortable[p]; + } + } + input += pitch - width; + output += outpitch - width; + } + + outwidth = width; + outheight = height; +} diff --git a/bsnes/lib/libfilter/direct.hpp b/bsnes/lib/libfilter/direct.hpp new file mode 100755 index 0000000..0d339fa --- /dev/null +++ b/bsnes/lib/libfilter/direct.hpp @@ -0,0 +1,6 @@ +class DirectFilter : public Filter { +public: + void render(uint32_t*, unsigned, unsigned&, unsigned&, uint16_t*, unsigned, unsigned*, unsigned, unsigned); +}; + +extern DirectFilter filter_direct; diff --git a/bsnes/lib/libfilter/filter.cpp b/bsnes/lib/libfilter/filter.cpp new file mode 100755 index 0000000..7025ba6 --- /dev/null +++ b/bsnes/lib/libfilter/filter.cpp @@ -0,0 +1,34 @@ +FilterInterface filter; + +void FilterInterface::set(FilterInterface::FilterType type) { + active_filter = type; +} + +void FilterInterface::render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height +) { + switch(active_filter) { default: + case Direct: { + filter_direct.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + } break; + + case Scanline: { + filter_scanline.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + } break; + + case Scale2x: { + filter_scale2x.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + } break; + + case HQ2x: { + filter_hq2x.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + } break; + + case NTSC: { + filter_ntsc.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + } break; + } +} + +FilterInterface::FilterInterface() : active_filter(FilterInterface::Direct) {} diff --git a/bsnes/lib/libfilter/filter.hpp b/bsnes/lib/libfilter/filter.hpp new file mode 100755 index 0000000..7aaa36b --- /dev/null +++ b/bsnes/lib/libfilter/filter.hpp @@ -0,0 +1,32 @@ +class Filter { +public: + virtual void render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height + ) = 0; +}; + +class FilterInterface : public Filter { +public: + enum FilterType { + Direct, + Scanline, + Scale2x, + HQ2x, + NTSC, + }; + + void set(FilterType type); + + void render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height + ); + + FilterInterface(); + +private: + FilterType active_filter; +}; + +extern FilterInterface filter; diff --git a/bsnes/lib/libfilter/hq2x.cpp b/bsnes/lib/libfilter/hq2x.cpp new file mode 100755 index 0000000..5cf657c --- /dev/null +++ b/bsnes/lib/libfilter/hq2x.cpp @@ -0,0 +1,160 @@ +HQ2xFilter filter_hq2x; + +#define diff(x, y) ((yuvtable[x] - yuvtable[y] + diff_offset) & diff_mask) +#define hdiff(x, y) ((x - yuvtable[y]) & diff_mask) +#define expand_rgb(n) { n |= n << 16; n &= 0x03e07c1f; } +#define pack_rgb(n) { n &= 0x03e07c1f; n |= n >> 16; } + +static uint16_t blend1(uint32_t c1, uint32_t c2) { + expand_rgb(c1); + expand_rgb(c2); + c1 = (c1 * 3 + c2) >> 2; + pack_rgb(c1); + return c1; +} + +static uint16_t blend2(uint32_t c1, uint32_t c2, uint32_t c3) { +//c1 = (c1 * 2 + c2 + c3) >> 2; + c2 = (c2 + c3 - ((c2 ^ c3) & 0x0421)) >> 1; + c1 = (c1 + c2 - ((c1 ^ c2) & 0x0421)) >> 1; + return c1; +} + +static uint16_t blend6(uint32_t c1, uint32_t c2, uint32_t c3) { + expand_rgb(c1); + expand_rgb(c2); + expand_rgb(c3); + c1 = (c1 * 5 + c2 * 2 + c3) >> 3; + pack_rgb(c1); + return c1; +} + +static uint16_t blend7(uint32_t c1, uint32_t c2, uint32_t c3) { + expand_rgb(c1); + expand_rgb(c2); + expand_rgb(c3); + c1 = (c1 * 6 + c2 + c3) >> 3; + pack_rgb(c1); + return c1; +} + +static uint16_t blend9(uint32_t c1, uint32_t c2, uint32_t c3) { + expand_rgb(c1); + expand_rgb(c2); + expand_rgb(c3); + c1 = (c1 * 2 + (c2 + c3) * 3) >> 3; + pack_rgb(c1); + return c1; +} + +static uint16_t blend10(uint32_t c1, uint32_t c2, uint32_t c3) { + expand_rgb(c1); + expand_rgb(c2); + expand_rgb(c3); + c1 = (c1 * 14 + c2 + c3) >> 4; + pack_rgb(c1); + return c1; +} + +void HQ2xFilter::render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height +) { + if(width > 256 || height > 240) { + filter_direct.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + return; + } + + pitch >>= 1; + outpitch >>= 2; + + uint32_t *out0 = output; + uint32_t *out1 = output + outpitch; + + #define W1 input[-1 - (int)pitch] + #define W2 input[ 0 - (int)pitch] + #define W3 input[+1 - (int)pitch] + #define W4 input[-1] + #define W5 input[ 0] + #define W6 input[+1] + #define W7 input[-1 + (int)pitch] + #define W8 input[ 0 + (int)pitch] + #define W9 input[+1 + (int)pitch] + + input += pitch; + memset(out0, 0, 2048); out0 += outpitch << 1; + memset(out1, 0, 2048); out1 += outpitch << 1; + + for(int y = height - 2; y; --y) { + input++; + *out0++ = 0; *out0++ = 0; + *out1++ = 0; *out1++ = 0; + + int32_t pattern = diff(W5, W4) ? 0x10 : 0x00; + for(int x = 256 - 2; x; --x) { + uint32_t center = yuvtable[W5] + diff_offset; + + //W4 for pixel x+1 is the same as W6 for pixel x + pattern = (pattern & 0x10) >> 1; + pattern |= hdiff(center, W1) ? 0x01 : 0x00; + pattern |= hdiff(center, W2) ? 0x02 : 0x00; + pattern |= hdiff(center, W3) ? 0x04 : 0x00; + //pattern |= hdiff(center, W4) ? 0x08 : 0x00; + pattern |= hdiff(center, W6) ? 0x10 : 0x00; + pattern |= hdiff(center, W7) ? 0x20 : 0x00; + pattern |= hdiff(center, W8) ? 0x40 : 0x00; + pattern |= hdiff(center, W9) ? 0x80 : 0x00; + + switch(pattern) { + #include "hq2x_table.hpp" + } + + input++; + out0 += 2; + out1 += 2; + } + + input++; + *out0++ = 0; *out0++ = 0; + *out1++ = 0; *out1++ = 0; + + input += pitch - 256; + out0 += outpitch + outpitch - 512; + out1 += outpitch + outpitch - 512; + } + + memset(out0, 0, 2048); + memset(out1, 0, 2048); + + outwidth = width * 2; + outheight = height * 2; +} + +HQ2xFilter::HQ2xFilter() { + yuvtable = new uint32_t[32768]; + + for(int i = 0; i < 32768; i++) { + int ir = (i) & 0x1f; + int ig = (i >> 5) & 0x1f; + int ib = (i >> 10) & 0x1f; + + //bgr555->bgr888 + double r = (ir << 3) | (ir >> 2); + double g = (ig << 3) | (ig >> 2); + double b = (ib << 3) | (ib >> 2); + + //bgr888->yuv888 + double y = (r + g + b) * (0.25f * (63.5f / 48.0f)); + double u = ((r - b) * 0.25f + 128.0f) * (7.5f / 7.0f); + double v = ((g * 2.0f - r - b) * 0.125f + 128.0f) * (7.5f / 6.0f); + + yuvtable[i] = (int(y) << 21) + (int(u) << 11) + (int(v)); + } + + diff_offset = (0x440 << 21) + (0x207 << 11) + 0x407; + diff_mask = (0x380 << 21) + (0x1f0 << 11) + 0x3f0; +} + +HQ2xFilter::~HQ2xFilter() { + delete[] yuvtable; +} diff --git a/bsnes/lib/libfilter/hq2x.hpp b/bsnes/lib/libfilter/hq2x.hpp new file mode 100755 index 0000000..227a796 --- /dev/null +++ b/bsnes/lib/libfilter/hq2x.hpp @@ -0,0 +1,14 @@ +class HQ2xFilter : public Filter { +public: + void render(uint32_t*, unsigned, unsigned&, unsigned&, uint16_t*, unsigned, unsigned*, unsigned, unsigned); + + HQ2xFilter(); + ~HQ2xFilter(); + +private: + uint32_t *yuvtable; + uint32_t diff_offset; + uint32_t diff_mask; +}; + +extern HQ2xFilter filter_hq2x; diff --git a/bsnes/lib/libfilter/hq2x_table.hpp b/bsnes/lib/libfilter/hq2x_table.hpp new file mode 100755 index 0000000..3d93e94 --- /dev/null +++ b/bsnes/lib/libfilter/hq2x_table.hpp @@ -0,0 +1,2690 @@ +/***** + * HQ2x Algorithm (C) 2003 Maxim Stepin + * License: LGPL + * + * Optimizations (C) 2006 Shay Green, byuu + *****/ + +#define p00(color) *(out0 + 0) = (colortable[color]) +#define p01(color) *(out0 + 1) = (colortable[color]) +#define p10(color) *(out1 + 0) = (colortable[color]) +#define p11(color) *(out1 + 1) = (colortable[color]) + +#define PIXEL00_0 p00( W5); +#define PIXEL00_10 p00(blend1 (W5, W1)); +#define PIXEL00_11 p00(blend1 (W5, W4)); +#define PIXEL00_12 p00(blend1 (W5, W2)); +#define PIXEL00_20 p00(blend2 (W5, W4, W2)); +#define PIXEL00_21 p00(blend2 (W5, W1, W2)); +#define PIXEL00_22 p00(blend2 (W5, W1, W4)); +#define PIXEL00_60 p00(blend6 (W5, W2, W4)); +#define PIXEL00_61 p00(blend6 (W5, W4, W2)); +#define PIXEL00_70 p00(blend7 (W5, W4, W2)); +#define PIXEL00_90 p00(blend9 (W5, W4, W2)); +#define PIXEL00_100 p00(blend10(W5, W4, W2)); + +#define PIXEL01_0 p01( W5); +#define PIXEL01_10 p01(blend1 (W5, W3)); +#define PIXEL01_11 p01(blend1 (W5, W2)); +#define PIXEL01_12 p01(blend1 (W5, W6)); +#define PIXEL01_20 p01(blend2 (W5, W2, W6)); +#define PIXEL01_21 p01(blend2 (W5, W3, W6)); +#define PIXEL01_22 p01(blend2 (W5, W3, W2)); +#define PIXEL01_60 p01(blend6 (W5, W6, W2)); +#define PIXEL01_61 p01(blend6 (W5, W2, W6)); +#define PIXEL01_70 p01(blend7 (W5, W2, W6)); +#define PIXEL01_90 p01(blend9 (W5, W2, W6)); +#define PIXEL01_100 p01(blend10(W5, W2, W6)); + +#define PIXEL10_0 p10( W5); +#define PIXEL10_10 p10(blend1 (W5, W7)); +#define PIXEL10_11 p10(blend1 (W5, W8)); +#define PIXEL10_12 p10(blend1 (W5, W4)); +#define PIXEL10_20 p10(blend2 (W5, W8, W4)); +#define PIXEL10_21 p10(blend2 (W5, W7, W4)); +#define PIXEL10_22 p10(blend2 (W5, W7, W8)); +#define PIXEL10_60 p10(blend6 (W5, W4, W8)); +#define PIXEL10_61 p10(blend6 (W5, W8, W4)); +#define PIXEL10_70 p10(blend7 (W5, W8, W4)); +#define PIXEL10_90 p10(blend9 (W5, W8, W4)); +#define PIXEL10_100 p10(blend10(W5, W8, W4)); + +#define PIXEL11_0 p11( W5); +#define PIXEL11_10 p11(blend1 (W5, W9)); +#define PIXEL11_11 p11(blend1 (W5, W6)); +#define PIXEL11_12 p11(blend1 (W5, W8)); +#define PIXEL11_20 p11(blend2 (W5, W6, W8)); +#define PIXEL11_21 p11(blend2 (W5, W9, W8)); +#define PIXEL11_22 p11(blend2 (W5, W9, W6)); +#define PIXEL11_60 p11(blend6 (W5, W8, W6)); +#define PIXEL11_61 p11(blend6 (W5, W6, W8)); +#define PIXEL11_70 p11(blend7 (W5, W6, W8)); +#define PIXEL11_90 p11(blend9 (W5, W6, W8)); +#define PIXEL11_100 p11(blend10(W5, W6, W8)); + + case 0: + case 1: + case 4: + case 32: + case 128: + case 5: + case 132: + case 160: + case 33: + case 129: + case 36: + case 133: + case 164: + case 161: + case 37: + case 165: { + PIXEL00_20 + PIXEL01_20 + PIXEL10_20 + PIXEL11_20 + } break; + + case 2: + case 34: + case 130: + case 162: { + PIXEL00_22 + PIXEL01_21 + PIXEL10_20 + PIXEL11_20 + } break; + + case 16: + case 17: + case 48: + case 49: { + PIXEL00_20 + PIXEL01_22 + PIXEL10_20 + PIXEL11_21 + } break; + + case 64: + case 65: + case 68: + case 69: { + PIXEL00_20 + PIXEL01_20 + PIXEL10_21 + PIXEL11_22 + } break; + + case 8: + case 12: + case 136: + case 140: { + PIXEL00_21 + PIXEL01_20 + PIXEL10_22 + PIXEL11_20 + } break; + + case 3: + case 35: + case 131: + case 163: { + PIXEL00_11 + PIXEL01_21 + PIXEL10_20 + PIXEL11_20 + } break; + + case 6: + case 38: + case 134: + case 166: { + PIXEL00_22 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + } break; + + case 20: + case 21: + case 52: + case 53: { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_21 + } break; + + case 144: + case 145: + case 176: + case 177: { + PIXEL00_20 + PIXEL01_22 + PIXEL10_20 + PIXEL11_12 + } break; + + case 192: + case 193: + case 196: + case 197: { + PIXEL00_20 + PIXEL01_20 + PIXEL10_21 + PIXEL11_11 + } break; + + case 96: + case 97: + case 100: + case 101: { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_22 + } break; + + case 40: + case 44: + case 168: + case 172: { + PIXEL00_21 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + } break; + + case 9: + case 13: + case 137: + case 141: { + PIXEL00_12 + PIXEL01_20 + PIXEL10_22 + PIXEL11_20 + } break; + + case 18: + case 50: + { + PIXEL00_22 + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_20 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 80: + case 81: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_21 + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_20 + } + break; + } + case 72: + case 76: + { + PIXEL00_21 + PIXEL01_20 + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 10: + case 138: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_22 + PIXEL11_20 + break; + } + case 66: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_21 + PIXEL11_22 + break; + } + case 24: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_22 + PIXEL11_21 + break; + } + case 7: + case 39: + case 135: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 148: + case 149: + case 180: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_12 + break; + } + case 224: + case 228: + case 225: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_11 + break; + } + case 41: + case 169: + case 45: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 22: + case 54: + { + PIXEL00_22 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 208: + case 209: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_21 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 104: + case 108: + { + PIXEL00_21 + PIXEL01_20 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 11: + case 139: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_22 + PIXEL11_20 + break; + } + case 19: + case 51: + { + if (diff(W2, W6)) + { + PIXEL00_11 + PIXEL01_10 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 146: + case 178: + { + PIXEL00_22 + if (diff(W2, W6)) + { + PIXEL01_10 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_20 + break; + } + case 84: + case 85: + { + PIXEL00_20 + if (diff(W6, W8)) + { + PIXEL01_11 + PIXEL11_10 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_21 + break; + } + case 112: + case 113: + { + PIXEL00_20 + PIXEL01_22 + if (diff(W6, W8)) + { + PIXEL10_12 + PIXEL11_10 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 200: + case 204: + { + PIXEL00_21 + PIXEL01_20 + if (diff(W8, W4)) + { + PIXEL10_10 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 73: + case 77: + { + if (diff(W8, W4)) + { + PIXEL00_12 + PIXEL10_10 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_20 + PIXEL11_22 + break; + } + case 42: + case 170: + { + if (diff(W4, W2)) + { + PIXEL00_10 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_21 + PIXEL11_20 + break; + } + case 14: + case 142: + { + if (diff(W4, W2)) + { + PIXEL00_10 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_22 + PIXEL11_20 + break; + } + case 67: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_21 + PIXEL11_22 + break; + } + case 70: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_21 + PIXEL11_22 + break; + } + case 28: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_22 + PIXEL11_21 + break; + } + case 152: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_22 + PIXEL11_12 + break; + } + case 194: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_21 + PIXEL11_11 + break; + } + case 98: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_12 + PIXEL11_22 + break; + } + case 56: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_11 + PIXEL11_21 + break; + } + case 25: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_22 + PIXEL11_21 + break; + } + case 26: + case 31: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_21 + break; + } + case 82: + case 214: + { + PIXEL00_22 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 88: + case 248: + { + PIXEL00_21 + PIXEL01_22 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 74: + case 107: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 27: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_22 + PIXEL11_21 + break; + } + case 86: + { + PIXEL00_22 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + PIXEL11_10 + break; + } + case 216: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_10 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 106: + { + PIXEL00_10 + PIXEL01_21 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 30: + { + PIXEL00_10 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_21 + break; + } + case 210: + { + PIXEL00_22 + PIXEL01_10 + PIXEL10_21 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 120: + { + PIXEL00_21 + PIXEL01_22 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 75: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_10 + PIXEL11_22 + break; + } + case 29: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_22 + PIXEL11_21 + break; + } + case 198: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_21 + PIXEL11_11 + break; + } + case 184: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_11 + PIXEL11_12 + break; + } + case 99: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_12 + PIXEL11_22 + break; + } + case 57: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_11 + PIXEL11_21 + break; + } + case 71: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_21 + PIXEL11_22 + break; + } + case 156: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_22 + PIXEL11_12 + break; + } + case 226: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_12 + PIXEL11_11 + break; + } + case 60: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_11 + PIXEL11_21 + break; + } + case 195: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_21 + PIXEL11_11 + break; + } + case 102: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_12 + PIXEL11_22 + break; + } + case 153: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_22 + PIXEL11_12 + break; + } + case 58: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 83: + { + PIXEL00_11 + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_21 + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 92: + { + PIXEL00_21 + PIXEL01_11 + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 202: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_21 + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 78: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_22 + break; + } + case 154: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 114: + { + PIXEL00_22 + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 89: + { + PIXEL00_12 + PIXEL01_22 + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 90: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 55: + case 23: + { + if (diff(W2, W6)) + { + PIXEL00_11 + PIXEL01_0 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 182: + case 150: + { + PIXEL00_22 + if (diff(W2, W6)) + { + PIXEL01_0 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_20 + break; + } + case 213: + case 212: + { + PIXEL00_20 + if (diff(W6, W8)) + { + PIXEL01_11 + PIXEL11_0 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_21 + break; + } + case 241: + case 240: + { + PIXEL00_20 + PIXEL01_22 + if (diff(W6, W8)) + { + PIXEL10_12 + PIXEL11_0 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 236: + case 232: + { + PIXEL00_21 + PIXEL01_20 + if (diff(W8, W4)) + { + PIXEL10_0 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 109: + case 105: + { + if (diff(W8, W4)) + { + PIXEL00_12 + PIXEL10_0 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_20 + PIXEL11_22 + break; + } + case 171: + case 43: + { + if (diff(W4, W2)) + { + PIXEL00_0 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_21 + PIXEL11_20 + break; + } + case 143: + case 15: + { + if (diff(W4, W2)) + { + PIXEL00_0 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_22 + PIXEL11_20 + break; + } + case 124: + { + PIXEL00_21 + PIXEL01_11 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 203: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_10 + PIXEL11_11 + break; + } + case 62: + { + PIXEL00_10 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 211: + { + PIXEL00_11 + PIXEL01_10 + PIXEL10_21 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 118: + { + PIXEL00_22 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_12 + PIXEL11_10 + break; + } + case 217: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_10 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 110: + { + PIXEL00_10 + PIXEL01_12 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 155: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_22 + PIXEL11_12 + break; + } + case 188: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_11 + PIXEL11_12 + break; + } + case 185: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_11 + PIXEL11_12 + break; + } + case 61: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_11 + PIXEL11_21 + break; + } + case 157: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_22 + PIXEL11_12 + break; + } + case 103: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_12 + PIXEL11_22 + break; + } + case 227: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_12 + PIXEL11_11 + break; + } + case 230: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_12 + PIXEL11_11 + break; + } + case 199: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_21 + PIXEL11_11 + break; + } + case 220: + { + PIXEL00_21 + PIXEL01_11 + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 158: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 234: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_21 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_11 + break; + } + case 242: + { + PIXEL00_22 + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 59: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 121: + { + PIXEL00_12 + PIXEL01_22 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 87: + { + PIXEL00_11 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 79: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_12 + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_22 + break; + } + case 122: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 94: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 218: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 91: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 229: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_11 + break; + } + case 167: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 173: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 181: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_12 + break; + } + case 186: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_12 + break; + } + case 115: + { + PIXEL00_11 + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 93: + { + PIXEL00_12 + PIXEL01_11 + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 206: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 205: + case 201: + { + PIXEL00_12 + PIXEL01_20 + if (diff(W8, W4)) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 174: + case 46: + { + if (diff(W4, W2)) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + PIXEL10_11 + PIXEL11_20 + break; + } + case 179: + case 147: + { + PIXEL00_11 + if (diff(W2, W6)) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_20 + PIXEL11_12 + break; + } + case 117: + case 116: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_12 + if (diff(W6, W8)) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 189: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_11 + PIXEL11_12 + break; + } + case 231: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_12 + PIXEL11_11 + break; + } + case 126: + { + PIXEL00_10 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 219: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_10 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 125: + { + if (diff(W8, W4)) + { + PIXEL00_12 + PIXEL10_0 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_11 + PIXEL11_10 + break; + } + case 221: + { + PIXEL00_12 + if (diff(W6, W8)) + { + PIXEL01_11 + PIXEL11_0 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_10 + break; + } + case 207: + { + if (diff(W4, W2)) + { + PIXEL00_0 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_10 + PIXEL11_11 + break; + } + case 238: + { + PIXEL00_10 + PIXEL01_12 + if (diff(W8, W4)) + { + PIXEL10_0 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 190: + { + PIXEL00_10 + if (diff(W2, W6)) + { + PIXEL01_0 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_11 + break; + } + case 187: + { + if (diff(W4, W2)) + { + PIXEL00_0 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_10 + PIXEL11_12 + break; + } + case 243: + { + PIXEL00_11 + PIXEL01_10 + if (diff(W6, W8)) + { + PIXEL10_12 + PIXEL11_0 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 119: + { + if (diff(W2, W6)) + { + PIXEL00_11 + PIXEL01_0 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_12 + PIXEL11_10 + break; + } + case 237: + case 233: + { + PIXEL00_12 + PIXEL01_20 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 175: + case 47: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + PIXEL10_11 + PIXEL11_20 + break; + } + case 183: + case 151: + { + PIXEL00_11 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_20 + PIXEL11_12 + break; + } + case 245: + case 244: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_12 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 250: + { + PIXEL00_10 + PIXEL01_10 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 123: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 95: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_10 + PIXEL11_10 + break; + } + case 222: + { + PIXEL00_10 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_10 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 252: + { + PIXEL00_21 + PIXEL01_11 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 249: + { + PIXEL00_12 + PIXEL01_22 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 235: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 111: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 63: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 159: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 215: + { + PIXEL00_11 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_21 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 246: + { + PIXEL00_22 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_12 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 254: + { + PIXEL00_10 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 253: + { + PIXEL00_12 + PIXEL01_11 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 251: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 239: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 127: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 191: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_11 + PIXEL11_12 + break; + } + case 223: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_10 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 247: + { + PIXEL00_11 + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_12 + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 255: + { + if (diff(W4, W2)) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (diff(W2, W6)) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + if (diff(W8, W4)) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (diff(W6, W8)) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } diff --git a/bsnes/lib/libfilter/libfilter.cpp b/bsnes/lib/libfilter/libfilter.cpp new file mode 100755 index 0000000..4ce046e --- /dev/null +++ b/bsnes/lib/libfilter/libfilter.cpp @@ -0,0 +1,14 @@ +#include "libfilter.hpp" +using nall::min; +using nall::max; + +namespace libfilter { + #include "colortable.cpp" + #include "filter.cpp" + + #include "direct.cpp" + #include "scanline.cpp" + #include "scale2x.cpp" + #include "hq2x.cpp" + #include "ntsc.cpp" +} //namespace libfilter diff --git a/bsnes/lib/libfilter/libfilter.hpp b/bsnes/lib/libfilter/libfilter.hpp new file mode 100755 index 0000000..338e12e --- /dev/null +++ b/bsnes/lib/libfilter/libfilter.hpp @@ -0,0 +1,22 @@ +#ifndef LIBFILTER_H +#define LIBFILTER_H + +#include +#include +#include +#include +#include +#include + +namespace libfilter { + #include "colortable.hpp" + #include "filter.hpp" + + #include "direct.hpp" + #include "scanline.hpp" + #include "scale2x.hpp" + #include "hq2x.hpp" + #include "ntsc.hpp" +} //namespace libfilter + +#endif //ifndef LIBFILTER_H diff --git a/bsnes/lib/libfilter/ntsc.cpp b/bsnes/lib/libfilter/ntsc.cpp new file mode 100755 index 0000000..c66029c --- /dev/null +++ b/bsnes/lib/libfilter/ntsc.cpp @@ -0,0 +1,73 @@ +#include "snes_ntsc/snes_ntsc.c" + +NTSCFilter filter_ntsc; + +void NTSCFilter::render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height +) { + if(!ntsc)return; + + int const out_width = outwidth = SNES_NTSC_OUT_WIDTH(256); + int const out_height = outheight = height; + burst ^= burst_toggle; + + //blit multiple scanlines of same width, rather than one at a time + int run_start = 0; + int run_width = line[0]; + int l = 0; + + while(1) { + if(run_width != line[l] || l >= out_height) { + uint16_t const *in = (uint16_t*)((uint8_t*)input + pitch * run_start); + uint16_t *out = (uint16_t*)((uint8_t*)output + outpitch * run_start); + int height = l - run_start; + int line_burst = (burst + run_start) % 3; + if(run_width == 256) { + snes_ntsc_blit(ntsc, in, (pitch >> 1), line_burst, out_width, height, out, outpitch); + } else { + snes_ntsc_blit_hires(ntsc, in, (pitch >> 1), line_burst, out_width, height, out, outpitch); + } + if(l >= out_height) break; + run_width = line[l]; + run_start = l; + } + l++; + } +} + +void NTSCFilter::adjust( + float hue, float saturation, float contrast, + float brightness, float sharpness, bool merge_fields +) { + static snes_ntsc_setup_t defaults; + snes_ntsc_setup_t setup = defaults; + setup.hue = hue; + setup.saturation = saturation; + setup.contrast = contrast; + setup.brightness = brightness; + setup.sharpness = sharpness; + setup.resolution = sharpness; + setup.merge_fields = merge_fields; + setup.bsnes_colortbl = 0; + + if(!ntsc) { + ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc); + if(!ntsc) { + return; //to do: report out of memory error + } + } + + burst = 0; + burst_toggle = (merge_fields ? 0 : 1); //don't toggle burst when fields are merged + snes_ntsc_init(ntsc, &setup); +} + +NTSCFilter::NTSCFilter() { + ntsc = 0; + adjust(0, 0, 0, 0, 0, false); +} + +NTSCFilter::~NTSCFilter() { + if(ntsc) free(ntsc); +} diff --git a/bsnes/lib/libfilter/ntsc.hpp b/bsnes/lib/libfilter/ntsc.hpp new file mode 100755 index 0000000..496e791 --- /dev/null +++ b/bsnes/lib/libfilter/ntsc.hpp @@ -0,0 +1,16 @@ +#include "snes_ntsc/snes_ntsc.h" + +class NTSCFilter : public Filter { +public: + void render(uint32_t*, unsigned, unsigned&, unsigned&, uint16_t*, unsigned, unsigned*, unsigned, unsigned); + void adjust(float hue, float saturation, float contrast, float brightness, float sharpness, bool merge_fields); + + NTSCFilter(); + ~NTSCFilter(); + +private: + struct snes_ntsc_t *ntsc; + int burst, burst_toggle; +}; + +extern NTSCFilter filter_ntsc; diff --git a/bsnes/lib/libfilter/scale2x.cpp b/bsnes/lib/libfilter/scale2x.cpp new file mode 100755 index 0000000..687a568 --- /dev/null +++ b/bsnes/lib/libfilter/scale2x.cpp @@ -0,0 +1,47 @@ +Scale2xFilter filter_scale2x; + +void Scale2xFilter::render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height +) { + if(width > 256 || height > 240) { + filter_direct.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + return; + } + + pitch >>= 1; + outpitch >>= 2; + + uint32_t A, B, C, D, P; + int prevline, nextline; + + for(int y = 0; y < height; y++) { + prevline = (y > 0) ? -pitch : 0; + nextline = (y < height - 1) ? pitch : 0; + for(int x = 0; x < 256; x++) { + A = *(input + prevline); + B = (x > 0) ? *(input - 1) : *input; + C = (x < 255) ? *(input + 1) : *input; + D = *(input + nextline); + P = colortable[*input]; + if(A != D && B != C) { + *(output) = A == B ? colortable[A] : P; + *(output + 1) = A == C ? colortable[A] : P; + *(output + outpitch) = D == B ? colortable[D] : P; + *(output + outpitch + 1) = D == C ? colortable[D] : P; + } else { + *(output) = P; + *(output + 1) = P; + *(output + outpitch) = P; + *(output + outpitch + 1) = P; + } + input++; + output += 2; + } + input += pitch - 256; + output += outpitch + outpitch - 512; + } + + outwidth = width * 2; + outheight = height * 2; +} diff --git a/bsnes/lib/libfilter/scale2x.hpp b/bsnes/lib/libfilter/scale2x.hpp new file mode 100755 index 0000000..00f5289 --- /dev/null +++ b/bsnes/lib/libfilter/scale2x.hpp @@ -0,0 +1,6 @@ +class Scale2xFilter : public Filter { +public: + void render(uint32_t*, unsigned, unsigned&, unsigned&, uint16_t*, unsigned, unsigned*, unsigned, unsigned); +}; + +extern Scale2xFilter filter_scale2x; diff --git a/bsnes/lib/libfilter/scanline.cpp b/bsnes/lib/libfilter/scanline.cpp new file mode 100755 index 0000000..af402e7 --- /dev/null +++ b/bsnes/lib/libfilter/scanline.cpp @@ -0,0 +1,40 @@ +ScanlineFilter filter_scanline; + +void ScanlineFilter::render( + uint32_t *output, unsigned outpitch, unsigned &outwidth, unsigned &outheight, + uint16_t *input, unsigned pitch, unsigned *line, unsigned width, unsigned height +) { + if(height > 240) { + filter_direct.render(output, outpitch, outwidth, outheight, input, pitch, line, width, height); + return; + } + + pitch >>= 1; + outpitch >>= 2; + + for(unsigned y = 0; y < height; y++) { + uint32_t *out0 = output; + uint32_t *out1 = output + pitch; + if(width == 512 && line[y] == 256) { + for(unsigned x = 0; x < 256; x++) { + uint16_t p = *input++; + *out0++ = colortable[p]; + *out0++ = colortable[p]; + *out1++ = (colortable[p] >> 1) & 0x7f7f7f; + *out1++ = (colortable[p] >> 1) & 0x7f7f7f; + } + input += 256; + } else { + for(unsigned x = 0; x < width; x++) { + uint16_t p = *input++; + *out0++ = colortable[p]; + *out1++ = (colortable[p] >> 1) & 0x7f7f7f; + } + } + input += pitch - width; + output += outpitch * 2; + } + + outwidth = width; + outheight = height * 2; +} diff --git a/bsnes/lib/libfilter/scanline.hpp b/bsnes/lib/libfilter/scanline.hpp new file mode 100755 index 0000000..c1b804c --- /dev/null +++ b/bsnes/lib/libfilter/scanline.hpp @@ -0,0 +1,6 @@ +class ScanlineFilter : public Filter { +public: + void render(uint32_t*, unsigned, unsigned&, unsigned&, uint16_t*, unsigned, unsigned*, unsigned, unsigned); +}; + +extern ScanlineFilter filter_scanline; diff --git a/bsnes/lib/libfilter/snes_ntsc/snes_ntsc.c b/bsnes/lib/libfilter/snes_ntsc/snes_ntsc.c new file mode 100755 index 0000000..815f215 --- /dev/null +++ b/bsnes/lib/libfilter/snes_ntsc/snes_ntsc.c @@ -0,0 +1,251 @@ +/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */ + +#include "snes_ntsc.h" + +/* Copyright (C) 2006-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0 }; +snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; +snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0 }; +snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0 }; + +#define alignment_count 3 +#define burst_count 3 +#define rescale_in 8 +#define rescale_out 7 + +#define artifacts_mid 1.0f +#define fringing_mid 1.0f +#define std_decoder_hue 0 + +#define rgb_bits 7 /* half normal range to allow for doubled hires pixels */ +#define gamma_size 32 + +#include "snes_ntsc_impl.h" + +/* 3 input pixels -> 8 composite samples */ +pixel_info_t const snes_ntsc_pixels [alignment_count] = { + { PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } }, + { PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } }, + { PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } }, +}; + +static void merge_kernel_fields( snes_ntsc_rgb_t* io ) +{ + int n; + for ( n = burst_size; n; --n ) + { + snes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias; + snes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias; + snes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias; + /* merge colors without losing precision */ + io [burst_size * 0] = + ((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; + io [burst_size * 1] = + ((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; + io [burst_size * 2] = + ((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; + ++io; + } +} + +static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out ) +{ + int n; + for ( n = burst_count; n; --n ) + { + unsigned i; + for ( i = 0; i < rgb_kernel_size / 2; i++ ) + { + snes_ntsc_rgb_t error = color - + out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] - + out [i + 7] - out [i + 5 +14] - out [i + 3 +28]; + DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 ); + } + out += alignment_count * rgb_kernel_size; + } +} + +void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup ) +{ + int merge_fields; + int entry; + init_t impl; + if ( !setup ) + setup = &snes_ntsc_composite; + init( &impl, setup ); + + merge_fields = setup->merge_fields; + if ( setup->artifacts <= -1 && setup->fringing <= -1 ) + merge_fields = 1; + + for ( entry = 0; entry < snes_ntsc_palette_size; entry++ ) + { + /* Reduce number of significant bits of source color. Clearing the + low bits of R and B were least notictable. Modifying green was too + noticeable. */ + int ir = entry >> 8 & 0x1E; + int ig = entry >> 4 & 0x1F; + int ib = entry << 1 & 0x1E; + + #if SNES_NTSC_BSNES_COLORTBL + if ( setup->bsnes_colortbl ) + { + int bgr15 = (ib << 10) | (ig << 5) | ir; + unsigned long rgb16 = setup->bsnes_colortbl [bgr15]; + ir = rgb16 >> 11 & 0x1E; + ig = rgb16 >> 6 & 0x1F; + ib = rgb16 & 0x1E; + } + #endif + + { + float rr = impl.to_float [ir]; + float gg = impl.to_float [ig]; + float bb = impl.to_float [ib]; + + float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i ); + + int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); + snes_ntsc_rgb_t rgb = PACK_RGB( r, g, b ); + + snes_ntsc_rgb_t* out = ntsc->table [entry]; + gen_kernel( &impl, y, i, q, out ); + if ( merge_fields ) + merge_kernel_fields( out ); + correct_errors( rgb, out ); + } + } +} + +#ifndef SNES_NTSC_NO_BLITTERS + +void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, + int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) +{ + int chunk_count = (in_width - 1) / snes_ntsc_in_chunk; + for ( ; in_height; --in_height ) + { + SNES_NTSC_IN_T const* line_in = input; + SNES_NTSC_BEGIN_ROW( ntsc, burst_phase, + snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) ); + snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; + int n; + ++line_in; + + for ( n = chunk_count; n; --n ) + { + /* order of input and output pixels must not be altered */ + SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) ); + SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) ); + SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) ); + SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + line_in += 3; + line_out += 7; + } + + /* finish final pixels */ + SNES_NTSC_COLOR_IN( 0, snes_ntsc_black ); + SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, snes_ntsc_black ); + SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, snes_ntsc_black ); + SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; + input += in_row_width; + rgb_out = (char*) rgb_out + out_pitch; + } +} + +void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, + int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) +{ + int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2); + for ( ; in_height; --in_height ) + { + SNES_NTSC_IN_T const* line_in = input; + SNES_NTSC_HIRES_ROW( ntsc, burst_phase, + snes_ntsc_black, snes_ntsc_black, snes_ntsc_black, + SNES_NTSC_ADJ_IN( line_in [0] ), + SNES_NTSC_ADJ_IN( line_in [1] ) ); + snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; + int n; + line_in += 2; + + for ( n = chunk_count; n; --n ) + { + /* twice as many input pixels per chunk */ + SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) ); + SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) ); + SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) ); + SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) ); + SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) ); + SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) ); + SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + line_in += 6; + line_out += 7; + } + + SNES_NTSC_COLOR_IN( 0, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 3, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 4, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 5, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; + input += in_row_width; + rgb_out = (char*) rgb_out + out_pitch; + } +} + +#endif diff --git a/bsnes/lib/libfilter/snes_ntsc/snes_ntsc.h b/bsnes/lib/libfilter/snes_ntsc/snes_ntsc.h new file mode 100755 index 0000000..37b3c7b --- /dev/null +++ b/bsnes/lib/libfilter/snes_ntsc/snes_ntsc.h @@ -0,0 +1,228 @@ +/* SNES NTSC video filter */ + +/* snes_ntsc 0.2.2 */ +#ifndef SNES_NTSC_H +#define SNES_NTSC_H + +#include "snes_ntsc_config.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown +in parenthesis and should remain fairly stable in future versions. */ +typedef struct snes_ntsc_setup_t +{ + /* Basic parameters */ + double hue; /* -1 = -180 degrees +1 = +180 degrees */ + double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ + double contrast; /* -1 = dark (0.5) +1 = light (1.5) */ + double brightness; /* -1 = dark (0.5) +1 = light (1.5) */ + double sharpness; /* edge contrast enhancement/blurring */ + + /* Advanced parameters */ + double gamma; /* -1 = dark (1.5) +1 = light (0.5) */ + double resolution; /* image resolution */ + double artifacts; /* artifacts caused by color changes */ + double fringing; /* color artifacts caused by brightness changes */ + double bleed; /* color bleed (color resolution reduction) */ + int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */ + float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ + + unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */ +} snes_ntsc_setup_t; + +/* Video format presets */ +extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */ +extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */ +extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */ +extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */ + +/* Initializes and adjusts parameters. Can be called multiple times on the same +snes_ntsc_t object. Can pass NULL for either parameter. */ +typedef struct snes_ntsc_t snes_ntsc_t; +void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup ); + +/* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT +and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB. +In_row_width is the number of pixels to get to the next input row. Out_pitch +is the number of *bytes* to get to the next output row. */ +void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, + long in_row_width, int burst_phase, int in_width, int in_height, + void* rgb_out, long out_pitch ); + +void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, + long in_row_width, int burst_phase, int in_width, int in_height, + void* rgb_out, long out_pitch ); + +/* Number of output pixels written by low-res blitter for given input width. Width +might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded +value. Guaranteed not to round 256 down at all. */ +#define SNES_NTSC_OUT_WIDTH( in_width ) \ + ((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk) + +/* Number of low-res input pixels that will fit within given output width. Might be +rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded +value. */ +#define SNES_NTSC_IN_WIDTH( out_width ) \ + (((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1) + + +/* Interface for user-defined custom blitters */ + +enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */ +enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */ +enum { snes_ntsc_black = 0 }; /* palette index for black */ +enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */ + +/* Begins outputting row and starts three pixels. First pixel will be cut off a bit. +Use snes_ntsc_black for unused pixels. Declares variables, so must be before first +statement in a block (unless you're using C++). */ +#define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \ + char const* ktable = \ + (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\ + SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable ) + +/* Begins input pixel */ +#define SNES_NTSC_COLOR_IN( index, color ) \ + SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable ) + +/* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0: +24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB) +16: RRRRRGGG GGGBBBBB (5-6-5 RGB) +15: RRRRRGG GGGBBBBB (5-5-5 RGB) +14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format) + 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ +#define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \ + SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 ) + +/* Hires equivalents */ +#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \ + char const* ktable = \ + (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\ + unsigned const snes_ntsc_pixel1_ = (pixel1);\ + snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\ + unsigned const snes_ntsc_pixel2_ = (pixel2);\ + snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\ + unsigned const snes_ntsc_pixel3_ = (pixel3);\ + snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\ + unsigned const snes_ntsc_pixel4_ = (pixel4);\ + snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\ + unsigned const snes_ntsc_pixel5_ = (pixel5);\ + snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\ + snes_ntsc_rgb_t const* kernel0 = kernel1;\ + snes_ntsc_rgb_t const* kernelx0;\ + snes_ntsc_rgb_t const* kernelx1 = kernel1;\ + snes_ntsc_rgb_t const* kernelx2 = kernel1;\ + snes_ntsc_rgb_t const* kernelx3 = kernel1;\ + snes_ntsc_rgb_t const* kernelx4 = kernel1;\ + snes_ntsc_rgb_t const* kernelx5 = kernel1 + +#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\ + snes_ntsc_rgb_t raw_ =\ + kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\ + kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\ + kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\ + kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\ + SNES_NTSC_CLAMP_( raw_, 0 );\ + SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\ +} + + +/* private */ +enum { snes_ntsc_entry_size = 128 }; +enum { snes_ntsc_palette_size = 0x2000 }; +typedef unsigned long snes_ntsc_rgb_t; +struct snes_ntsc_t { + snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size]; +}; +enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count }; + +#define SNES_NTSC_RGB16( ktable, n ) \ + (snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \ + (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) + +#define SNES_NTSC_BGR15( ktable, n ) \ + (snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \ + (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) + +/* common 3->7 ntsc macros */ +#define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \ + unsigned const snes_ntsc_pixel0_ = (pixel0);\ + snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\ + unsigned const snes_ntsc_pixel1_ = (pixel1);\ + snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\ + unsigned const snes_ntsc_pixel2_ = (pixel2);\ + snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\ + snes_ntsc_rgb_t const* kernelx0;\ + snes_ntsc_rgb_t const* kernelx1 = kernel0;\ + snes_ntsc_rgb_t const* kernelx2 = kernel0 + +#define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\ + snes_ntsc_rgb_t raw_ =\ + kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ + kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ + SNES_NTSC_CLAMP_( raw_, shift );\ + SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\ +} + +/* common ntsc macros */ +#define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) +#define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2) +#define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101) +#define SNES_NTSC_CLAMP_( io, shift ) {\ + snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\ + snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\ + io |= clamp;\ + clamp -= sub;\ + io &= clamp;\ +} + +#define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ + unsigned color_;\ + kernelx##index = kernel##index;\ + kernel##index = (color_ = (color), ENTRY( table, color_ ));\ +} + +/* x is always zero except in snes_ntsc library */ +/* original routine */ +/* +#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ + if ( bits == 16 )\ + rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ + if ( bits == 24 || bits == 32 )\ + rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ + if ( bits == 15 )\ + rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ + if ( bits == 14 )\ + rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\ + if ( bits == 0 )\ + rgb_out = raw_ << x;\ +} +*/ + +/* custom bsnes routine -- hooks into bsnes colortable */ +#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ + if ( bits == 16 ) {\ + rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ + rgb_out = ((rgb_out&0xf800)>>11)|((rgb_out&0x07c0)>>1)|((rgb_out&0x001f)<<10);\ + rgb_out = colortable[rgb_out];\ + } else if ( bits == 24 || bits == 32 ) {\ + rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ + rgb_out = ((rgb_out&0xf80000)>>19)|((rgb_out&0x00f800)>>6)|((rgb_out&0x0000f8)<<7);\ + rgb_out = colortable[rgb_out];\ + } else if ( bits == 15 ) {\ + rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ + rgb_out = ((rgb_out&0x7c00)>>10)|((rgb_out&0x03e0))|((rgb_out&0x001f)<<10);\ + rgb_out = colortable[rgb_out];\ + } else {\ + rgb_out = raw_ << x;\ + }\ +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/bsnes/lib/libfilter/snes_ntsc/snes_ntsc_config.h b/bsnes/lib/libfilter/snes_ntsc/snes_ntsc_config.h new file mode 100755 index 0000000..5c85e26 --- /dev/null +++ b/bsnes/lib/libfilter/snes_ntsc/snes_ntsc_config.h @@ -0,0 +1,26 @@ +/* Configure library by modifying this file */ + +#ifndef SNES_NTSC_CONFIG_H +#define SNES_NTSC_CONFIG_H + +/* Format of source pixels */ +/* #define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16 */ +#define SNES_NTSC_IN_FORMAT SNES_NTSC_BGR15 + +/* The following affect the built-in blitter only; a custom blitter can +handle things however it wants. */ + +/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */ +#define SNES_NTSC_OUT_DEPTH 32 + +/* Type of input pixel values */ +#define SNES_NTSC_IN_T unsigned short + +/* Each raw pixel input value is passed through this. You might want to mask +the pixel index if you use the high bits as flags, etc. */ +#define SNES_NTSC_ADJ_IN( in ) in + +/* For each pixel, this is the basic operation: +output_color = SNES_NTSC_ADJ_IN( SNES_NTSC_IN_T ) */ + +#endif diff --git a/bsnes/lib/libfilter/snes_ntsc/snes_ntsc_impl.h b/bsnes/lib/libfilter/snes_ntsc/snes_ntsc_impl.h new file mode 100755 index 0000000..03e5df9 --- /dev/null +++ b/bsnes/lib/libfilter/snes_ntsc/snes_ntsc_impl.h @@ -0,0 +1,439 @@ +/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */ + +/* Common implementation of NTSC filters */ + +#include +#include + +/* Copyright (C) 2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#define DISABLE_CORRECTION 0 + +#undef PI +#define PI 3.14159265358979323846f + +#ifndef LUMA_CUTOFF + #define LUMA_CUTOFF 0.20 +#endif +#ifndef gamma_size + #define gamma_size 1 +#endif +#ifndef rgb_bits + #define rgb_bits 8 +#endif +#ifndef artifacts_max + #define artifacts_max (artifacts_mid * 1.5f) +#endif +#ifndef fringing_max + #define fringing_max (fringing_mid * 2) +#endif +#ifndef STD_HUE_CONDITION + #define STD_HUE_CONDITION( setup ) 1 +#endif + +#define ext_decoder_hue (std_decoder_hue + 15) +#define rgb_unit (1 << rgb_bits) +#define rgb_offset (rgb_unit * 2 + 0.5f) + +enum { burst_size = snes_ntsc_entry_size / burst_count }; +enum { kernel_half = 16 }; +enum { kernel_size = kernel_half * 2 + 1 }; + +typedef struct init_t +{ + float to_rgb [burst_count * 6]; + float to_float [gamma_size]; + float contrast; + float brightness; + float artifacts; + float fringing; + float kernel [rescale_out * kernel_size * 2]; +} init_t; + +#define ROTATE_IQ( i, q, sin_b, cos_b ) {\ + float t;\ + t = i * cos_b - q * sin_b;\ + q = i * sin_b + q * cos_b;\ + i = t;\ +} + +static void init_filters( init_t* impl, snes_ntsc_setup_t const* setup ) +{ +#if rescale_out > 1 + float kernels [kernel_size * 2]; +#else + float* const kernels = impl->kernel; +#endif + + /* generate luma (y) filter using sinc kernel */ + { + /* sinc with rolloff (dsf) */ + float const rolloff = 1 + (float) setup->sharpness * (float) 0.032; + float const maxh = 32; + float const pow_a_n = (float) pow( rolloff, maxh ); + float sum; + int i; + /* quadratic mapping to reduce negative (blurring) range */ + float to_angle = (float) setup->resolution + 1; + to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1); + + kernels [kernel_size * 3 / 2] = maxh; /* default center value */ + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + int x = i - kernel_half; + float angle = x * to_angle; + /* instability occurs at center point with rolloff very close to 1.0 */ + if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 ) + { + float rolloff_cos_a = rolloff * (float) cos( angle ); + float num = 1 - rolloff_cos_a - + pow_a_n * (float) cos( maxh * angle ) + + pow_a_n * rolloff * (float) cos( (maxh - 1) * angle ); + float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; + float dsf = num / den; + kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5; + } + } + + /* apply blackman window and find sum */ + sum = 0; + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + float x = PI * 2 / (kernel_half * 2) * i; + float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 ); + sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman); + } + + /* normalize kernel */ + sum = 1.0f / sum; + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + int x = kernel_size * 3 / 2 - kernel_half + i; + kernels [x] *= sum; + assert( kernels [x] == kernels [x] ); /* catch numerical instability */ + } + } + + /* generate chroma (iq) filter using gaussian kernel */ + { + float const cutoff_factor = -0.03125f; + float cutoff = (float) setup->bleed; + int i; + + if ( cutoff < 0 ) + { + /* keep extreme value accessible only near upper end of scale (1.0) */ + cutoff *= cutoff; + cutoff *= cutoff; + cutoff *= cutoff; + cutoff *= -30.0f / 0.65f; + } + cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff; + + for ( i = -kernel_half; i <= kernel_half; i++ ) + kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff ); + + /* normalize even and odd phases separately */ + for ( i = 0; i < 2; i++ ) + { + float sum = 0; + int x; + for ( x = i; x < kernel_size; x += 2 ) + sum += kernels [x]; + + sum = 1.0f / sum; + for ( x = i; x < kernel_size; x += 2 ) + { + kernels [x] *= sum; + assert( kernels [x] == kernels [x] ); /* catch numerical instability */ + } + } + } + + /* + printf( "luma:\n" ); + for ( i = kernel_size; i < kernel_size * 2; i++ ) + printf( "%f\n", kernels [i] ); + printf( "chroma:\n" ); + for ( i = 0; i < kernel_size; i++ ) + printf( "%f\n", kernels [i] ); + */ + + /* generate linear rescale kernels */ + #if rescale_out > 1 + { + float weight = 1.0f; + float* out = impl->kernel; + int n = rescale_out; + do + { + float remain = 0; + int i; + weight -= 1.0f / rescale_in; + for ( i = 0; i < kernel_size * 2; i++ ) + { + float cur = kernels [i]; + float m = cur * weight; + *out++ = m + remain; + remain = cur - m; + } + } + while ( --n ); + } + #endif +} + +static float const default_decoder [6] = + { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f }; + +static void init( init_t* impl, snes_ntsc_setup_t const* setup ) +{ + impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset; + impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit; + #ifdef default_palette_contrast + if ( !setup->palette ) + impl->contrast *= default_palette_contrast; + #endif + + impl->artifacts = (float) setup->artifacts; + if ( impl->artifacts > 0 ) + impl->artifacts *= artifacts_max - artifacts_mid; + impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid; + + impl->fringing = (float) setup->fringing; + if ( impl->fringing > 0 ) + impl->fringing *= fringing_max - fringing_mid; + impl->fringing = impl->fringing * fringing_mid + fringing_mid; + + init_filters( impl, setup ); + + /* generate gamma table */ + if ( gamma_size > 1 ) + { + float const to_float = 1.0f / (gamma_size - (gamma_size > 1)); + float const gamma = 1.1333f - (float) setup->gamma * 0.5f; + /* match common PC's 2.2 gamma to TV's 2.65 gamma */ + int i; + for ( i = 0; i < gamma_size; i++ ) + impl->to_float [i] = + (float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness; + } + + /* setup decoder matricies */ + { + float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue; + float sat = (float) setup->saturation + 1; + float const* decoder = setup->decoder_matrix; + if ( !decoder ) + { + decoder = default_decoder; + if ( STD_HUE_CONDITION( setup ) ) + hue += PI / 180 * (std_decoder_hue - ext_decoder_hue); + } + + { + float s = (float) sin( hue ) * sat; + float c = (float) cos( hue ) * sat; + float* out = impl->to_rgb; + int n; + + n = burst_count; + do + { + float const* in = decoder; + int n = 3; + do + { + float i = *in++; + float q = *in++; + *out++ = i * c - q * s; + *out++ = i * s + q * c; + } + while ( --n ); + if ( burst_count <= 1 ) + break; + ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */ + } + while ( --n ); + } + } +} + +/* kernel generation */ + +#define RGB_TO_YIQ( r, g, b, y, i ) (\ + (y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\ + (i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\ + ((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\ +) + +#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\ + r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\ + g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\ + (type) (y + to_rgb [4] * i + to_rgb [5] * q)\ +) + +#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) + +enum { rgb_kernel_size = burst_size / alignment_count }; +enum { rgb_bias = rgb_unit * 2 * snes_ntsc_rgb_builder }; + +typedef struct pixel_info_t +{ + int offset; + float negate; + float kernel [4]; +} pixel_info_t; + +#if rescale_in > 1 + #define PIXEL_OFFSET_( ntsc, scaled ) \ + (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \ + (kernel_size * 2 * scaled)) + + #define PIXEL_OFFSET( ntsc, scaled ) \ + PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\ + (((scaled) + rescale_out * 10) % rescale_out) ),\ + (1.0f - (((ntsc) + 100) & 2)) +#else + #define PIXEL_OFFSET( ntsc, scaled ) \ + (kernel_size / 2 + (ntsc) - (scaled)),\ + (1.0f - (((ntsc) + 100) & 2)) +#endif + +extern pixel_info_t const snes_ntsc_pixels [alignment_count]; + +/* Generate pixel at all burst phases and column alignments */ +static void gen_kernel( init_t* impl, float y, float i, float q, snes_ntsc_rgb_t* out ) +{ + /* generate for each scanline burst phase */ + float const* to_rgb = impl->to_rgb; + int burst_remain = burst_count; + y -= rgb_offset; + do + { + /* Encode yiq into *two* composite signals (to allow control over artifacting). + Convolve these with kernels which: filter respective components, apply + sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack + into integer. Based on algorithm by NewRisingSun. */ + pixel_info_t const* pixel = snes_ntsc_pixels; + int alignment_remain = alignment_count; + do + { + /* negate is -1 when composite starts at odd multiple of 2 */ + float const yy = y * impl->fringing * pixel->negate; + float const ic0 = (i + yy) * pixel->kernel [0]; + float const qc1 = (q + yy) * pixel->kernel [1]; + float const ic2 = (i - yy) * pixel->kernel [2]; + float const qc3 = (q - yy) * pixel->kernel [3]; + + float const factor = impl->artifacts * pixel->negate; + float const ii = i * factor; + float const yc0 = (y + ii) * pixel->kernel [0]; + float const yc2 = (y - ii) * pixel->kernel [2]; + + float const qq = q * factor; + float const yc1 = (y + qq) * pixel->kernel [1]; + float const yc3 = (y - qq) * pixel->kernel [3]; + + float const* k = &impl->kernel [pixel->offset]; + int n; + ++pixel; + for ( n = rgb_kernel_size; n; --n ) + { + float i = k[0]*ic0 + k[2]*ic2; + float q = k[1]*qc1 + k[3]*qc3; + float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 + + k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset; + if ( rescale_out <= 1 ) + k--; + else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] ) + k += kernel_size * 2 - 1; + else + k -= kernel_size * 2 * (rescale_out - 1) + 2; + { + int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g ); + *out++ = PACK_RGB( r, g, b ) - rgb_bias; + } + } + } + while ( alignment_count > 1 && --alignment_remain ); + + if ( burst_count <= 1 ) + break; + + to_rgb += 6; + + ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */ + } + while ( --burst_remain ); +} + +static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out ); + +#if DISABLE_CORRECTION + #define CORRECT_ERROR( a ) { out [i] += rgb_bias; } + #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; } +#else + #define CORRECT_ERROR( a ) { out [a] += error; } + #define DISTRIBUTE_ERROR( a, b, c ) {\ + snes_ntsc_rgb_t fourth = (error + 2 * snes_ntsc_rgb_builder) >> 2;\ + fourth &= (rgb_bias >> 1) - snes_ntsc_rgb_builder;\ + fourth -= rgb_bias >> 2;\ + out [a] += fourth;\ + out [b] += fourth;\ + out [c] += fourth;\ + out [i] += error - (fourth * 3);\ + } +#endif + +#define RGB_PALETTE_OUT( rgb, out_ )\ +{\ + unsigned char* out = (out_);\ + snes_ntsc_rgb_t clamped = (rgb);\ + SNES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\ + out [0] = (unsigned char) (clamped >> 21);\ + out [1] = (unsigned char) (clamped >> 11);\ + out [2] = (unsigned char) (clamped >> 1);\ +} + +/* blitter related */ + +#ifndef restrict + #if defined (__GNUC__) + #define restrict __restrict__ + #elif defined (_MSC_VER) && _MSC_VER > 1300 + #define restrict __restrict + #else + /* no support for restricted pointers */ + #define restrict + #endif +#endif + +#include + +#if SNES_NTSC_OUT_DEPTH <= 16 + #if USHRT_MAX == 0xFFFF + typedef unsigned short snes_ntsc_out_t; + #else + #error "Need 16-bit int type" + #endif + +#else + #if UINT_MAX == 0xFFFFFFFF + typedef unsigned int snes_ntsc_out_t; + #elif ULONG_MAX == 0xFFFFFFFF + typedef unsigned long snes_ntsc_out_t; + #else + #error "Need 32-bit int type" + #endif + +#endif diff --git a/bsnes/lib/nall/Makefile.string b/bsnes/lib/nall/Makefile.string new file mode 100755 index 0000000..0de1db2 --- /dev/null +++ b/bsnes/lib/nall/Makefile.string @@ -0,0 +1,63 @@ +# Makefile.string +# author: byuu +# license: public domain + +[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z +[0-9] = 0 1 2 3 4 5 6 7 8 9 +[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ? +[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup]) +[space] := +[space] += + +##### +# function strtr(source, from, to) +##### +strtr = \ + $(eval __temp := $1) \ + $(strip \ + $(foreach c, \ + $(join $(addsuffix :,$2),$3), \ + $(eval __temp := \ + $(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) + +##### +# function strupper(source) +##### +strupper = $(call strtr,$1,$([a-z]),$([A-Z])) + +##### +# function strlower(source) +##### +strlower = $(call strtr,$1,$([A-Z]),$([a-z])) + +##### +# function strlen(source) +##### +strlen = \ + $(eval __temp := $(subst $([space]),_,$1)) \ + $(words \ + $(strip \ + $(foreach c, \ + $([all]), \ + $(eval __temp := \ + $(subst $c,$c ,$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) \ + ) + +##### +# function streq(source) +##### +streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1) + +##### +# function strne(source) +##### +strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,) diff --git a/bsnes/lib/nall/algorithm.hpp b/bsnes/lib/nall/algorithm.hpp new file mode 100755 index 0000000..98b3952 --- /dev/null +++ b/bsnes/lib/nall/algorithm.hpp @@ -0,0 +1,23 @@ +#ifndef NALL_ALGORITHM_HPP +#define NALL_ALGORITHM_HPP + +#undef min +#undef max + +namespace nall { + template T min(const T& t, const U& u) { + return t < u ? t : u; + } + + template T max(const T& t, const U& u) { + return t > u ? t : u; + } + + //pseudo-random number generator + inline unsigned prng() { + static unsigned n = 0; + return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); + } +} + +#endif diff --git a/bsnes/lib/nall/any.hpp b/bsnes/lib/nall/any.hpp new file mode 100755 index 0000000..9689af1 --- /dev/null +++ b/bsnes/lib/nall/any.hpp @@ -0,0 +1,74 @@ +#ifndef NALL_ANY_HPP +#define NALL_ANY_HPP + +#include +#include +#include + +namespace nall { + class any { + public: + bool empty() const { return container; } + const std::type_info& type() const { return container ? container->type() : typeid(void); } + + template any& operator=(const T& value_) { + typedef typename static_if< + is_array::value, + typename remove_extent::type>::type*, + T + >::type auto_t; + + if(type() == typeid(auto_t)) { + static_cast*>(container)->value = (auto_t)value_; + } else { + if(container) delete container; + container = new holder((auto_t)value_); + } + + return *this; + } + + any() : container(0) {} + template any(const T& value_) : container(0) { operator=(value_); } + + private: + struct placeholder { + virtual const std::type_info& type() const = 0; + } *container; + + template struct holder : placeholder { + T value; + const std::type_info& type() const { return typeid(T); } + holder(const T& value_) : value(value_) {} + }; + + template friend T any_cast(any&); + template friend T any_cast(const any&); + template friend T* any_cast(any*); + template friend const T* any_cast(const any*); + }; + + template T any_cast(any &value) { + typedef typename remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T any_cast(const any &value) { + typedef const typename remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T* any_cast(any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } + + template const T* any_cast(const any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } +} + +#endif diff --git a/bsnes/lib/nall/array.hpp b/bsnes/lib/nall/array.hpp new file mode 100755 index 0000000..62d5486 --- /dev/null +++ b/bsnes/lib/nall/array.hpp @@ -0,0 +1,94 @@ +#ifndef NALL_ARRAY_HPP +#define NALL_ARRAY_HPP + +#include +#include +#include + +namespace nall { + //dynamic vector array + //neither constructor nor destructor is ever invoked; + //thus, this should only be used for POD objects. + template class array { + protected: + T *pool; + unsigned poolsize, buffersize; + + public: + unsigned size() const { return buffersize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) free(pool); + pool = 0; + poolsize = 0; + buffersize = 0; + } + + void reserve(unsigned newsize) { + if(newsize == poolsize) return; + + pool = (T*)realloc(pool, newsize * sizeof(T)); + poolsize = newsize; + buffersize = min(buffersize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2 + buffersize = newsize; + } + + T* get(unsigned minsize = 0) { + if(minsize > buffersize) resize(minsize); + if(minsize > buffersize) throw "array[] out of bounds"; + return pool; + } + + void add(const T data) { + operator[](buffersize) = data; + } + + signed find(const T data) { + for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return i; + return -1; //not found + } + + void clear() { + memset(pool, 0, buffersize * sizeof(T)); + } + + array() { + pool = 0; + poolsize = 0; + buffersize = 0; + } + + ~array() { reset(); } + + array(const array &source) : pool(0) { + operator=(source); + } + + array& operator=(const array &source) { + if(pool) free(pool); + buffersize = source.buffersize; + poolsize = source.poolsize; + pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size, + memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects + return *this; + } + + inline T& operator[](unsigned index) { + if(index >= buffersize) resize(index + 1); + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + }; +} + +#endif diff --git a/bsnes/lib/nall/base64.hpp b/bsnes/lib/nall/base64.hpp new file mode 100755 index 0000000..8b107b2 --- /dev/null +++ b/bsnes/lib/nall/base64.hpp @@ -0,0 +1,92 @@ +#ifndef NALL_BASE64_HPP +#define NALL_BASE64_HPP + +#include + +#include +#include + +namespace nall { + class base64 { + public: + static bool encode(char *&output, const uint8_t* input, unsigned inlength) { + output = new(zeromemory) char[inlength * 8 / 6 + 6]; + + unsigned i = 0, o = 0; + while(i < inlength) { + switch(i % 3) { + case 0: { + output[o++] = enc(input[i] >> 2); + output[o] = enc((input[i] & 3) << 4); + } break; + + case 1: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 4)); + output[o] = enc((input[i] & 15) << 2); + } break; + + case 2: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 6)); + output[o++] = enc(input[i] & 63); + } break; + } + + i++; + } + + return true; + } + + static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { + unsigned inlength = strlen(input), infix = 0; + output = new(zeromemory) uint8_t[inlength]; + + unsigned i = 0, o = 0; + while(i < inlength) { + uint8_t x = dec(input[i]); + + switch(i++ & 3) { + case 0: { + output[o] = x << 2; + } break; + + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + } break; + + case 2: { + output[o++] |= x >> 2; + output[o] = (x & 3) << 6; + } break; + + case 3: { + output[o++] |= x; + } break; + } + } + + outlength = o; + return true; + } + + private: + static char enc(uint8_t n) { + static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return lookup_table[n & 63]; + } + + static uint8_t dec(char n) { + if(n >= 'A' && n <= 'Z') return n - 'A'; + if(n >= 'a' && n <= 'z') return n - 'a' + 26; + if(n >= '0' && n <= '9') return n - '0' + 52; + if(n == '-') return 62; + if(n == '_') return 63; + return 0; + } + }; +} + +#endif diff --git a/bsnes/lib/nall/bit.hpp b/bsnes/lib/nall/bit.hpp new file mode 100755 index 0000000..169fc14 --- /dev/null +++ b/bsnes/lib/nall/bit.hpp @@ -0,0 +1,51 @@ +#ifndef NALL_BIT_HPP +#define NALL_BIT_HPP + +namespace nall { + template inline unsigned uclamp(const unsigned x) { + enum { y = (1U << bits) - 1 }; + return y + ((x - y) & -(x < y)); //min(x, y); + } + + template inline unsigned uclip(const unsigned x) { + enum { m = (1U << bits) - 1 }; + return (x & m); + } + + template inline signed sclamp(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 }; + return (x > m) ? m : (x < -b) ? -b : x; + } + + template inline signed sclip(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << bits) - 1 }; + return ((x & m) ^ b) - b; + } + + namespace bit { + //lowest(0b1110) == 0b0010 + template inline T lowest(const T x) { + return x & -x; + } + + //clear_lowest(0b1110) == 0b1100 + template inline T clear_lowest(const T x) { + return x & (x - 1); + } + + //set_lowest(0b0101) == 0b0111 + template inline T set_lowest(const T x) { + return x | (x + 1); + } + + //round up to next highest single bit: + //round(15) == 16, round(16) == 16, round(17) == 32 + inline unsigned round(unsigned x) { + if((x & (x - 1)) == 0) return x; + while(x & (x - 1)) x &= x - 1; + return x << 1; + } + } +} + +#endif diff --git a/bsnes/lib/nall/config.hpp b/bsnes/lib/nall/config.hpp new file mode 100755 index 0000000..4dacd52 --- /dev/null +++ b/bsnes/lib/nall/config.hpp @@ -0,0 +1,124 @@ +#ifndef NALL_CONFIG_HPP +#define NALL_CONFIG_HPP + +#include +#include +#include + +namespace nall { + namespace configuration_traits { + template struct is_boolean { enum { value = false }; }; + template<> struct is_boolean { enum { value = true }; }; + + template struct is_signed { enum { value = false }; }; + template<> struct is_signed { enum { value = true }; }; + + template struct is_unsigned { enum { value = false }; }; + template<> struct is_unsigned { enum { value = true }; }; + + template struct is_double { enum { value = false }; }; + template<> struct is_double { enum { value = true }; }; + + template struct is_string { enum { value = false }; }; + template<> struct is_string { enum { value = true }; }; + } + + class configuration { + public: + enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t }; + struct item_t { + uintptr_t data; + string name; + string desc; + type_t type; + + string get() const { + switch(type) { + case boolean_t: return string() << *(bool*)data; + case signed_t: return string() << *(signed*)data; + case unsigned_t: return string() << *(unsigned*)data; + case double_t: return string() << *(double*)data; + case string_t: return string() << "\"" << *(string*)data << "\""; + } + return "???"; + } + + void set(string s) { + switch(type) { + case boolean_t: *(bool*)data = (s == "true"); break; + case signed_t: *(signed*)data = strsigned(s); break; + case unsigned_t: *(unsigned*)data = strunsigned(s); break; + case double_t: *(double*)data = strdouble(s); break; + case string_t: trim(s, "\""); *(string*)data = s; break; + } + } + }; + vector list; + + template + void attach(T &data, const char *name, const char *desc = "") { + unsigned n = list.size(); + list[n].data = (uintptr_t)&data; + list[n].name = name; + list[n].desc = desc; + + if(configuration_traits::is_boolean::value) list[n].type = boolean_t; + else if(configuration_traits::is_signed::value) list[n].type = signed_t; + else if(configuration_traits::is_unsigned::value) list[n].type = unsigned_t; + else if(configuration_traits::is_double::value) list[n].type = double_t; + else if(configuration_traits::is_string::value) list[n].type = string_t; + else list[n].type = unknown_t; + } + + virtual bool load(const char *filename) { + string data; + if(data.readfile(filename) == true) { + data.replace("\r", ""); + lstring line; + line.split("\n", data); + + for(unsigned i = 0; i < line.size(); i++) { + int position = qstrpos(line[i], "#"); + if(position >= 0) line[i][position] = 0; + if(qstrpos(line[i], " = ") < 0) continue; + + lstring part; + part.qsplit(" = ", line[i]); + trim(part[0]); + trim(part[1]); + + for(unsigned n = 0; n < list.size(); n++) { + if(part[0] == list[n].name) { + list[n].set(part[1]); + break; + } + } + } + + return true; + } else { + return false; + } + } + + virtual bool save(const char *filename) const { + file fp; + if(fp.open(filename, file::mode_write)) { + for(unsigned i = 0; i < list.size(); i++) { + string output; + output << list[i].name << " = " << list[i].get(); + if(list[i].desc != "") output << " # " << list[i].desc; + output << "\r\n"; + fp.print(output); + } + + fp.close(); + return true; + } else { + return false; + } + } + }; +} + +#endif diff --git a/bsnes/lib/nall/crc32.hpp b/bsnes/lib/nall/crc32.hpp new file mode 100755 index 0000000..ad36fbf --- /dev/null +++ b/bsnes/lib/nall/crc32.hpp @@ -0,0 +1,66 @@ +#ifndef NALL_CRC32_HPP +#define NALL_CRC32_HPP + +#include + +namespace nall { + const uint32_t crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + + inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) { + return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff]; + } + + inline uint32_t crc32_calculate(const uint8_t *data, unsigned length) { + uint32_t crc32 = ~0; + for(unsigned i = 0; i < length; i++) { + crc32 = crc32_adjust(crc32, data[i]); + } + return ~crc32; + } +} + +#endif diff --git a/bsnes/lib/nall/detect.hpp b/bsnes/lib/nall/detect.hpp new file mode 100755 index 0000000..98c017e --- /dev/null +++ b/bsnes/lib/nall/detect.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_DETECT_HPP +#define NALL_DETECT_HPP + +/* Compiler detection */ + +#if defined(__GNUC__) + #define COMPILER_GCC +#elif defined(_MSC_VER) + #define COMPILER_VISUALC +#endif + +/* Platform detection */ + +#if defined(_WIN32) + #define PLATFORM_WIN +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define PLATFORM_X +#endif + +/* Endian detection */ + +#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64) + #define ARCH_LSB +#elif defined(__powerpc__) || defined(_M_PPC) + #define ARCH_MSB +#endif + +#endif diff --git a/bsnes/lib/nall/dictionary.hpp b/bsnes/lib/nall/dictionary.hpp new file mode 100755 index 0000000..35128c2 --- /dev/null +++ b/bsnes/lib/nall/dictionary.hpp @@ -0,0 +1,73 @@ +#ifndef NALL_DICTIONARY_HPP +#define NALL_DICTIONARY_HPP + +#include +#include +#include + +namespace nall { + class dictionary : noncopyable { + public: + string operator[](const char *input) { + for(unsigned i = 0; i < index_input.size(); i++) { + if(index_input[i] == input) return index_output[i]; + } + + //no match, use input; remove input identifier, if one exists + if(strbegin(input, "{{")) { + int pos = strpos(input, "}}"); + if(pos >= 0) { + string temp = substr(input, pos + 2); + return temp; + } + } + + return input; + } + + bool import(const char *filename) { + string data; + if(data.readfile(filename) == false) return false; + ltrim_once(data, "\xef\xbb\xbf"); //remove UTF-8 marker, if it exists + data.replace("\r", ""); + + lstring line; + line.split("\n", data); + for(unsigned i = 0; i < line.size(); i++) { + lstring part; + //format: "Input" = "Output" + part.qsplit("=", line[i]); + if(part.size() != 2) continue; + + //remove whitespace + trim(part[0]); + trim(part[1]); + + //remove quotes + trim_once(part[0], "\""); + trim_once(part[1], "\""); + + unsigned n = index_input.size(); + index_input[n] = part[0]; + index_output[n] = part[1]; + } + + return true; + } + + void reset() { + index_input.reset(); + index_output.reset(); + } + + ~dictionary() { + reset(); + } + + protected: + lstring index_input; + lstring index_output; + }; +} + +#endif diff --git a/bsnes/lib/nall/endian.hpp b/bsnes/lib/nall/endian.hpp new file mode 100755 index 0000000..40d1563 --- /dev/null +++ b/bsnes/lib/nall/endian.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_ENDIAN_HPP +#define NALL_ENDIAN_HPP + +#if !defined(ARCH_MSB) + //little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 + #define order_lsb2(a,b) a,b + #define order_lsb3(a,b,c) a,b,c + #define order_lsb4(a,b,c,d) a,b,c,d + #define order_lsb5(a,b,c,d,e) a,b,c,d,e + #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h + #define order_msb2(a,b) b,a + #define order_msb3(a,b,c) c,b,a + #define order_msb4(a,b,c,d) d,c,b,a + #define order_msb5(a,b,c,d,e) e,d,c,b,a + #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a +#else + //big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 + #define order_lsb2(a,b) b,a + #define order_lsb3(a,b,c) c,b,a + #define order_lsb4(a,b,c,d) d,c,b,a + #define order_lsb5(a,b,c,d,e) e,d,c,b,a + #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a + #define order_msb2(a,b) a,b + #define order_msb3(a,b,c) a,b,c + #define order_msb4(a,b,c,d) a,b,c,d + #define order_msb5(a,b,c,d,e) a,b,c,d,e + #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h +#endif + +#endif diff --git a/bsnes/lib/nall/file.hpp b/bsnes/lib/nall/file.hpp new file mode 100755 index 0000000..dc4da3a --- /dev/null +++ b/bsnes/lib/nall/file.hpp @@ -0,0 +1,218 @@ +#ifndef NALL_FILE_HPP +#define NALL_FILE_HPP + +#include +#include + +#include +#include +#include + +namespace nall { + class file : noncopyable { + public: + enum FileMode { mode_read, mode_write, mode_readwrite, mode_writeread }; + enum SeekMode { seek_absolute, seek_relative }; + + uint8_t read() { + if(!fp) return 0xff; //file not open + if(file_mode == mode_write) return 0xff; //reads not permitted + if(file_offset >= file_size) return 0xff; //cannot read past end of file + buffer_sync(); + return buffer[(file_offset++) & buffer_mask]; + } + + uintmax_t readl(unsigned length = 1) { + uintmax_t data = 0; + for(int i = 0; i < length; i++) { + data |= (uintmax_t)read() << (i << 3); + } + return data; + } + + uintmax_t readm(unsigned length = 1) { + uintmax_t data = 0; + while(length--) { + data <<= 8; + data |= read(); + } + return data; + } + + void read(uint8_t *buffer, unsigned length) { + while(length--) *buffer++ = read(); + } + + void write(uint8_t data) { + if(!fp) return; //file not open + if(file_mode == mode_read) return; //writes not permitted + buffer_sync(); + buffer[(file_offset++) & buffer_mask] = data; + buffer_dirty = true; + if(file_offset > file_size) file_size = file_offset; + } + + void writel(uintmax_t data, unsigned length = 1) { + while(length--) { + write(data); + data >>= 8; + } + } + + void writem(uintmax_t data, unsigned length = 1) { + for(int i = length - 1; i >= 0; i--) { + write(data >> (i << 3)); + } + } + + void write(const uint8_t *buffer, unsigned length) { + while(length--) write(*buffer++); + } + + void print(const char *string) { + if(!string) return; + while(*string) write(*string++); + } + + void flush() { + buffer_flush(); + fflush(fp); + } + + void seek(int offset, SeekMode mode = seek_absolute) { + if(!fp) return; //file not open + buffer_flush(); + + uintmax_t req_offset = file_offset; + switch(mode) { + case seek_absolute: req_offset = offset; break; + case seek_relative: req_offset += offset; break; + } + + if(req_offset < 0) req_offset = 0; //cannot seek before start of file + if(req_offset > file_size) { + if(file_mode == mode_read) { //cannot seek past end of file + req_offset = file_size; + } else { //pad file to requested location + file_offset = file_size; + while(file_size < req_offset) write(0x00); + } + } + + file_offset = req_offset; + } + + int offset() { + if(!fp) return -1; //file not open + return file_offset; + } + + int size() { + if(!fp) return -1; //file not open + return file_size; + } + + bool end() { + if(!fp) return true; //file not open + return file_offset >= file_size; + } + + static bool exists(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + if(fp) { + fclose(fp); + return true; + } + return false; + } + + bool open() { + return fp; + } + + bool open(const char *fn, FileMode mode) { + if(fp) return false; + + switch(file_mode = mode) { + #if !defined(_WIN32) + case mode_read: fp = fopen(fn, "rb"); break; + case mode_write: fp = fopen(fn, "wb+"); break; //need read permission for buffering + case mode_readwrite: fp = fopen(fn, "rb+"); break; + case mode_writeread: fp = fopen(fn, "wb+"); break; + #else + case mode_read: fp = _wfopen(utf16_t(fn), L"rb"); break; + case mode_write: fp = _wfopen(utf16_t(fn), L"wb+"); break; + case mode_readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break; + case mode_writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break; + #endif + } + if(!fp) return false; + buffer_offset = -1; //invalidate buffer + file_offset = 0; + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + return true; + } + + void close() { + if(!fp) return; + buffer_flush(); + fclose(fp); + fp = 0; + } + + file() { + memset(buffer, 0, sizeof buffer); + buffer_offset = -1; + buffer_dirty = false; + fp = 0; + file_offset = 0; + file_size = 0; + file_mode = mode_read; + } + + ~file() { + close(); + } + + private: + enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 }; + char buffer[buffer_size]; + int buffer_offset; + bool buffer_dirty; + FILE *fp; + unsigned file_offset; + unsigned file_size; + FileMode file_mode; + + void buffer_sync() { + if(!fp) return; //file not open + if(buffer_offset != (file_offset & ~buffer_mask)) { + buffer_flush(); + buffer_offset = file_offset & ~buffer_mask; + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fread(buffer, 1, length, fp); + } + } + + void buffer_flush() { + if(!fp) return; //file not open + if(file_mode == mode_read) return; //buffer cannot be written to + if(buffer_offset < 0) return; //buffer unused + if(buffer_dirty == false) return; //buffer unmodified since read + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fwrite(buffer, 1, length, fp); + buffer_offset = -1; //invalidate buffer + buffer_dirty = false; + } + }; +} + +#endif diff --git a/bsnes/lib/nall/filemap.hpp b/bsnes/lib/nall/filemap.hpp new file mode 100755 index 0000000..a05f0eb --- /dev/null +++ b/bsnes/lib/nall/filemap.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_FILEMAP_HPP +#define NALL_FILEMAP_HPP + +#include +#include + +#include +#include +#if defined(_WIN32) + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace nall { + class filemap { + public: + enum filemode { mode_read, mode_write, mode_readwrite, mode_writeread }; + + bool open(const char *filename, filemode mode) { return p_open(filename, mode); } + void close() { return p_close(); } + unsigned size() const { return p_size; } + uint8_t* handle() { return p_handle; } + const uint8_t* handle() const { return p_handle; } + filemap() : p_size(0), p_handle(0) { p_ctor(); } + ~filemap() { p_dtor(); } + + private: + unsigned p_size; + uint8_t *p_handle; + + #if defined(_WIN32) + //============= + //MapViewOfFile + //============= + + HANDLE p_filehandle, p_maphandle; + + bool p_open(const char *filename, filemode mode) { + int desired_access, creation_disposition, flprotect, map_access; + + switch(mode) { + default: return false; + case mode_read: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READONLY; + map_access = FILE_MAP_READ; + break; + case mode_write: + //write access requires read access + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode_readwrite: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode_writeread: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_NEW; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + } + + p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL, + creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if(p_filehandle == INVALID_HANDLE_VALUE) return false; + + p_size = GetFileSize(p_filehandle, NULL); + + p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL); + if(p_maphandle == INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + return false; + } + + p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size); + return p_handle; + } + + void p_close() { + if(p_handle) { + UnmapViewOfFile(p_handle); + p_handle = 0; + } + + if(p_maphandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_maphandle); + p_maphandle = INVALID_HANDLE_VALUE; + } + + if(p_filehandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + } + } + + void p_ctor() { + p_filehandle = INVALID_HANDLE_VALUE; + p_maphandle = INVALID_HANDLE_VALUE; + } + + void p_dtor() { + close(); + } + + #else + //==== + //mmap + //==== + + int p_fd; + + bool p_open(const char *filename, filemode mode) { + int open_flags, mmap_flags; + + switch(mode) { + default: return false; + case mode_read: + open_flags = O_RDONLY; + mmap_flags = PROT_READ; + break; + case mode_write: + open_flags = O_RDWR | O_CREAT; //mmap() requires read access + mmap_flags = PROT_WRITE; + break; + case mode_readwrite: + open_flags = O_RDWR; + mmap_flags = PROT_READ | PROT_WRITE; + break; + case mode_writeread: + open_flags = O_RDWR | O_CREAT; + mmap_flags = PROT_READ | PROT_WRITE; + break; + } + + p_fd = ::open(filename, open_flags); + if(p_fd < 0) return false; + + struct stat p_stat; + fstat(p_fd, &p_stat); + p_size = p_stat.st_size; + + p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0); + if(p_handle == MAP_FAILED) { + p_handle = 0; + ::close(p_fd); + p_fd = -1; + return false; + } + + return p_handle; + } + + void p_close() { + if(p_handle) { + munmap(p_handle, p_size); + p_handle = 0; + } + + if(p_fd >= 0) { + ::close(p_fd); + p_fd = -1; + } + } + + void p_ctor() { + p_fd = -1; + } + + void p_dtor() { + p_close(); + } + + #endif + }; +} + +#endif diff --git a/bsnes/lib/nall/function.hpp b/bsnes/lib/nall/function.hpp new file mode 100755 index 0000000..9e90cb1 --- /dev/null +++ b/bsnes/lib/nall/function.hpp @@ -0,0 +1,184 @@ +#ifndef NALL_FUNCTION_HPP +#define NALL_FUNCTION_HPP + +#include + +//prologue + +#define TN typename + +namespace nall { + template class function; +} + +//parameters = 0 + +#define cat(n) n +#define TL typename R +#define PL +#define CL + +#include "function.hpp" + +//parameters = 1 + +#define cat(n) , n +#define TL TN R, TN P1 +#define PL P1 p1 +#define CL p1 + +#include "function.hpp" + +//parameters = 2 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2 +#define PL P1 p1, P2 p2 +#define CL p1, p2 + +#include "function.hpp" + +//parameters = 3 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3 +#define PL P1 p1, P2 p2, P3 p3 +#define CL p1, p2, p3 + +#include "function.hpp" + +//parameters = 4 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4 +#define PL P1 p1, P2 p2, P3 p3, P4 p4 +#define CL p1, p2, p3, p4 + +#include "function.hpp" + +//parameters = 5 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5 +#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5 +#define CL p1, p2, p3, p4, p5 + +#include "function.hpp" + +//parameters = 6 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5, TN P6 +#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6 +#define CL p1, p2, p3, p4, p5, p6 + +#include "function.hpp" + +//parameters = 7 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5, TN P6, TN P7 +#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7 +#define CL p1, p2, p3, p4, p5, p6, p7 + +#include "function.hpp" + +//parameters = 8 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5, TN P6, TN P7, TN P8 +#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8 +#define CL p1, p2, p3, p4, p5, p6, p7, p8 + +#include "function.hpp" + +//epilogue + +#undef TN +#define NALL_FUNCTION_T + +#elif !defined(NALL_FUNCTION_T) + +//function implementation template class + +namespace nall { + template + class function { + private: + struct base1 { virtual void func1(PL) {} }; + struct base2 { virtual void func2(PL) {} }; + struct derived : base1, virtual base2 {}; + + struct data_t { + R (*fn_call)(const data_t& cat(PL)); + union { + R (*fn_global)(PL); + struct { + R (derived::*fn_member)(PL); + void *object; + }; + }; + } data; + + static R fn_call_global(const data_t &d cat(PL)) { + return d.fn_global(CL); + } + + template + static R fn_call_member(const data_t &d cat(PL)) { + return (((C*)d.object)->*((R (C::*&)(PL))d.fn_member))(CL); + } + + public: + R operator()(PL) const { return data.fn_call(data cat(CL)); } + operator bool() const { return data.fn_call; } + + function() { data.fn_call = 0; } + + function(R (*fn)(PL)) { + data.fn_call = &fn_call_global; + data.fn_global = fn; + } + + template + function(R (C::*fn)(PL), C *obj) { + data.fn_call = &fn_call_member; + (R (C::*&)(PL))data.fn_member = fn; + assert(sizeof data.fn_member >= sizeof fn); + data.object = obj; + } + + template + function(R (C::*fn)(PL) const, C *obj) { + data.fn_call = &fn_call_member; + (R (C::*&)(PL))data.fn_member = (R (C::*&)(PL))fn; + assert(sizeof data.fn_member >= sizeof fn); + data.object = obj; + } + + function &operator=(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); return *this; } + function(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); } + }; + + template + function bind(R (*fn)(PL)) { + return function(fn); + } + + template + function bind(R (C::*fn)(PL), C *obj) { + return function(fn, obj); + } + + template + function bind(R (C::*fn)(PL) const, C *obj) { + return function(fn, obj); + } +} + +#undef cat +#undef TL +#undef PL +#undef CL + +#endif diff --git a/bsnes/lib/nall/input.hpp b/bsnes/lib/nall/input.hpp new file mode 100755 index 0000000..033c26b --- /dev/null +++ b/bsnes/lib/nall/input.hpp @@ -0,0 +1,263 @@ +#ifndef NALL_INPUT_HPP +#define NALL_INPUT_HPP + +#include +#include +#include + +#include + +namespace nall { + enum { input_none = 0 }; + + template struct keyboard { + enum { + none = keyboard::limit, + escape, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, + print_screen, scroll_lock, pause, tilde, + num_1, num_2, num_3, num_4, num_5, num_6, num_7, num_8, num_9, num_0, + dash, equal, backspace, + insert, delete_, home, end, page_up, page_down, + a, b, c, d, e, f, g, h, i, j, k, l, m, + n, o, p, q, r, s, t, u, v, w, x, y, z, + lbracket, rbracket, backslash, semicolon, apostrophe, comma, period, slash, + pad_1, pad_2, pad_3, pad_4, pad_5, pad_6, pad_7, pad_8, pad_9, pad_0, + point, enter, add, subtract, multiply, divide, + num_lock, caps_lock, + up, down, left, right, + tab, return_, spacebar, + lctrl, rctrl, lalt, ralt, lshift, rshift, lsuper, rsuper, menu, + limit, + }; + }; + + template<> struct keyboard<-1> { + enum { count = 16 }; + enum { + none, + escape, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, + print_screen, scroll_lock, pause, tilde, + num_1, num_2, num_3, num_4, num_5, num_6, num_7, num_8, num_9, num_0, + dash, equal, backspace, + insert, delete_, home, end, page_up, page_down, + a, b, c, d, e, f, g, h, i, j, k, l, m, + n, o, p, q, r, s, t, u, v, w, x, y, z, + lbracket, rbracket, backslash, semicolon, apostrophe, comma, period, slash, + pad_1, pad_2, pad_3, pad_4, pad_5, pad_6, pad_7, pad_8, pad_9, pad_0, + point, enter, add, subtract, multiply, divide, + num_lock, caps_lock, + up, down, left, right, + tab, return_, spacebar, + lctrl, rctrl, lalt, ralt, lshift, rshift, lsuper, rsuper, menu, + length, //number of syms per keyboard + limit = 0, + }; + + static uint16_t index(unsigned keyboard_number, unsigned keyboard_enum) { + if(keyboard_number >= count) return input_none; + return limit + keyboard_number * length + keyboard_enum; + } + }; + + template struct mouse { + enum { buttons = 8 }; + enum { + none = mouse::limit, + x, y, z, + button, + limit = button + buttons, + }; + }; + + template<> struct mouse<-1> { + enum { count = 16, buttons = 8 }; + enum { + none, + x, y, z, + button, + length = button + buttons - none, //number of syms per mouse + limit = keyboard::count - 1>::limit, + }; + + static uint16_t index(unsigned mouse_number, unsigned mouse_enum) { + if(mouse_number >= count) return input_none; + return limit + mouse_number * length + mouse_enum; + } + }; + + template struct joypad { + enum { hats = 8, axes = 32, buttons = 96 }; + enum { + none = joypad::limit, + hat, + axis = hat + hats, + button = axis + axes, + limit = button + buttons, + }; + }; + + template<> struct joypad<-1> { + enum { count = 16, hats = 8, axes = 32, buttons = 96 }; + enum { hat_center = 0, hat_up = 1, hat_right = 2, hat_down = 4, hat_left = 8 }; + enum { + none, + hat, + axis = hat + hats, + button = axis + axes, + length = button + buttons - none, //number of syms per joypad + limit = mouse::count - 1>::limit, + }; + + static uint16_t index(unsigned joypad_number, unsigned joypad_enum) { + if(joypad_number >= count) return input_none; + return limit + joypad_number * length + joypad_enum; + } + }; + + enum { input_limit = joypad::count - 1>::limit }; + + static const char keysym[][64] = { + "none", + "escape", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", + "print_screen", "scroll_lock", "pause", "tilde", + "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "num_7", "num_8", "num_9", "num_0", + "dash", "equal", "backspace", + "insert", "delete", "home", "end", "page_up", "page_down", + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", + "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + "lbracket", "rbracket", "backslash", "semicolon", "apostrophe", "comma", "period", "slash", + "pad_1", "pad_2", "pad_3", "pad_4", "pad_5", "pad_6", "pad_7", "pad_8", "pad_9", "pad_0", + "point", "enter", "add", "subtract", "multiply", "divide", + "num_lock", "caps_lock", + "up", "down", "left", "right", + "tab", "return", "spacebar", + "lctrl", "rctrl", "lalt", "ralt", "lshift", "rshift", "lsuper", "rsuper", "menu", + "limit", + }; + + static const char* input_find(uint16_t key) { + static char buffer[64]; + + for(unsigned k = 0; k < keyboard<>::count; k++) { + if(key >= keyboard<>::index(k, keyboard<>::none) && key < keyboard<>::index(k, keyboard<>::length)) { + sprintf(buffer, "keyboard%.2d.%s", k, keysym[key - keyboard<>::index(k, keyboard<>::none)]); + return buffer; + } + } + + for(unsigned m = 0; m < mouse<>::count; m++) { + if(key == mouse<>::index(m, mouse<>::x)) { sprintf(buffer, "mouse%.2d.x", m); return buffer; } + if(key == mouse<>::index(m, mouse<>::y)) { sprintf(buffer, "mouse%.2d.y", m); return buffer; } + if(key == mouse<>::index(m, mouse<>::z)) { sprintf(buffer, "mouse%.2d.z", m); return buffer; } + + if(key >= mouse<>::index(m, mouse<>::button + 0) + && key < mouse<>::index(m, mouse<>::button + mouse<>::buttons)) { + sprintf(buffer, "mouse%.2d.button%.2d", m, key - mouse<>::index(m, mouse<>::button)); + return buffer; + } + } + + for(unsigned j = 0; j < joypad<>::count; j++) { + if(key >= joypad<>::index(j, joypad<>::hat + 0) + && key < joypad<>::index(j, joypad<>::hat + joypad<>::hats)) { + sprintf(buffer, "joypad%.2d.hat%.2d", j, key - joypad<>::index(j, joypad<>::hat)); + return buffer; + } + + if(key >= joypad<>::index(j, joypad<>::axis + 0) + && key < joypad<>::index(j, joypad<>::axis + joypad<>::axes)) { + sprintf(buffer, "joypad%.2d.axis%.2d", j, key - joypad<>::index(j, joypad<>::axis)); + return buffer; + } + + if(key >= joypad<>::index(j, joypad<>::button + 0) + && key < joypad<>::index(j, joypad<>::button + joypad<>::buttons)) { + sprintf(buffer, "joypad%.2d.button%.2d", j, key - joypad<>::index(j, joypad<>::button)); + return buffer; + } + } + + return "none"; + } + + static char* input_find(char *out, uint16_t key) { + strcpy(out, input_find(key)); + return out; + } + + static uint16_t input_find(const char *key) { + if(!memcmp(key, "keyboard", 8)) { + key += 8; + if(!*key || !*(key + 1)) return input_none; + uint8_t k = (*key - '0') * 10 + (*(key + 1) - '0'); + if(k >= keyboard<>::count) return input_none; + key += 2; + + if(*key++ != '.') return input_none; + + for(unsigned i = 0; i < keyboard<>::length; i++) { + if(!strcmp(key, keysym[i])) return keyboard<>::index(k, i); + } + } + + if(!memcmp(key, "mouse", 5)) { + key += 5; + if(!*key || !*(key + 1)) return input_none; + uint8_t m = (*key - '0') * 10 + (*(key + 1) - '0'); + if(m >= mouse<>::count) return input_none; + key += 2; + + if(!strcmp(key, ".x")) return mouse<>::index(m, mouse<>::x); + if(!strcmp(key, ".y")) return mouse<>::index(m, mouse<>::y); + if(!strcmp(key, ".z")) return mouse<>::index(m, mouse<>::z); + + if(!memcmp(key, ".button", 7)) { + key += 7; + if(!*key || !*(key + 1)) return input_none; + uint8_t button = (*key - '0') * 10 + (*(key + 1) - '0'); + if(button >= mouse<>::buttons) return input_none; + return mouse<>::index(m, mouse<>::button + button); + } + + return input_none; + } + + if(!memcmp(key, "joypad", 6)) { + key += 6; + if(!*key || !*(key + 1)) return input_none; + uint8_t j = (*key - '0') * 10 + (*(key + 1) - '0'); + if(j >= joypad<>::count) return input_none; + key += 2; + + if(!memcmp(key, ".hat", 4)) { + key += 4; + if(!*key || !*(key + 1)) return input_none; + uint8_t hat = (*key - '0') * 10 + (*(key + 1) - '0'); + if(hat >= joypad<>::hats) return input_none; + return joypad<>::index(j, joypad<>::hat + hat); + } + + if(!memcmp(key, ".axis", 5)) { + key += 5; + if(!*key || !*(key + 1)) return input_none; + uint8_t axis = (*key - '0') * 10 + (*(key + 1) - '0'); + if(axis >= joypad<>::axes) return input_none; + return joypad<>::index(j, joypad<>::axis + axis); + } + + if(!memcmp(key, ".button", 7)) { + key += 7; + if(!*key || !*(key + 1)) return input_none; + uint8_t button = (*key - '0') * 10 + (*(key + 1) - '0'); + if(button >= joypad<>::buttons) return input_none; + return joypad<>::index(j, joypad<>::button + button); + } + + return input_none; + } + + return input_none; + } +} + +#endif diff --git a/bsnes/lib/nall/lzss.hpp b/bsnes/lib/nall/lzss.hpp new file mode 100755 index 0000000..202bc81 --- /dev/null +++ b/bsnes/lib/nall/lzss.hpp @@ -0,0 +1,81 @@ +#ifndef NALL_LZSS_HPP +#define NALL_LZSS_HPP + +#include +#include +#include + +namespace nall { + class lzss { + public: + static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) { + output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9]; + + unsigned i = 0, o = 0; + while(i < inlength) { + unsigned flagoffset = o++; + uint8_t flag = 0x00; + + for(unsigned b = 0; b < 8 && i < inlength; b++) { + unsigned longest = 0, pointer; + for(unsigned index = 1; index < 4096; index++) { + unsigned count = 0; + while(true) { + if(count >= 15 + 3) break; //verify pattern match is not longer than max length + if(i + count >= inlength) break; //verify pattern match does not read past end of input + if(i + count < index) break; //verify read is not before start of input + if(input[i + count] != input[i + count - index]) break; //verify pattern still matches + count++; + } + + if(count > longest) { + longest = count; + pointer = index; + } + } + + if(longest < 3) output[o++] = input[i++]; + else { + flag |= 1 << b; + uint16_t x = ((longest - 3) << 12) + pointer; + output[o++] = x; + output[o++] = x >> 8; + i += longest; + } + } + + output[flagoffset] = flag; + } + + outlength = o; + return true; + } + + static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) { + output = new(zeromemory) uint8_t[length]; + + unsigned i = 0, o = 0; + while(o < length) { + uint8_t flag = input[i++]; + + for(unsigned b = 0; b < 8 && o < length; b++) { + if(!(flag & (1 << b))) output[o++] = input[i++]; + else { + uint16_t offset = input[i++]; + offset += input[i++] << 8; + uint16_t lookuplength = (offset >> 12) + 3; + offset &= 4095; + for(unsigned index = 0; index < lookuplength && o + index < length; index++) { + output[o + index] = output[o + index - offset]; + } + o += lookuplength; + } + } + } + + return true; + } + }; +} + +#endif diff --git a/bsnes/lib/nall/moduloarray.hpp b/bsnes/lib/nall/moduloarray.hpp new file mode 100755 index 0000000..30e0977 --- /dev/null +++ b/bsnes/lib/nall/moduloarray.hpp @@ -0,0 +1,36 @@ +#ifndef NALL_MODULO_HPP +#define NALL_MODULO_HPP + +#include + +namespace nall { + template class modulo_array { + public: + inline T operator[](int index) const { + return buffer[size + index]; + } + + inline T read(int index) const { + return buffer[size + index]; + } + + inline void write(unsigned index, const T value) { + buffer[index] = + buffer[index + size] = + buffer[index + size + size] = value; + } + + modulo_array() { + buffer = new(zeromemory) T[size * 3]; + } + + ~modulo_array() { + delete[] buffer; + } + + private: + T *buffer; + }; +} + +#endif diff --git a/bsnes/lib/nall/new.hpp b/bsnes/lib/nall/new.hpp new file mode 100755 index 0000000..abc2ca5 --- /dev/null +++ b/bsnes/lib/nall/new.hpp @@ -0,0 +1,25 @@ +#ifndef NALL_NEW_HPP +#define NALL_NEW_HPP + +#include +#include +#include + +namespace nall { + struct zeromemory_t {}; + static zeromemory_t zeromemory; +} + +inline void* operator new[](size_t size, const nall::zeromemory_t&) throw(std::bad_alloc) { + void *p = new uint8_t[size]; + memset(p, 0, size); + return p; +} + +inline void* operator new[](size_t size, const std::nothrow_t&, const nall::zeromemory_t&) throw() { + void *p = new(std::nothrow) uint8_t[size]; + if(p) memset(p, 0, size); + return p; +} + +#endif diff --git a/bsnes/lib/nall/platform.hpp b/bsnes/lib/nall/platform.hpp new file mode 100755 index 0000000..bdae675 --- /dev/null +++ b/bsnes/lib/nall/platform.hpp @@ -0,0 +1,75 @@ +#ifndef NALL_PLATFORM_HPP +#define NALL_PLATFORM_HPP + +//========================= +//standard platform headers +//========================= + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) + #include + #include + #include +#else + #include + #include + #include +#endif + +//================== +//warning supression +//================== + +//Visual C++ +#if defined(_MSC_VER) + //disable libc "deprecation" warnings + #pragma warning(disable:4996) +#endif + +//================ +//POSIX compliance +//================ + +#if defined(_MSC_VER) + #define PATH_MAX _MAX_PATH + #define va_copy(dest, src) ((dest) = (src)) +#endif + +#if defined(_WIN32) + #define getcwd _getcwd + #define ftruncate _chsize + #define putenv _putenv + #define rmdir _rmdir + #define vsnprintf _vsnprintf + #define usleep(n) Sleep(n / 1000) +#endif + +//================ +//inline expansion +//================ + +#if defined(__GNUC__) + #define noinline __attribute__((noinline)) + #define inline inline + #define alwaysinline __attribute__((always_inline)) +#elif defined(_MSC_VER) + #define noinline __declspec(noinline) + #define inline inline + #define alwaysinline __forceinline +#else + #define noinline + #define inline inline + #define alwaysinline inline +#endif + +#endif diff --git a/bsnes/lib/nall/priorityqueue.hpp b/bsnes/lib/nall/priorityqueue.hpp new file mode 100755 index 0000000..3daccfa --- /dev/null +++ b/bsnes/lib/nall/priorityqueue.hpp @@ -0,0 +1,94 @@ +#ifndef NALL_PRIORITYQUEUE_HPP +#define NALL_PRIORITYQUEUE_HPP + +#include +#include +#include + +namespace nall { + template void priority_queue_nocallback(type_t) {} + + //priority queue implementation using binary min-heap array; + //does not require normalize() function. + //O(1) find (tick) + //O(log n) insert (enqueue) + //O(log n) remove (dequeue) + template class priority_queue : noncopyable { + public: + inline void tick(unsigned ticks) { + basecounter += ticks; + while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue()); + } + + //counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks); + //counter cannot exceed std::numeric_limits::max() >> 1. + void enqueue(unsigned counter, type_t event) { + unsigned child = heapsize++; + counter += basecounter; + + while(child) { + unsigned parent = (child - 1) >> 1; + if(gte(counter, heap[parent].counter)) break; + + heap[child].counter = heap[parent].counter; + heap[child].event = heap[parent].event; + child = parent; + } + + heap[child].counter = counter; + heap[child].event = event; + } + + type_t dequeue() { + type_t event(heap[0].event); + unsigned parent = 0; + unsigned counter = heap[--heapsize].counter; + + while(true) { + unsigned child = (parent << 1) + 1; + if(child >= heapsize) break; + if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++; + if(gte(heap[child].counter, counter)) break; + + heap[parent].counter = heap[child].counter; + heap[parent].event = heap[child].event; + parent = child; + } + + heap[parent].counter = counter; + heap[parent].event = heap[heapsize].event; + return event; + } + + void reset() { + basecounter = 0; + heapsize = 0; + } + + priority_queue(unsigned size, function callback_ = &priority_queue_nocallback) + : callback(callback_) { + heap = new heap_t[size]; + reset(); + } + + ~priority_queue() { + delete[] heap; + } + + private: + function callback; + unsigned basecounter; + unsigned heapsize; + struct heap_t { + unsigned counter; + type_t event; + } *heap; + + //return true if x is greater than or equal to y + inline bool gte(unsigned x, unsigned y) { + return x - y < (std::numeric_limits::max() >> 1); + } + }; +} + +#endif diff --git a/bsnes/lib/nall/property.hpp b/bsnes/lib/nall/property.hpp new file mode 100755 index 0000000..0099939 --- /dev/null +++ b/bsnes/lib/nall/property.hpp @@ -0,0 +1,45 @@ +#ifndef NALL_PROPERTY_HPP +#define NALL_PROPERTY_HPP + +//nall::property implements a variable container that disallows write access +//to non-derived objects. This requires use of property::set(), as C++ lacks +//the ability to make this implementation completely transparent. + +namespace nall { + class property { + public: + template class property_t; + + protected: + template T& get(property_t&); + template property_t& set(property_t&, const T); + + public: + template + class property_t { + public: + const T& operator()() const { return value; } + property_t() : value() {} + property_t(const T value_) : value(value_) {} + + protected: + T value; + operator T&() { return value; } + property_t& operator=(const T newValue) { value = newValue; return *this; } + friend T& property::get(property_t&); + friend property_t& property::set(property_t&, const T); + }; + }; + + template + T& property::get(property::property_t &p) { + return p.operator T&(); + } + + template + property::property_t& property::set(property::property_t &p, const T value) { + return p.operator=(value); + } +} + +#endif diff --git a/bsnes/lib/nall/serial.hpp b/bsnes/lib/nall/serial.hpp new file mode 100755 index 0000000..6f5cf6d --- /dev/null +++ b/bsnes/lib/nall/serial.hpp @@ -0,0 +1,80 @@ +#ifndef NALL_SERIAL_HPP +#define NALL_SERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace nall { + class serial { + public: + //-1 on error, otherwise return bytes read + int read(uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::read(port, (void*)data, length); + } + + //-1 on error, otherwise return bytes written + int write(const uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::write(port, (void*)data, length); + } + + bool open(const char *portname, unsigned rate) { + close(); + + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; + + if(ioctl(port, TIOCEXCL) == -1) { close(); return false; } + if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; } + if(tcgetattr(port, &original_attr) == -1) { close(); return false; } + + termios attr = original_attr; + cfmakeraw(&attr); + cfsetspeed(&attr, rate); + + attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN); + attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + attr.c_iflag |= (IGNBRK | IGNPAR); + attr.c_oflag &=~ (OPOST); + attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB); + attr.c_cflag |= (CS8 | CREAD | CLOCAL); + attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0; + + if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; } + return port_open = true; + } + + void close() { + if(port != -1) { + tcdrain(port); + if(port_open == true) { + tcsetattr(port, TCSANOW, &original_attr); + port_open = false; + } + ::close(port); + port = -1; + } + } + + serial() { + port = -1; + port_open = false; + } + + ~serial() { + close(); + } + + private: + int port; + bool port_open; + termios original_attr; + }; +} + +#endif diff --git a/bsnes/lib/nall/sort.hpp b/bsnes/lib/nall/sort.hpp new file mode 100755 index 0000000..23c317a --- /dev/null +++ b/bsnes/lib/nall/sort.hpp @@ -0,0 +1,62 @@ +#ifndef NALL_SORT_HPP +#define NALL_SORT_HPP + +#include + +//class: merge sort +//average: O(n log n) +//worst: O(n log n) +//memory: O(n) +//stack: O(log n) +//stable?: yes + +//notes: +//there are two primary reasons for choosing merge sort +//over the (usually) faster quick sort*: +//1: it is a stable sort. +//2: it lacks O(n^2) worst-case overhead. +//(* which is also O(n log n) in the average case.) + +namespace nall { + template + void sort(T list[], unsigned length) { + if(length <= 1) return; //nothing to sort + + //use insertion sort to quickly sort smaller blocks + if(length < 64) { + for(unsigned i = 0; i < length; i++) { + unsigned min = i; + for(unsigned j = i + 1; j < length; j++) { + if(list[j] < list[min]) min = j; + } + if(min != i) swap(list[i], list[min]); + } + return; + } + + //split list in half and recursively sort both + unsigned middle = length / 2; + sort(list, middle); + sort(list + middle, length - middle); + + //left and right are sorted here; perform merge sort + T *buffer = new T[length]; + unsigned offset = 0; + unsigned left = 0; + unsigned right = middle; + while(left < middle && right < length) { + if(list[left] < list[right]) { + buffer[offset++] = list[left++]; + } else { + buffer[offset++] = list[right++]; + } + } + while(left < middle) buffer[offset++] = list[left++]; + while(right < length) buffer[offset++] = list[right++]; + + for(unsigned i = 0; i < length; i++) list[i] = buffer[i]; + delete[] buffer; + } +} + +#endif diff --git a/bsnes/lib/nall/static.hpp b/bsnes/lib/nall/static.hpp new file mode 100755 index 0000000..00c3664 --- /dev/null +++ b/bsnes/lib/nall/static.hpp @@ -0,0 +1,17 @@ +#ifndef NALL_STATIC_HPP +#define NALL_STATIC_HPP + +namespace nall { + template struct static_assert; + template<> struct static_assert {}; + + template struct static_if { + typedef true_type type; + }; + + template struct static_if { + typedef false_type type; + }; +} + +#endif diff --git a/bsnes/lib/nall/stdint.hpp b/bsnes/lib/nall/stdint.hpp new file mode 100755 index 0000000..7e2c7a4 --- /dev/null +++ b/bsnes/lib/nall/stdint.hpp @@ -0,0 +1,44 @@ +#ifndef NALL_STDINT_HPP +#define NALL_STDINT_HPP + +#include + +#if defined(_MSC_VER) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef signed long long int64_t; + typedef int64_t intmax_t; + #if defined(_WIN64) + typedef int64_t intptr_t; + #else + typedef int32_t intptr_t; + #endif + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef unsigned long long uint64_t; + typedef uint64_t uintmax_t; + #if defined(_WIN64) + typedef uint64_t uintptr_t; + #else + typedef uint32_t uintptr_t; + #endif +#else + #include +#endif + +namespace nall { + static static_assert int8_t_assert; + static static_assert int16_t_assert; + static static_assert int32_t_assert; + static static_assert int64_t_assert; + + static static_assert uint8_t_assert; + static static_assert uint16_t_assert; + static static_assert uint32_t_assert; + static static_assert uint64_t_assert; +} + +#endif diff --git a/bsnes/lib/nall/string.cpp b/bsnes/lib/nall/string.cpp new file mode 100755 index 0000000..7894c7c --- /dev/null +++ b/bsnes/lib/nall/string.cpp @@ -0,0 +1,24 @@ +#ifndef NALL_STRING_CPP +#define NALL_STRING_CPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + #include + #include + #include +} + +#endif diff --git a/bsnes/lib/nall/string.hpp b/bsnes/lib/nall/string.hpp new file mode 100755 index 0000000..a47e244 --- /dev/null +++ b/bsnes/lib/nall/string.hpp @@ -0,0 +1,175 @@ +#ifndef NALL_STRING_HPP +#define NALL_STRING_HPP + +#include +#include +#include +#include +#include + +//=============== +//libc extensions +//=============== + +//compare.cpp +char chrlower(char c); +char chrupper(char c); + +int stricmp(const char *dest, const char *src); + +int strpos (const char *str, const char *key); +int qstrpos(const char *str, const char *key); + +bool strbegin (const char *str, const char *key); +bool stribegin(const char *str, const char *key); + +bool strend (const char *str, const char *key); +bool striend(const char *str, const char *key); + +//convert.cpp +char* strlower(char *str); +char* strupper(char *str); + +char* strtr(char *dest, const char *before, const char *after); + +uintmax_t strhex (const char *str); +intmax_t strsigned (const char *str); +uintmax_t strunsigned(const char *str); +uintmax_t strbin (const char *str); +double strdouble (const char *str); + +size_t strhex (char *str, uintmax_t value, size_t length = 0); +size_t strsigned (char *str, intmax_t value, size_t length = 0); +size_t strunsigned(char *str, uintmax_t value, size_t length = 0); +size_t strbin (char *str, uintmax_t value, size_t length = 0); +size_t strdouble (char *str, double value, size_t length = 0); + +//match.cpp +bool match(const char *pattern, const char *str); + +//math.cpp +bool strint (const char *str, int &result); +bool strmath(const char *str, int &result); + +//strl.cpp +size_t strlcpy(char *dest, const char *src, size_t length); +size_t strlcat(char *dest, const char *src, size_t length); + +//trim.cpp +char* ltrim(char *str, const char *key = " "); +char* rtrim(char *str, const char *key = " "); +char* trim (char *str, const char *key = " "); + +char* ltrim_once(char *str, const char *key = " "); +char* rtrim_once(char *str, const char *key = " "); +char* trim_once (char *str, const char *key = " "); + +//================ +//string + lstring +//================ + +namespace nall { + class string; + template inline string to_string(T); + + class string { + public: + void reserve(size_t); + unsigned length() const; + + string& assign(const char*); + string& append(const char*); + template string& operator= (T value) { return assign(to_string(value)); } + template string& operator<<(T value) { return append(to_string(value)); } + + operator const char*() const; + char* operator()(); + char& operator[](int); + + bool operator==(const char*) const; + bool operator!=(const char*) const; + bool operator< (const char*) const; + bool operator<=(const char*) const; + bool operator> (const char*) const; + bool operator>=(const char*) const; + + string(); + string(const char*); + string(const string&); + string& operator=(const string&); + ~string(); + + //core.cpp + bool readfile(const char*); + + //replace.cpp + string& replace (const char*, const char*); + string& qreplace(const char*, const char*); + + protected: + char *data; + size_t size; + }; + + class lstring : public vector { + public: + template lstring& operator<<(T value) { + operator[](size()).assign(to_string(value)); + return *this; + } + + //core.cpp + int find(const char*); + + //split.cpp + void split (const char*, const char*, unsigned = 0); + void qsplit(const char*, const char*, unsigned = 0); + }; +} + +//===================== +//string<>libc wrappers +//===================== + +size_t strlcpy(nall::string &dest, const char *src, size_t length); +size_t strlcat(nall::string &dest, const char *src, size_t length); + +nall::string& strlower(nall::string &str); +nall::string& strupper(nall::string &str); + +nall::string& strtr(nall::string &dest, const char *before, const char *after); + +nall::string& ltrim(nall::string &str, const char *key = " "); +nall::string& rtrim(nall::string &str, const char *key = " "); +nall::string& trim (nall::string &str, const char *key = " "); + +nall::string& ltrim_once(nall::string &str, const char *key = " "); +nall::string& rtrim_once(nall::string &str, const char *key = " "); +nall::string& trim_once (nall::string &str, const char *key = " "); + +//============== +//misc functions +//============== + +nall::string substr(const char *src, size_t start = 0, size_t length = 0); + +nall::string strhex (uintmax_t value); +nall::string strsigned (intmax_t value); +nall::string strunsigned(uintmax_t value); +nall::string strbin (uintmax_t value); +nall::string strdouble (double value); + +namespace nall { + //this is needed, as C++98 does not support explicit template specialization inside classes; + //redundant memory allocation should hopefully be avoided via compiler optimizations. + template<> inline string to_string (bool v) { return v ? "true" : "false"; } + template<> inline string to_string (signed int v) { return strsigned(v); } + template<> inline string to_string (unsigned int v) { return strunsigned(v); } + template<> inline string to_string (double v) { return strdouble(v); } + template<> inline string to_string (char *v) { return v; } + template<> inline string to_string (const char *v) { return v; } + template<> inline string to_string (string v) { return v; } + template<> inline string to_string(const string &v) { return v; } +} + +#endif diff --git a/bsnes/lib/nall/string/compare.cpp b/bsnes/lib/nall/string/compare.cpp new file mode 100755 index 0000000..168063a --- /dev/null +++ b/bsnes/lib/nall/string/compare.cpp @@ -0,0 +1,99 @@ +#ifdef NALL_STRING_CPP + +char chrlower(char c) { + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; +} + +char chrupper(char c) { + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + +int stricmp(const char *dest, const char *src) { + while(*dest) { + if(chrlower(*dest) != chrlower(*src)) break; + dest++; + src++; + } + + return (int)chrlower(*dest) - (int)chrlower(*src); +} + +int strpos(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return -1; + for(int i = 0; i <= ssl - ksl; i++) { + if(!memcmp(str + i, key, ksl)) { + return i; + } + } + return -1; +} + +int qstrpos(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return -1; + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = str[i]; + if(x == '\"' || x == '\'') { + uint8_t z = i++; + while(str[i] != x && i < ssl) i++; + if(i >= ssl) i = z; + } + if(!memcmp(str + i, key, ksl)) { + return i; + } else { + i++; + } + } + return -1; +} + +bool strbegin(const char *str, const char *key) { + int i, ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str, key, ksl)); +} + +bool stribegin(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = 0; i < ksl; i++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[i] && str[i]+0x20 != key[i])return false; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[i] && str[i]-0x20 != key[i])return false; + } else { + if(str[i] != key[i])return false; + } + } + return true; +} + +bool strend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str + ssl - ksl, key, ksl)); +} + +bool striend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[z] && str[i]+0x20 != key[z])return false; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[z] && str[i]-0x20 != key[z])return false; + } else { + if(str[i] != key[z])return false; + } + } + return true; +} + +#endif diff --git a/bsnes/lib/nall/string/convert.cpp b/bsnes/lib/nall/string/convert.cpp new file mode 100755 index 0000000..c08ccae --- /dev/null +++ b/bsnes/lib/nall/string/convert.cpp @@ -0,0 +1,284 @@ +#ifdef NALL_STRING_CPP + +char* strlower(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrlower(str[i]); + i++; + } + return str; +} + +char* strupper(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrupper(str[i]); + i++; + } + return str; +} + +char* strtr(char *dest, const char *before, const char *after) { + if(!dest || !before || !after) return dest; + int sl = strlen(dest), bsl = strlen(before), asl = strlen(after); + + if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace + for(unsigned i = 0; i < sl; i++) { + for(unsigned l = 0; l < bsl; l++) { + if(dest[i] == before[l]) { + dest[i] = after[l]; + break; + } + } + } + + return dest; +} + +uintmax_t strhex(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip hex identifiers 0x and $, if present + if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2; + else if(*str == '$') str++; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x >= 'A' && x <= 'F') x -= 'A' - 10; + else if(x >= 'a' && x <= 'f') x -= 'a' - 10; + else break; //stop at first invalid character + result = result * 16 + x; + } + + return result; +} + +intmax_t strsigned(const char *str) { + if(!str) return 0; + intmax_t result = 0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return !negate ? result : -result; +} + +uintmax_t strunsigned(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return result; +} + +uintmax_t strbin(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip bin identifiers 0b and %, if present + if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2; + else if(*str == '%') str++; + + while(*str) { + uint8_t x = *str++; + if(x == '0' || x == '1') x -= '0'; + else break; //stop at first invalid character + result = result * 2 + x; + } + + return result; +} + +double strdouble(const char *str) { + if(!str) return 0.0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + intmax_t result_integral = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x == '.') break; //break loop and read fractional part + else return (double)result_integral; //invalid value, assume no fractional part + result_integral = result_integral * 10 + x; + } + + intmax_t result_fractional = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result_fractional = result_fractional * 10 + x; + } + + //calculate fractional portion + double result = (double)result_fractional; + while((uintmax_t)result > 0) result /= 10.0; + result += (double)result_integral; + + return !negate ? result : -result; +} + +// + +size_t strhex(char *str, uintmax_t value, size_t length /* = 0 */) { + if(length == 0) length -= 1U; //"infinite" length + size_t initial_length = length; + + //count number of digits in value + int digits_integral = 1; + uintmax_t digits_integral_ = value; + while(digits_integral_ /= 16) digits_integral++; + + int digits = digits_integral; + if(!str) return digits + 1; //only computing required length? + + length = nall::min(digits, length - 1); + str += length; //seek to end of target string + *str = 0; //set null terminator + + while(length--) { + uint8_t x = value % 16; + value /= 16; + *--str = x < 10 ? (x + '0') : (x + 'a' - 10); //iterate backwards to write string + } + + return nall::min(initial_length, digits + 1); +} + +size_t strsigned(char *str, intmax_t value_, size_t length /* = 0 */) { + if(length == 0) length = -1U; //"infinite" length + size_t initial_length = length; + + bool negate = value_ < 0; + uintmax_t value = value_ >= 0 ? value_ : -value_; + + //count number of digits in value + int digits_integral = 1; + uintmax_t digits_integral_ = value; + while(digits_integral_ /= 10) digits_integral++; + + int digits = (negate ? 1 : 0) + digits_integral; + if(!str) return digits + 1; //only computing required length? + + length = nall::min(digits, length - 1); + str += length; //seek to end of target string + *str = 0; //set null terminator + while(length && digits_integral--) { + uint8_t x = '0' + (value % 10); + value /= 10; + *--str = x; //iterate backwards to write string + length--; + } + + if(length && negate) { + *--str = '-'; + } + + return nall::min(initial_length, digits + 1); +} + +size_t strunsigned(char *str, uintmax_t value, size_t length /* = 0 */) { + if(length == 0) length = -1U; //"infinite" length + size_t initial_length = length; + + //count number of digits in value + int digits_integral = 1; + uintmax_t digits_integral_ = value; + while(digits_integral_ /= 10) digits_integral++; + + int digits = digits_integral; + if(!str) return digits_integral + 1; //only computing required length? + + length = nall::min(digits, length - 1); + str += length; //seek to end of target string + *str = 0; //set null terminator + + while(length--) { + uint8_t x = '0' + (value % 10); + value /= 10; + *--str = x; //iterate backwards to write string + } + + return nall::min(initial_length, digits + 1); +} + +size_t strbin(char *str, uintmax_t value, size_t length /* = 0 */) { + if(length == 0) length = -1U; //"infinite" length + size_t initial_length = length; + + //count number of digits in value + int digits_integral = 1; + uintmax_t digits_integral_ = value; + while(digits_integral_ /= 2) digits_integral++; + + int digits = digits_integral; + if(!str) return digits + 1; //only computing required length? + + length = nall::min(digits, length - 1); + str += length; //seek to end of target string + *str = 0; //set null terminator + + while(length--) { + uint8_t x = '0' + (value % 2); + value /= 2; + *--str = x; //iterate backwards to write string + } + + return nall::min(initial_length, digits + 1); +} + +//using sprintf is certainly not the most ideal method to convert +//a double to a string ... but attempting to parse a double by +//hand, digit-by-digit, results in subtle rounding errors. +// +//note: length parameter is currently ignored. +//it remains for consistency and possible future support. +size_t strdouble(char *str, double value, size_t length /* = 0 */) { + char buffer[256]; + sprintf(buffer, "%f", value); + + //remove excess 0's in fraction (2.500000 -> 2.5) + for(char *p = buffer; *p; p++) { + if(*p == '.') { + char *p = buffer + strlen(buffer) - 1; + while(*p == '0') { + if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1. + p--; + } + break; + } + } + + length = strlen(buffer); + if(str) strcpy(str, buffer); + return length + 1; +} + +#endif diff --git a/bsnes/lib/nall/string/core.cpp b/bsnes/lib/nall/string/core.cpp new file mode 100755 index 0000000..119e50b --- /dev/null +++ b/bsnes/lib/nall/string/core.cpp @@ -0,0 +1,103 @@ +#ifdef NALL_STRING_CPP + +void string::reserve(size_t size_) { + if(size_ > size) { + size = size_; + data = (char*)realloc(data, size + 1); + data[size] = 0; + } +} + +unsigned string::length() const { + return strlen(data); +} + +string& string::assign(const char *s) { + unsigned length = strlen(s); + reserve(length); + strcpy(data, s); + return *this; +} + +string& string::append(const char *s) { + unsigned length = strlen(data) + strlen(s); + reserve(length); + strcat(data, s); + return *this; +} + +string::operator const char*() const { + return data; +} + +char* string::operator()() { + return data; +} + +char& string::operator[](int index) { + reserve(index); + return data[index]; +} + +bool string::operator==(const char *str) const { return strcmp(data, str) == 0; } +bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; } +bool string::operator< (const char *str) const { return strcmp(data, str) < 0; } +bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; } +bool string::operator> (const char *str) const { return strcmp(data, str) > 0; } +bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; } + +string::string() { + size = 64; + data = (char*)malloc(size + 1); + *data = 0; +} + +string::string(const char *value) { + size = strlen(value); + data = strdup(value); +} + +string::string(const string &value) { + size = strlen(value); + data = strdup(value); +} + +string& string::operator=(const string &value) { + assign(value); +} + +string::~string() { + free(data); +} + +bool string::readfile(const char *filename) { + assign(""); + + #if !defined(_WIN32) + FILE *fp = fopen(filename, "rb"); + #else + FILE *fp = _wfopen(nall::utf16_t(filename), L"rb"); + #endif + if(!fp) return false; + + fseek(fp, 0, SEEK_END); + size_t size = ftell(fp); + rewind(fp); + char *fdata = new char[size + 1]; + unsigned unused = fread(fdata, 1, size, fp); + fclose(fp); + fdata[size] = 0; + assign(fdata); + delete[] fdata; + + return true; +} + +int lstring::find(const char *key) { + for(unsigned i = 0; i < size(); i++) { + if(operator[](i) == key) return i; + } + return -1; +} + +#endif diff --git a/bsnes/lib/nall/string/match.cpp b/bsnes/lib/nall/string/match.cpp new file mode 100755 index 0000000..186ed19 --- /dev/null +++ b/bsnes/lib/nall/string/match.cpp @@ -0,0 +1,71 @@ +#ifdef NALL_STRING_CPP + +bool match(const char *p, const char *s) { + const char *p_ = 0, *s_ = 0; + + for(;;) { + if(!*s) { + while(*p == '*') p++; + return !*p; + } + + //wildcard match + if(*p == '*') { + p_ = p++, s_ = s; + continue; + } + + //any match + if(*p == '?') { + p++, s++; + continue; + } + + //ranged match + if(*p == '{') { + #define pattern(name_, rule_) \ + if(strbegin(p, name_)) { \ + if(rule_) { \ + p += sizeof(name_) - 1, s++; \ + continue; \ + } \ + goto failure; \ + } + + pattern("{alpha}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) + pattern("{alphanumeric}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z') || (*s >= '0' && *s <= '9')) + pattern("{binary}", (*s == '0' || *s == '1')) + pattern("{hex}", (*s >= '0' && *s <= '9') || (*s >= 'A' && *s <= 'F') || (*s >= 'a' && *s <= 'f')) + pattern("{lowercase}", (*s >= 'a' && *s <= 'z')) + pattern("{numeric}", (*s >= '0' && *s <= '9')) + pattern("{uppercase}", (*s >= 'A' && *s <= 'Z')) + pattern("{whitespace}", (*s == ' ' || *s == '\t')) + + #undef pattern + goto failure; + } + + //reserved character match + if(*p == '\\') { + p++; + //fallthrough + } + + //literal match + if(*p == *s) { + p++, *s++; + continue; + } + + //attempt wildcard rematch + failure: + if(p_) { + p = p_, s = s_ + 1; + continue; + } + + return false; + } +} + +#endif diff --git a/bsnes/lib/nall/string/math.cpp b/bsnes/lib/nall/string/math.cpp new file mode 100755 index 0000000..31835b1 --- /dev/null +++ b/bsnes/lib/nall/string/math.cpp @@ -0,0 +1,159 @@ +#ifdef NALL_STRING_CPP + +static int eval_integer(const char *&s) { + if(!*s) throw "unrecognized_integer"; + int value = 0, x = *s, y = *(s + 1); + + //hexadecimal + if(x == '0' && (y == 'X' || y == 'x')) { + s += 2; + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; } + if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; } + if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; } + return value; + } + } + + //binary + if(x == '0' && (y == 'B' || y == 'b')) { + s += 2; + while(true) { + if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; } + return value; + } + } + + //octal (or decimal '0') + if(x == '0') { + s += 1; + while(true) { + if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; } + return value; + } + } + + //decimal + if(x >= '0' && x <= '9') { + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; } + return value; + } + } + + //char + if(x == '\'' && y != '\'') { + s += 1; + while(true) { + value = value * 256 + *s++; + if(*s == '\'') { s += 1; return value; } + if(!*s) throw "mismatched_char"; + } + } + + throw "unrecognized_integer"; +} + +static int eval(const char *&s, int depth = 0) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) throw "unrecognized_token"; + int value = 0, x = *s, y = *(s + 1); + + if(*s == '(') { + value = eval(++s, 1); + if(*s++ != ')') throw "mismatched_group"; + } + + else if(x == '!') value = !eval(++s, 13); + else if(x == '~') value = ~eval(++s, 13); + else if(x == '+') value = +eval(++s, 13); + else if(x == '-') value = -eval(++s, 13); + + else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s); + + else throw "unrecognized_token"; + + while(true) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) break; + x = *s, y = *(s + 1); + + if(depth >= 13) break; + if(x == '*') { value *= eval(++s, 13); continue; } + if(x == '/') { value /= eval(++s, 13); continue; } + if(x == '%') { value %= eval(++s, 13); continue; } + + if(depth >= 12) break; + if(x == '+') { value += eval(++s, 12); continue; } + if(x == '-') { value -= eval(++s, 12); continue; } + + if(depth >= 11) break; + if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; } + if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; } + + if(depth >= 10) break; + if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; } + if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; } + if(x == '<') { value = value < eval(++s, 10); continue; } + if(x == '>') { value = value > eval(++s, 10); continue; } + + if(depth >= 9) break; + if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; } + if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; } + + if(depth >= 8) break; + if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; } + + if(depth >= 7) break; + if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; } + + if(depth >= 6) break; + if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; } + + if(depth >= 5) break; + if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; } + + if(depth >= 4) break; + if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; } + + if(depth >= 3) break; + if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; } + + if(x == '?') { + int lhs = eval(++s, 2); + if(*s != ':') throw "mismatched_ternary"; + int rhs = eval(++s, 2); + value = value ? lhs : rhs; + continue; + } + if(depth >= 2) break; + + if(depth > 0 && x == ')') break; + + throw "unrecognized_token"; + } + + return value; +} + +bool strint(const char *s, int &result) { + try { + result = eval_integer(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +bool strmath(const char *s, int &result) { + try { + result = eval(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +#endif diff --git a/bsnes/lib/nall/string/replace.cpp b/bsnes/lib/nall/string/replace.cpp new file mode 100755 index 0000000..7c13cec --- /dev/null +++ b/bsnes/lib/nall/string/replace.cpp @@ -0,0 +1,98 @@ +#ifdef NALL_STRING_CPP + +string& string::replace(const char *key, const char *token) { + int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { //the new string may be longer than the old string... + for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need... + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +string& string::qreplace(const char *key, const char *token) { + int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + uint8_t x; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { + for(i = 0; i <= ssl - ksl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i; + i++; + while(data[i++] != x) { + if(i == ssl) { + i = l; + break; + } + } + } + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i++; + while(data[i] != x && i < ssl)i++; + if(i >= ssl)i = l; + else { + memcpy(buffer + z, data + l, i - l); + z += i - l; + } + } + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + replace_count++; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +#endif diff --git a/bsnes/lib/nall/string/split.cpp b/bsnes/lib/nall/string/split.cpp new file mode 100755 index 0000000..b97068b --- /dev/null +++ b/bsnes/lib/nall/string/split.cpp @@ -0,0 +1,51 @@ +#ifdef NALL_STRING_CPP + +void lstring::split(const char *key, const char *src, unsigned limit) { + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +void lstring::qsplit(const char *key, const char *src, unsigned limit) { + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = src[i]; + + if(x == '\"' || x == '\'') { + int z = i++; //skip opening quote + while(i < ssl && src[i] != x) i++; + if(i >= ssl) i = z; //failed match, rewind i + else { + i++; //skip closing quote + continue; //restart in case next char is also a quote + } + } + + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +#endif diff --git a/bsnes/lib/nall/string/strl.cpp b/bsnes/lib/nall/string/strl.cpp new file mode 100755 index 0000000..c23f652 --- /dev/null +++ b/bsnes/lib/nall/string/strl.cpp @@ -0,0 +1,47 @@ +#ifdef NALL_STRING_CPP + +//strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller + +//return = strlen(src) +size_t strlcpy(char *dest, const char *src, size_t length) { + char *d = dest; + const char *s = src; + size_t n = length; + + if(n) { + while(--n && (*d++ = *s++)); //copy as many bytes as possible, or until null terminator reached + } + + if(!n) { + if(length) *d = 0; + while(*s++); //traverse rest of s, so that s - src == strlen(src) + } + + return (s - src - 1); //return length of copied string, sans null terminator +} + +//return = strlen(src) + min(length, strlen(dest)) +size_t strlcat(char *dest, const char *src, size_t length) { + char *d = dest; + const char *s = src; + size_t n = length; + + while(n-- && *d) d++; //find end of dest + size_t dlength = d - dest; + n = length - dlength; //subtract length of dest from maximum string length + + if(!n) return dlength + strlen(s); + + while(*s) { + if(n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = 0; + + return dlength + (s - src); //return length of resulting string, sans null terminator +} + +#endif diff --git a/bsnes/lib/nall/string/trim.cpp b/bsnes/lib/nall/string/trim.cpp new file mode 100755 index 0000000..1c5e5bd --- /dev/null +++ b/bsnes/lib/nall/string/trim.cpp @@ -0,0 +1,35 @@ +#ifdef NALL_STRING_CPP + +char* ltrim(char *str, const char *key) { + if(!key || !*key) return str; + while(strbegin(str, key)) strcpy(str, str + strlen(key)); + return str; +} + +char* rtrim(char *str, const char *key) { + if(!key || !*key) return str; + while(strend(str, key)) str[strlen(str) - strlen(key)] = 0; + return str; +} + +char* trim(char *str, const char *key) { + return ltrim(rtrim(str, key), key); +} + +char* ltrim_once(char *str, const char *key) { + if(!key || !*key) return str; + if(strbegin(str, key)) strcpy(str, str + strlen(key)); + return str; +} + +char* rtrim_once(char *str, const char *key) { + if(!key || !*key) return str; + if(strend(str, key)) str[strlen(str) - strlen(key)] = 0; + return str; +} + +char* trim_once(char *str, const char *key) { + return ltrim_once(rtrim_once(str, key), key); +} + +#endif diff --git a/bsnes/lib/nall/string/utility.cpp b/bsnes/lib/nall/string/utility.cpp new file mode 100755 index 0000000..1a78281 --- /dev/null +++ b/bsnes/lib/nall/string/utility.cpp @@ -0,0 +1,74 @@ +#ifdef NALL_STRING_CPP + +size_t strlcpy(nall::string &dest, const char *src, size_t length) { + dest.reserve(length); + return strlcpy(dest(), src, length); +} + +size_t strlcat(nall::string &dest, const char *src, size_t length) { + dest.reserve(length); + return strlcat(dest(), src, length); +} + +nall::string substr(const char *src, size_t start, size_t length) { + nall::string dest; + if(length == 0) { + //copy entire string + dest = src + start; + } else { + //copy partial string + strlcpy(dest, src + start, length + 1); + } + return dest; +} + +/* very simplistic wrappers to return nall::string& instead of char* type */ + +nall::string& strlower(nall::string &str) { strlower(str()); return str; } +nall::string& strupper(nall::string &str) { strupper(str()); return str; } +nall::string& strtr(nall::string &dest, const char *before, const char *after) { strtr(dest(), before, after); return dest; } +nall::string& ltrim(nall::string &str, const char *key) { ltrim(str(), key); return str; } +nall::string& rtrim(nall::string &str, const char *key) { rtrim(str(), key); return str; } +nall::string& trim (nall::string &str, const char *key) { trim (str(), key); return str; } +nall::string& ltrim_once(nall::string &str, const char *key) { ltrim_once(str(), key); return str; } +nall::string& rtrim_once(nall::string &str, const char *key) { rtrim_once(str(), key); return str; } +nall::string& trim_once (nall::string &str, const char *key) { trim_once (str(), key); return str; } + +/* arithmetic <> string */ + +nall::string strhex(uintmax_t value) { + nall::string temp; + temp.reserve(strhex(0, value)); + strhex(temp(), value); + return temp; +} + +nall::string strsigned(intmax_t value) { + nall::string temp; + temp.reserve(strsigned(0, value)); + strsigned(temp(), value); + return temp; +} + +nall::string strunsigned(uintmax_t value) { + nall::string temp; + temp.reserve(strunsigned(0, value)); + strunsigned(temp(), value); + return temp; +} + +nall::string strbin(uintmax_t value) { + nall::string temp; + temp.reserve(strbin(0, value)); + strbin(temp(), value); + return temp; +} + +nall::string strdouble(double value) { + nall::string temp; + temp.reserve(strdouble(0, value)); + strdouble(temp(), value); + return temp; +} + +#endif diff --git a/bsnes/lib/nall/traits.hpp b/bsnes/lib/nall/traits.hpp new file mode 100755 index 0000000..9632020 --- /dev/null +++ b/bsnes/lib/nall/traits.hpp @@ -0,0 +1,94 @@ +#ifndef NALL_TRAITS_HPP +#define NALL_TRAITS_HPP + +namespace nall { + //== + //is + //== + + template struct is_integral { enum { value = false }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + + template struct is_floating_point { enum { value = false }; }; + template<> struct is_floating_point { enum { value = true }; }; + template<> struct is_floating_point { enum { value = true }; }; + template<> struct is_floating_point { enum { value = true }; }; + + template struct is_void { enum { value = false }; }; + template<> struct is_void { enum { value = true }; }; + + template struct is_arithmetic { + enum { value = is_integral::value || is_floating_point::value }; + }; + + template struct is_fundamental { + enum { value = is_integral::value || is_floating_point::value || is_void::value }; + }; + + template struct is_compound { + enum { value = !is_fundamental::value }; + }; + + template struct is_array { enum { value = false }; }; + template struct is_array { enum { value = true }; }; + template struct is_array { enum { value = true }; }; + + template struct is_const { enum { value = false }; }; + template struct is_const { enum { value = true }; }; + template struct is_const { enum { value = true }; }; + + template struct is_pointer { enum { value = false }; }; + template struct is_pointer { enum { value = true }; }; + + template struct is_reference { enum { value = false }; }; + template struct is_reference { enum { value = true }; }; + + template struct is_same { enum { value = false }; }; + template struct is_same { enum { value = true }; }; + + //=== + //add + //=== + + template struct add_const { typedef const T type; }; + template struct add_const { typedef const T type; }; + template struct add_const { typedef const T& type; }; + + template struct add_pointer { typedef T* type; }; + template struct add_pointer { typedef T** type; }; + + template struct add_reference { typedef T& type; }; + template struct add_reference { typedef T& type; }; + + //====== + //remove + //====== + + template struct remove_const { typedef T type; }; + template struct remove_const { typedef T type; }; + template struct remove_const { typedef T type; }; + + template struct remove_extent { typedef T type; }; + template struct remove_extent { typedef T type; }; + template struct remove_extent { typedef T type; }; + + template struct remove_pointer { typedef T type; }; + template struct remove_pointer { typedef T type; }; + + template struct remove_reference { typedef T type; }; + template struct remove_reference { typedef T type; }; +} + +#endif diff --git a/bsnes/lib/nall/ups.hpp b/bsnes/lib/nall/ups.hpp new file mode 100755 index 0000000..610e364 --- /dev/null +++ b/bsnes/lib/nall/ups.hpp @@ -0,0 +1,191 @@ +#ifndef NALL_UPS_HPP +#define NALL_UPS_HPP + +#include + +#include +#include +#include +#include +#include + +namespace nall { + class ups { + public: + enum result { + ok, + patch_unreadable, + patch_unwritable, + patch_invalid, + input_invalid, + output_invalid, + patch_crc32_invalid, + input_crc32_invalid, + output_crc32_invalid, + }; + + ups::result create(const char *patch_fn, const uint8_t *x_data, unsigned x_size, const uint8_t *y_data, unsigned y_size) { + if(!fp.open(patch_fn, file::mode_write)) return patch_unwritable; + + crc32 = ~0; + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + //header + write('U'); + write('P'); + write('S'); + write('1'); + encptr(x_size); + encptr(y_size); + + //body + unsigned max_size = max(x_size, y_size); + unsigned relative = 0; + for(unsigned i = 0; i < max_size;) { + uint8_t x = i < x_size ? x_data[i] : 0x00; + uint8_t y = i < y_size ? y_data[i] : 0x00; + + if(x == y) { + i++; + continue; + } + + encptr(i++ - relative); + write(x ^ y); + + while(true) { + if(i >= max_size) { + write(0x00); + break; + } + + x = i < x_size ? x_data[i] : 0x00; + y = i < y_size ? y_data[i] : 0x00; + i++; + write(x ^ y); + if(x == y) break; + } + + relative = i; + } + + //footer + for(unsigned i = 0; i < 4; i++) write(x_crc32 >> (i << 3)); + for(unsigned i = 0; i < 4; i++) write(y_crc32 >> (i << 3)); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) write(p_crc32 >> (i << 3)); + + fp.close(); + return ok; + } + + ups::result apply(const uint8_t *p_data, unsigned p_size, const uint8_t *x_data, unsigned x_size, uint8_t *&y_data, unsigned &y_size) { + if(p_size < 18) return patch_invalid; + p_buffer = p_data; + + crc32 = ~0; + + //header + if(read() != 'U') return patch_invalid; + if(read() != 'P') return patch_invalid; + if(read() != 'S') return patch_invalid; + if(read() != '1') return patch_invalid; + + unsigned px_size = decptr(); + unsigned py_size = decptr(); + + //mirror + if(x_size != px_size && x_size != py_size) return input_invalid; + y_size = (x_size == px_size) ? py_size : px_size; + y_data = new(zeromemory) uint8_t[y_size]; + + for(unsigned i = 0; i < x_size && i < y_size; i++) y_data[i] = x_data[i]; + for(unsigned i = x_size; i < y_size; i++) y_data[i] = 0x00; + + //body + unsigned relative = 0; + while(p_buffer < p_data + p_size - 12) { + relative += decptr(); + + while(true) { + uint8_t x = read(); + if(x && relative < y_size) { + uint8_t y = relative < x_size ? x_data[relative] : 0x00; + y_data[relative] = x ^ y; + } + relative++; + if(!x) break; + } + } + + //footer + unsigned px_crc32 = 0, py_crc32 = 0, pp_crc32 = 0; + for(unsigned i = 0; i < 4; i++) px_crc32 |= read() << (i << 3); + for(unsigned i = 0; i < 4; i++) py_crc32 |= read() << (i << 3); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) pp_crc32 |= read() << (i << 3); + + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + if(px_size != py_size) { + if(x_size == px_size && x_crc32 != px_crc32) return input_crc32_invalid; + if(x_size == py_size && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_size == px_size && y_crc32 != px_crc32) return output_crc32_invalid; + if(y_size == py_size && y_crc32 != py_crc32) return output_crc32_invalid; + } else { + if(x_crc32 != px_crc32 && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_crc32 != px_crc32 && y_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 == y_crc32 && px_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 != y_crc32 && px_crc32 == py_crc32) return output_crc32_invalid; + } + + if(p_crc32 != pp_crc32) return patch_crc32_invalid; + return ok; + } + + private: + file fp; + uint32_t crc32; + const uint8_t *p_buffer; + + uint8_t read() { + uint8_t n = *p_buffer++; + crc32 = crc32_adjust(crc32, n); + return n; + } + + void write(uint8_t n) { + fp.write(n); + crc32 = crc32_adjust(crc32, n); + } + + void encptr(uint64_t offset) { + while(true) { + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + write(0x80 | x); + break; + } + write(x); + offset--; + } + } + + uint64_t decptr() { + uint64_t offset = 0, shift = 1; + while(true) { + uint8_t x = read(); + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; + } + }; +} + +#endif diff --git a/bsnes/lib/nall/utf8.hpp b/bsnes/lib/nall/utf8.hpp new file mode 100755 index 0000000..cae4e1b --- /dev/null +++ b/bsnes/lib/nall/utf8.hpp @@ -0,0 +1,71 @@ +#ifndef NALL_UTF8_HPP +#define NALL_UTF8_HPP + +#include + +//UTF-8 <> UTF-16 conversion +//used only for Win32; Linux, etc use UTF-8 internally + +#if defined(_WIN32) + +#undef NOMINMAX +#define NOMINMAX +#include + +namespace nall { + //UTF-8 to UTF-16 + class utf16_t { + public: + operator wchar_t*() { + return buffer; + } + + operator const wchar_t*() const { + return buffer; + } + + utf16_t(const char *s = "") { + if(!s) s = ""; + unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); + buffer = new(zeromemory) wchar_t[length + 1]; + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + } + + ~utf16_t() { + delete[] buffer; + } + + private: + wchar_t *buffer; + }; + + //UTF-16 to UTF-8 + class utf8_t { + public: + operator char*() { + return buffer; + } + + operator const char*() const { + return buffer; + } + + utf8_t(const wchar_t *s = L"") { + if(!s) s = L""; + unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0); + buffer = new(zeromemory) char[length + 1]; + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0); + } + + ~utf8_t() { + delete[] buffer; + } + + private: + char *buffer; + }; +} + +#endif //if defined(_WIN32) + +#endif diff --git a/bsnes/lib/nall/utility.hpp b/bsnes/lib/nall/utility.hpp new file mode 100755 index 0000000..14d3f87 --- /dev/null +++ b/bsnes/lib/nall/utility.hpp @@ -0,0 +1,29 @@ +#ifndef NALL_UTILITY_HPP +#define NALL_UTILITY_HPP + +namespace nall { + template + inline void swap(T &x, T &y) { + T temp(x); + x = y; + y = temp; + } + + template + struct base_from_member { + T value; + base_from_member(T value_) : value(value_) {} + }; + + class noncopyable { + protected: + noncopyable() {} + ~noncopyable() {} + + private: + noncopyable(const noncopyable&); + const noncopyable& operator=(const noncopyable&); + }; +} + +#endif diff --git a/bsnes/lib/nall/varint.hpp b/bsnes/lib/nall/varint.hpp new file mode 100755 index 0000000..cc3bb17 --- /dev/null +++ b/bsnes/lib/nall/varint.hpp @@ -0,0 +1,92 @@ +#ifndef NALL_VARINT_HPP +#define NALL_VARINT_HPP + +#include +#include +#include + +namespace nall { + template class uint_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + unsigned int, + typename static_if< + sizeof(long) >= bytes, + unsigned long, + typename static_if< + sizeof(long long) >= bytes, + unsigned long long, + void + >::type + >::type + >::type T; + static_assert::value> uint_assert; + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = uclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = uclip(data - 1); return r; } + inline T operator ++() { return data = uclip(data + 1); } + inline T operator --() { return data = uclip(data - 1); } + inline T operator =(const T i) { return data = uclip(i); } + inline T operator |=(const T i) { return data = uclip(data | i); } + inline T operator ^=(const T i) { return data = uclip(data ^ i); } + inline T operator &=(const T i) { return data = uclip(data & i); } + inline T operator<<=(const T i) { return data = uclip(data << i); } + inline T operator>>=(const T i) { return data = uclip(data >> i); } + inline T operator +=(const T i) { return data = uclip(data + i); } + inline T operator -=(const T i) { return data = uclip(data - i); } + inline T operator *=(const T i) { return data = uclip(data * i); } + inline T operator /=(const T i) { return data = uclip(data / i); } + inline T operator %=(const T i) { return data = uclip(data % i); } + + inline uint_t() : data(0) {} + inline uint_t(const T i) : data(uclip(i)) {} + }; + + template class int_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + signed int, + typename static_if< + sizeof(long) >= bytes, + signed long, + typename static_if< + sizeof(long long) >= bytes, + signed long long, + void + >::type + >::type + >::type T; + static_assert::value> int_assert; + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = sclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = sclip(data - 1); return r; } + inline T operator ++() { return data = sclip(data + 1); } + inline T operator --() { return data = sclip(data - 1); } + inline T operator =(const T i) { return data = sclip(i); } + inline T operator |=(const T i) { return data = sclip(data | i); } + inline T operator ^=(const T i) { return data = sclip(data ^ i); } + inline T operator &=(const T i) { return data = sclip(data & i); } + inline T operator<<=(const T i) { return data = sclip(data << i); } + inline T operator>>=(const T i) { return data = sclip(data >> i); } + inline T operator +=(const T i) { return data = sclip(data + i); } + inline T operator -=(const T i) { return data = sclip(data - i); } + inline T operator *=(const T i) { return data = sclip(data * i); } + inline T operator /=(const T i) { return data = sclip(data / i); } + inline T operator %=(const T i) { return data = sclip(data % i); } + + inline int_t() : data(0) {} + inline int_t(const T i) : data(sclip(i)) {} + }; +} + +#endif diff --git a/bsnes/lib/nall/vector.hpp b/bsnes/lib/nall/vector.hpp new file mode 100755 index 0000000..ca12b80 --- /dev/null +++ b/bsnes/lib/nall/vector.hpp @@ -0,0 +1,162 @@ +#ifndef NALL_VECTOR_HPP +#define NALL_VECTOR_HPP + +#include +#include +#include +#include + +namespace nall { + //linear_vector + //memory: O(capacity * 2) + // + //linear_vector uses placement new + manual destructor calls to create a + //contiguous block of memory for all objects. accessing individual elements + //is fast, though resizing the array incurs significant overhead. + //reserve() overhead is reduced from quadratic time to amortized constant time + //by resizing twice as much as requested. + // + //if objects hold memory address references to themselves (introspection), a + //valid copy constructor will be needed to keep pointers valid. + + template class linear_vector : noncopyable { + protected: + T *pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + T *poolcopy = (T*)malloc(newsize * sizeof(T)); + for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]); + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + pool = poolcopy; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + if(newsize < objectsize) { + //vector is shrinking; destroy excess objects + for(unsigned i = newsize; i < objectsize; i++) pool[i].~T(); + } else if(newsize > objectsize) { + //vector is expanding; allocate new objects + for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T; + } + + objectsize = newsize; + } + + void add(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + new(pool + objectsize++) T(data); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize) throw "vector[] out of bounds"; + return pool[index]; + } + + linear_vector() : pool(0), poolsize(0), objectsize(0) {} + ~linear_vector() { reset(); } + }; + + //pointer_vector + //memory: O(1) + // + //pointer_vector keeps an array of pointers to each vector object. this adds + //significant overhead to individual accesses, but allows for optimal memory + //utilization. + // + //by guaranteeing that the base memory address of each objects never changes, + //this avoids the need for an object to have a valid copy constructor. + + template class pointer_vector : noncopyable { + protected: + T **pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; } + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + pool = (T**)realloc(pool, newsize * sizeof(T*)); + for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + objectsize = newsize; + } + + void add(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + pool[objectsize++] = new T(data); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + if(!pool[index]) pool[index] = new T; + return *pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize || !pool[index]) throw "vector[] out of bounds"; + return *pool[index]; + } + + pointer_vector() : pool(0), poolsize(0), objectsize(0) {} + ~pointer_vector() { reset(); } + }; + + //default vector type + template class vector : public linear_vector {}; +} + +#endif diff --git a/bsnes/lib/ruby/audio.hpp b/bsnes/lib/ruby/audio.hpp new file mode 100755 index 0000000..e462d90 --- /dev/null +++ b/bsnes/lib/ruby/audio.hpp @@ -0,0 +1,28 @@ +class Audio { +public: + enum Setting { + //AudioInterface settings + Volume, + Resample, + ResampleOutputFrequency, + ResampleInputFrequency, + + //Audio settings + Handle, + Synchronize, + Frequency, + Latency, + }; + + virtual bool cap(Setting) { return false; } + virtual uintptr_t get(Setting) { return false; } + virtual bool set(Setting, uintptr_t) { return false; } + + virtual void sample(uint16_t left, uint16_t right) {} + virtual void clear() {} + virtual bool init() { return true; } + virtual void term() {} + + Audio() {} + virtual ~Audio() {} +}; diff --git a/bsnes/lib/ruby/audio/alsa.cpp b/bsnes/lib/ruby/audio/alsa.cpp new file mode 100755 index 0000000..eb5e732 --- /dev/null +++ b/bsnes/lib/ruby/audio/alsa.cpp @@ -0,0 +1,237 @@ +#include + +namespace ruby { + +#include "alsa.hpp" + +class pAudioALSA { +public: + AudioALSA &self; + + struct { + snd_pcm_t *handle; + snd_pcm_format_t format; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t period_size; + int channels; + const char *name; + } device; + + struct { + uint32_t *data; + unsigned length; + } buffer; + + struct { + bool synchronize; + unsigned frequency; + unsigned latency; + } settings; + + bool cap(Audio::Setting setting) { + if(setting == Audio::Synchronize) return true; + if(setting == Audio::Frequency) return true; + if(setting == Audio::Latency) return true; + return false; + } + + uintptr_t get(Audio::Setting setting) { + if(setting == Audio::Synchronize) return settings.synchronize; + if(setting == Audio::Frequency) return settings.frequency; + if(setting == Audio::Latency) return settings.latency; + return false; + } + + bool set(Audio::Setting setting, uintptr_t param) { + if(setting == Audio::Synchronize) { + if(settings.synchronize != param) { + settings.synchronize = param; + if(device.handle) { + term(); + init(); + } + } + return true; + } + + if(setting == Audio::Frequency) { + if(settings.frequency != param) { + settings.frequency = param; + if(device.handle) { + term(); + init(); + } + } + return true; + } + + if(setting == Audio::Latency) { + if(settings.latency != param) { + settings.latency = param; + if(device.handle) { + term(); + init(); + } + } + return true; + } + + return false; + } + + void sample(uint16_t left, uint16_t right) { + if(!device.handle) return; + + buffer.data[buffer.length++] = left + (right << 16); + if(buffer.length < device.period_size) return; + + if(settings.synchronize == false) { + snd_pcm_avail_update(device.handle); + snd_pcm_sframes_t delay; + snd_pcm_delay(device.handle, &delay); + if(delay < 0) { + snd_pcm_prepare(device.handle); + } else if(delay > device.buffer_size - device.period_size) { + buffer.length = 0; + return; + } + } + + uint32_t *buffer_ptr = buffer.data; + int i = 4; + + while((buffer.length > 0) && i--) { + snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer_ptr, buffer.length); + if(written < 0) { + //no samples written + snd_pcm_recover(device.handle, written, 1); + } else if(written <= buffer.length) { + buffer.length -= written; + buffer_ptr += written; + } + } + + if(i < 0) { + if(buffer.data == buffer_ptr) { + buffer.length--; + buffer_ptr++; + } + memmove(buffer.data, buffer_ptr, buffer.length * sizeof(uint32_t)); + } + } + + bool init() { + if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, 0) < 0) { + term(); + return false; + } + + /* //below code will not work with 24khz frequency rate (ALSA library bug) + if(snd_pcm_set_params(device.handle, device.format, SND_PCM_ACCESS_RW_INTERLEAVED, + device.channels, settings.frequency, 1, settings.latency * 100) < 0) { + //failed to set device parameters + term(); + return false; + } + + if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) { + device.period_size = settings.latency * 100 * 1e-6 * settings.frequency / 4; + }*/ + + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + unsigned rate = settings.frequency; + unsigned buffer_time = settings.latency * 100; + unsigned period_time = settings.latency * 100 / 4; + + snd_pcm_hw_params_alloca(&hwparams); + if(snd_pcm_hw_params_any(device.handle, hwparams) < 0) { + term(); + return false; + } + + if(snd_pcm_hw_params_set_access(device.handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 + || snd_pcm_hw_params_set_format(device.handle, hwparams, device.format) < 0 + || snd_pcm_hw_params_set_channels(device.handle, hwparams, device.channels) < 0 + || snd_pcm_hw_params_set_rate_near(device.handle, hwparams, &rate, 0) < 0 + || snd_pcm_hw_params_set_period_time_near(device.handle, hwparams, &period_time, 0) < 0 + || snd_pcm_hw_params_set_buffer_time_near(device.handle, hwparams, &buffer_time, 0) < 0 + ) { + term(); + return false; + } + + if(snd_pcm_hw_params(device.handle, hwparams) < 0) { + term(); + return false; + } + + if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) { + term(); + return false; + } + + snd_pcm_sw_params_alloca(&swparams); + if(snd_pcm_sw_params_current(device.handle, swparams) < 0) { + term(); + return false; + } + + if(snd_pcm_sw_params_set_start_threshold(device.handle, swparams, + (device.buffer_size / device.period_size) * device.period_size) < 0 + ) { + term(); + return false; + } + + if(snd_pcm_sw_params(device.handle, swparams) < 0) { + term(); + return false; + } + + buffer.data = new uint32_t[device.period_size]; + return true; + } + + void term() { + if(device.handle) { + snd_pcm_drain(device.handle); + snd_pcm_close(device.handle); + device.handle = 0; + } + + if(buffer.data) { + delete[] buffer.data; + buffer.data = 0; + } + } + + pAudioALSA(AudioALSA &self_) : self(self_) { + device.handle = 0; + device.format = SND_PCM_FORMAT_S16_LE; + device.channels = 2; + device.name = "default"; + + buffer.data = 0; + buffer.length = 0; + + settings.synchronize = false; + settings.frequency = 22050; + settings.latency = 60; + } + + ~pAudioALSA() { + term(); + } +}; + +bool AudioALSA::cap(Setting setting) { return p.cap(setting); } +uintptr_t AudioALSA::get(Setting setting) { return p.get(setting); } +bool AudioALSA::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +void AudioALSA::sample(uint16_t left, uint16_t right) { p.sample(left, right); } +bool AudioALSA::init() { return p.init(); } +void AudioALSA::term() { p.term(); } +AudioALSA::AudioALSA() : p(*new pAudioALSA(*this)) {} +AudioALSA::~AudioALSA() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/audio/alsa.hpp b/bsnes/lib/ruby/audio/alsa.hpp new file mode 100755 index 0000000..bc175ee --- /dev/null +++ b/bsnes/lib/ruby/audio/alsa.hpp @@ -0,0 +1,23 @@ +/* + audio.alsa (2008-08-12) + authors: Nach, RedDwarf +*/ + +class pAudioALSA; + +class AudioALSA : public Audio { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + void sample(uint16_t left, uint16_t right); + bool init(); + void term(); + + AudioALSA(); + ~AudioALSA(); + +private: + pAudioALSA &p; +}; diff --git a/bsnes/lib/ruby/audio/ao.cpp b/bsnes/lib/ruby/audio/ao.cpp new file mode 100755 index 0000000..e943d57 --- /dev/null +++ b/bsnes/lib/ruby/audio/ao.cpp @@ -0,0 +1,97 @@ +#include + +namespace ruby { + +#include "ao.hpp" + +class pAudioAO { +public: + AudioAO &self; + + int driver_id; + ao_sample_format driver_format; + ao_device *audio_device; + + struct { + unsigned frequency; + } settings; + + bool cap(Audio::Setting setting) { + if(setting == Audio::Frequency) return true; + return false; + } + + uintptr_t get(Audio::Setting setting) { + if(setting == Audio::Frequency) return settings.frequency; + return false; + } + + bool set(Audio::Setting setting, uintptr_t param) { + if(setting == Audio::Frequency) { + settings.frequency = param; + if(audio_device) { + term(); + init(); + } + return true; + } + return false; + } + + void sample(uint16_t l_sample, uint16_t r_sample) { + uint32_t samp = (l_sample << 0) + (r_sample << 16); + ao_play(audio_device, (char*)&samp, 4); //This may need to be byte swapped for Big Endian + } + + bool init() { + driver_id = ao_default_driver_id(); //ao_driver_id((const char*)driver) + if(driver_id < 0) return false; + + driver_format.bits = 16; + driver_format.channels = 2; + driver_format.rate = settings.frequency; + driver_format.byte_format = AO_FMT_LITTLE; + + ao_option *options = 0; + ao_info *di = ao_driver_info(driver_id); + if(!di) return false; + if(!strcmp(di->short_name, "alsa")) { + ao_append_option(&options, "buffer_time", "100000"); //100ms latency (default was 500ms) + } + + audio_device = ao_open_live(driver_id, &driver_format, options); + if(!audio_device) return false; + + return true; + } + + void term() { + if(audio_device) { + ao_close(audio_device); + audio_device = 0; + } + } + + pAudioAO(AudioAO &self_) : self(self_) { + audio_device = 0; + ao_initialize(); + + settings.frequency = 22050; + } + + ~pAudioAO() { + term(); + //ao_shutdown(); //FIXME: this is causing a segfault for some reason when called ... + } +}; + +bool AudioAO::cap(Setting setting) { return p.cap(setting); } +uintptr_t AudioAO::get(Setting setting) { return p.get(setting); } +bool AudioAO::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +void AudioAO::sample(uint16_t l_sample, uint16_t r_sample) { p.sample(l_sample, r_sample); } +bool AudioAO::init() { return p.init(); } +void AudioAO::term() { p.term(); } +AudioAO::AudioAO() : p(*new pAudioAO(*this)) {} +AudioAO::~AudioAO() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/audio/ao.hpp b/bsnes/lib/ruby/audio/ao.hpp new file mode 100755 index 0000000..adfea53 --- /dev/null +++ b/bsnes/lib/ruby/audio/ao.hpp @@ -0,0 +1,23 @@ +/* + audio.ao (2008-06-01) + authors: Nach, RedDwarf +*/ + +class pAudioAO; + +class AudioAO : public Audio { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + void sample(uint16_t left, uint16_t right); + bool init(); + void term(); + + AudioAO(); + ~AudioAO(); + +private: + pAudioAO &p; +}; diff --git a/bsnes/lib/ruby/audio/directsound.cpp b/bsnes/lib/ruby/audio/directsound.cpp new file mode 100755 index 0000000..8a13ede --- /dev/null +++ b/bsnes/lib/ruby/audio/directsound.cpp @@ -0,0 +1,220 @@ +#include +#include + +namespace ruby { + +#include "directsound.hpp" + +class pAudioDS { +public: + AudioDS &self; + + LPDIRECTSOUND ds; + LPDIRECTSOUNDBUFFER dsb_p, dsb_b; + DSBUFFERDESC dsbd; + WAVEFORMATEX wfx; + + struct { + unsigned rings; + unsigned latency; + + uint32_t *buffer; + unsigned bufferoffset; + + unsigned readring; + unsigned writering; + int distance; + } device; + + struct { + HWND handle; + bool synchronize; + unsigned frequency; + unsigned latency; + } settings; + + bool cap(Audio::Setting setting) { + if(setting == Audio::Handle) return true; + if(setting == Audio::Synchronize) return true; + if(setting == Audio::Frequency) return true; + if(setting == Audio::Latency) return true; + return false; + } + + uintptr_t get(Audio::Setting setting) { + if(setting == Audio::Handle) return (uintptr_t)settings.handle; + if(setting == Audio::Synchronize) return settings.synchronize; + if(setting == Audio::Frequency) return settings.frequency; + if(setting == Audio::Latency) return settings.latency; + return false; + } + + bool set(Audio::Setting setting, uintptr_t param) { + if(setting == Audio::Handle) { + settings.handle = (HWND)param; + return true; + } + + if(setting == Audio::Synchronize) { + settings.synchronize = param; + if(ds) clear(); + return true; + } + + if(setting == Audio::Frequency) { + settings.frequency = param; + if(ds) init(); + return true; + } + + if(setting == Audio::Latency) { + settings.latency = param; + if(ds) init(); + return true; + } + + return false; + } + + void sample(uint16_t left, uint16_t right) { + device.buffer[device.bufferoffset++] = left + (right << 16); + if(device.bufferoffset < device.latency) return; + device.bufferoffset = 0; + + DWORD pos, size; + void *output; + + if(settings.synchronize == true) { + //wait until playback buffer has an empty ring to write new audio data to + while(device.distance >= device.rings - 1) { + dsb_b->GetCurrentPosition(&pos, 0); + unsigned activering = pos / (device.latency * 4); + if(activering == device.readring) { + if(video.get(Video::Synchronize) == false) Sleep(1); + continue; + } + + //subtract number of played rings from ring distance counter + device.distance -= (device.rings + activering - device.readring) % device.rings; + device.readring = activering; + + if(device.distance < 2) { + //buffer underflow; set max distance to recover quickly + device.distance = device.rings - 1; + device.writering = (device.rings + device.readring - 1) % device.rings; + break; + } + } + } + + device.writering = (device.writering + 1) % device.rings; + device.distance = (device.distance + 1) % device.rings; + + if(dsb_b->Lock(device.writering * device.latency * 4, device.latency * 4, &output, &size, 0, 0, 0) == DS_OK) { + memcpy(output, device.buffer, device.latency * 4); + dsb_b->Unlock(output, size, 0, 0); + } + } + + void clear() { + device.readring = 0; + device.writering = device.rings - 1; + device.distance = device.rings - 1; + + device.bufferoffset = 0; + if(device.buffer) memset(device.buffer, 0, device.latency * device.rings * 4); + + if(!dsb_b) return; + dsb_b->Stop(); + dsb_b->SetCurrentPosition(0); + + DWORD size; + void *output; + dsb_b->Lock(0, device.latency * device.rings * 4, &output, &size, 0, 0, 0); + memset(output, 0, size); + dsb_b->Unlock(output, size, 0, 0); + + dsb_b->Play(0, 0, DSBPLAY_LOOPING); + } + + bool init() { + term(); + + device.rings = 8; + device.latency = settings.frequency * settings.latency / device.rings / 1000.0 + 0.5; + device.buffer = new uint32_t[device.latency * device.rings]; + device.bufferoffset = 0; + + DirectSoundCreate(0, &ds, 0); + ds->SetCooperativeLevel((HWND)settings.handle, DSSCL_PRIORITY); + + memset(&dsbd, 0, sizeof(dsbd)); + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbd.dwBufferBytes = 0; + dsbd.lpwfxFormat = 0; + ds->CreateSoundBuffer(&dsbd, &dsb_p, 0); + + memset(&wfx, 0, sizeof(wfx)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = settings.frequency; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + dsb_p->SetFormat(&wfx); + + memset(&dsbd, 0, sizeof(dsbd)); + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE; + dsbd.dwBufferBytes = device.latency * device.rings * sizeof(uint32_t); + dsbd.guid3DAlgorithm = GUID_NULL; + dsbd.lpwfxFormat = &wfx; + ds->CreateSoundBuffer(&dsbd, &dsb_b, 0); + dsb_b->SetFrequency(settings.frequency); + dsb_b->SetCurrentPosition(0); + + clear(); + return true; + } + + void term() { + if(device.buffer) { + delete[] device.buffer; + device.buffer = 0; + } + + if(dsb_b) { dsb_b->Stop(); dsb_b->Release(); dsb_b = 0; } + if(dsb_p) { dsb_p->Stop(); dsb_p->Release(); dsb_p = 0; } + if(ds) { ds->Release(); ds = 0; } + } + + pAudioDS(AudioDS &self_) : self(self_) { + ds = 0; + dsb_p = 0; + dsb_b = 0; + + device.buffer = 0; + device.bufferoffset = 0; + device.readring = 0; + device.writering = 0; + device.distance = 0; + + settings.handle = GetDesktopWindow(); + settings.synchronize = false; + settings.frequency = 22050; + settings.latency = 120; + } +}; + +bool AudioDS::cap(Setting setting) { return p.cap(setting); } +uintptr_t AudioDS::get(Setting setting) { return p.get(setting); } +bool AudioDS::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +void AudioDS::sample(uint16_t left, uint16_t right) { p.sample(left, right); } +void AudioDS::clear() { p.clear(); } +bool AudioDS::init() { return p.init(); } +void AudioDS::term() { p.term(); } +AudioDS::AudioDS() : p(*new pAudioDS(*this)) {} +AudioDS::~AudioDS() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/audio/directsound.hpp b/bsnes/lib/ruby/audio/directsound.hpp new file mode 100755 index 0000000..9fe0a18 --- /dev/null +++ b/bsnes/lib/ruby/audio/directsound.hpp @@ -0,0 +1,24 @@ +/* + audio.directsound (2007-12-26) + author: byuu +*/ + +class pAudioDS; + +class AudioDS : public Audio { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + void sample(uint16_t left, uint16_t right); + void clear(); + bool init(); + void term(); + + AudioDS(); + ~AudioDS(); + +private: + pAudioDS &p; +}; diff --git a/bsnes/lib/ruby/audio/openal.cpp b/bsnes/lib/ruby/audio/openal.cpp new file mode 100755 index 0000000..69d811e --- /dev/null +++ b/bsnes/lib/ruby/audio/openal.cpp @@ -0,0 +1,207 @@ +#include +#include + +namespace ruby { + +#include "openal.hpp" + +class pAudioOpenAL { +public: + AudioOpenAL &self; + + struct { + ALCdevice *handle; + ALCcontext *context; + ALuint source; + ALenum format; + unsigned latency; + unsigned queue_length; + } device; + + struct { + uint32_t *data; + unsigned length; + unsigned size; + } buffer; + + struct { + bool synchronize; + unsigned frequency; + unsigned latency; + } settings; + + bool cap(Audio::Setting setting) { + if(setting == Audio::Synchronize) return true; + if(setting == Audio::Frequency) return true; + if(setting == Audio::Latency) return true; + return false; + } + + uintptr_t get(Audio::Setting setting) { + if(setting == Audio::Synchronize) return settings.synchronize; + if(setting == Audio::Frequency) return settings.frequency; + if(setting == Audio::Latency) return settings.latency; + return false; + } + + bool set(Audio::Setting setting, uintptr_t param) { + if(setting == Audio::Synchronize) { + settings.synchronize = param; + return true; + } + + if(setting == Audio::Frequency) { + settings.frequency = param; + return true; + } + + if(setting == Audio::Latency) { + if(settings.latency != param) { + settings.latency = param; + update_latency(); + } + return true; + } + + return false; + } + + void sample(uint16_t sl, uint16_t sr) { + buffer.data[buffer.length++] = sl + (sr << 16); + if(buffer.length < buffer.size) return; + + ALuint albuffer = 0; + int processed = 0; + while(true) { + alGetSourcei(device.source, AL_BUFFERS_PROCESSED, &processed); + while(processed--) { + alSourceUnqueueBuffers(device.source, 1, &albuffer); + alDeleteBuffers(1, &albuffer); + device.queue_length--; + } + //wait for buffer playback to catch up to sample generation if not synchronizing + if(settings.synchronize == false || device.queue_length < 3) break; + } + + if(device.queue_length < 3) { + alGenBuffers(1, &albuffer); + alBufferData(albuffer, device.format, buffer.data, buffer.size * 4, settings.frequency); + alSourceQueueBuffers(device.source, 1, &albuffer); + device.queue_length++; + } + + ALint playing; + alGetSourcei(device.source, AL_SOURCE_STATE, &playing); + if(playing != AL_PLAYING) alSourcePlay(device.source); + buffer.length = 0; + } + + void update_latency() { + if(buffer.data) delete[] buffer.data; + buffer.size = settings.frequency * settings.latency / 1000.0 + 0.5; + buffer.data = new uint32_t[buffer.size]; + } + + bool init() { + update_latency(); + device.queue_length = 0; + + bool success = false; + if(device.handle = alcOpenDevice(NULL)) { + if(device.context = alcCreateContext(device.handle, NULL)) { + alcMakeContextCurrent(device.context); + alGenSources(1, &device.source); + + //alSourcef (device.source, AL_PITCH, 1.0); + //alSourcef (device.source, AL_GAIN, 1.0); + //alSource3f(device.source, AL_POSITION, 0.0, 0.0, 0.0); + //alSource3f(device.source, AL_VELOCITY, 0.0, 0.0, 0.0); + //alSource3f(device.source, AL_DIRECTION, 0.0, 0.0, 0.0); + //alSourcef (device.source, AL_ROLLOFF_FACTOR, 0.0); + //alSourcei (device.source, AL_SOURCE_RELATIVE, AL_TRUE); + + alListener3f(AL_POSITION, 0.0, 0.0, 0.0); + alListener3f(AL_VELOCITY, 0.0, 0.0, 0.0); + ALfloat listener_orientation[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + alListenerfv(AL_ORIENTATION, listener_orientation); + + success = true; + } + } + + if(success == false) { + term(); + return false; + } + + return true; + } + + void term() { + if(alIsSource(device.source) == AL_TRUE) { + int playing = 0; + alGetSourcei(device.source, AL_SOURCE_STATE, &playing); + if(playing == AL_PLAYING) { + alSourceStop(device.source); + int queued = 0; + alGetSourcei(device.source, AL_BUFFERS_QUEUED, &queued); + while(queued--) { + ALuint albuffer = 0; + alSourceUnqueueBuffers(device.source, 1, &albuffer); + alDeleteBuffers(1, &albuffer); + device.queue_length--; + } + } + + alDeleteSources(1, &device.source); + device.source = 0; + } + + if(device.context) { + alcMakeContextCurrent(NULL); + alcDestroyContext(device.context); + device.context = 0; + } + + if(device.handle) { + alcCloseDevice(device.handle); + device.handle = 0; + } + + if(buffer.data) { + delete[] buffer.data; + buffer.data = 0; + } + } + + pAudioOpenAL(AudioOpenAL &self_) : self(self_) { + device.source = 0; + device.handle = 0; + device.context = 0; + device.format = AL_FORMAT_STEREO16; + device.queue_length = 0; + + buffer.data = 0; + buffer.length = 0; + buffer.size = 0; + + settings.synchronize = true; + settings.frequency = 22050; + settings.latency = 40; + } + + ~pAudioOpenAL() { + term(); + } +}; + +bool AudioOpenAL::cap(Setting setting) { return p.cap(setting); } +uintptr_t AudioOpenAL::get(Setting setting) { return p.get(setting); } +bool AudioOpenAL::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +void AudioOpenAL::sample(uint16_t sl, uint16_t sr) { p.sample(sl, sr); } +bool AudioOpenAL::init() { return p.init(); } +void AudioOpenAL::term() { p.term(); } +AudioOpenAL::AudioOpenAL() : p(*new pAudioOpenAL(*this)) {} +AudioOpenAL::~AudioOpenAL() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/audio/openal.hpp b/bsnes/lib/ruby/audio/openal.hpp new file mode 100755 index 0000000..cb8e65b --- /dev/null +++ b/bsnes/lib/ruby/audio/openal.hpp @@ -0,0 +1,24 @@ +/* + audio.openal (2007-12-26) + author: Nach + contributors: byuu, wertigon, _willow_ +*/ + +class pAudioOpenAL; + +class AudioOpenAL : public Audio { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + void sample(uint16_t sl, uint16_t sr); + bool init(); + void term(); + + AudioOpenAL(); + ~AudioOpenAL(); + +private: + pAudioOpenAL &p; +}; diff --git a/bsnes/lib/ruby/audio/oss.cpp b/bsnes/lib/ruby/audio/oss.cpp new file mode 100755 index 0000000..a16955f --- /dev/null +++ b/bsnes/lib/ruby/audio/oss.cpp @@ -0,0 +1,116 @@ +#include +#include +#include +#include + +//OSS4 soundcard.h includes below SNDCTL defines, but OSS3 does not +//However, OSS4 soundcard.h does not reside in +//Therefore, attempt to manually define SNDCTL values if using OSS3 header +//Note that if the defines below fail to work on any specific platform, one can point soundcard.h +//above to the correct location for OSS4 (usually /usr/lib/oss/include/sys/soundcard.h) +//Failing that, one can disable OSS4 ioctl calls inside init() and remove the below defines + +#ifndef SNDCTL_DSP_COOKEDMODE + #define SNDCTL_DSP_COOKEDMODE _IOW('P', 30, int) +#endif + +#ifndef SNDCTL_DSP_POLICY + #define SNDCTL_DSP_POLICY _IOW('P', 45, int) +#endif + +namespace ruby { + +#include "oss.hpp" + +class pAudioOSS { +public: + AudioOSS &self; + + struct { + int fd; + int format; + int channels; + const char *name; + } device; + + struct { + unsigned frequency; + } settings; + + bool cap(Audio::Setting setting) { + if(setting == Audio::Frequency) return true; + return false; + } + + uintptr_t get(Audio::Setting setting) { + if(setting == Audio::Frequency) return settings.frequency; + return false; + } + + bool set(Audio::Setting setting, uintptr_t param) { + if(setting == Audio::Frequency) { + settings.frequency = param; + if(device.fd > 0) { + term(); + init(); + } + return true; + } + return false; + } + + void sample(uint16_t sl, uint16_t sr) { + uint32_t sample = sl + (sr << 16); + unsigned unused = write(device.fd, &sample, 4); + } + + bool init() { + device.fd = open(device.name, O_WRONLY, O_NONBLOCK); + if(device.fd < 0) return false; + + #if 1 //SOUND_VERSION >= 0x040000 + //attempt to enable OSS4-specific features regardless of version + //OSS3 ioctl calls will silently fail, but sound will still work + int cooked = 1, policy = 4; //policy should be 0 - 10, lower = less latency, more CPU usage + ioctl(device.fd, SNDCTL_DSP_COOKEDMODE, &cooked); + ioctl(device.fd, SNDCTL_DSP_POLICY, &policy); + #endif + int freq = settings.frequency; + ioctl(device.fd, SNDCTL_DSP_CHANNELS, &device.channels); + ioctl(device.fd, SNDCTL_DSP_SETFMT, &device.format); + ioctl(device.fd, SNDCTL_DSP_SPEED, &freq); + + return true; + } + + void term() { + if(device.fd > 0) { + close(device.fd); + device.fd = -1; + } + } + + pAudioOSS(AudioOSS &self_) : self(self_) { + device.fd = -1; + device.format = AFMT_S16_LE; + device.channels = 2; + device.name = "/dev/dsp"; + + settings.frequency = 22050; + } + + ~pAudioOSS() { + term(); + } +}; + +bool AudioOSS::cap(Setting setting) { return p.cap(setting); } +uintptr_t AudioOSS::get(Setting setting) { return p.get(setting); } +bool AudioOSS::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +void AudioOSS::sample(uint16_t sl, uint16_t sr) { p.sample(sl, sr); } +bool AudioOSS::init() { return p.init(); } +void AudioOSS::term() { p.term(); } +AudioOSS::AudioOSS() : p(*new pAudioOSS(*this)) {} +AudioOSS::~AudioOSS() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/audio/oss.hpp b/bsnes/lib/ruby/audio/oss.hpp new file mode 100755 index 0000000..1d2777e --- /dev/null +++ b/bsnes/lib/ruby/audio/oss.hpp @@ -0,0 +1,23 @@ +/* + audio.oss (2007-12-26) + author: Nach +*/ + +class pAudioOSS; + +class AudioOSS : public Audio { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + void sample(uint16_t sl, uint16_t sr); + bool init(); + void term(); + + AudioOSS(); + ~AudioOSS(); + +private: + pAudioOSS &p; +}; diff --git a/bsnes/lib/ruby/audio/pulseaudio.cpp b/bsnes/lib/ruby/audio/pulseaudio.cpp new file mode 100755 index 0000000..d9c08bc --- /dev/null +++ b/bsnes/lib/ruby/audio/pulseaudio.cpp @@ -0,0 +1,121 @@ +#include +#include + +namespace ruby { + +#include "pulseaudio.hpp" + +class pAudioPulseAudio { +public: + struct { + pa_simple *handle; + pa_sample_spec spec; + } device; + + struct { + uint32_t *data; + unsigned offset; + } buffer; + + struct { + unsigned frequency; + } settings; + + AudioPulseAudio &self; + + bool cap(Audio::Setting setting) { + if(setting == Audio::Frequency) return true; + return false; + } + + uintptr_t get(Audio::Setting setting) { + if(setting == Audio::Frequency) return settings.frequency; + return false; + } + + bool set(Audio::Setting setting, uintptr_t param) { + if(setting == Audio::Frequency) { + settings.frequency = param; + if(device.handle) { + term(); + init(); + } + return true; + } + + return false; + } + + void sample(uint16_t left, uint16_t right) { + if(!device.handle) return; + + buffer.data[buffer.offset++] = left + (right << 16); + if(buffer.offset >= 64) { + int error; + pa_simple_write(device.handle, (const void*)buffer.data, buffer.offset * sizeof(uint32_t), &error); + buffer.offset = 0; + } + } + + bool init() { + device.spec.format = PA_SAMPLE_S16LE; + device.spec.channels = 2; + device.spec.rate = settings.frequency; + + int error = 0; + device.handle = pa_simple_new( + 0, //default server + "ruby::pulseaudio", //application name + PA_STREAM_PLAYBACK, //direction + 0, //default device + "audio", //stream description + &device.spec, //sample format + 0, //default channel map + 0, //default buffering attributes + &error //error code + ); + if(!device.handle) { + fprintf(stderr, "ruby::pulseaudio failed to initialize - %s\n", pa_strerror(error)); + return false; + } + + buffer.data = new uint32_t[64]; + buffer.offset = 0; + return true; + } + + void term() { + if(device.handle) { + int error; + pa_simple_flush(device.handle, &error); + pa_simple_free(device.handle); + device.handle = 0; + } + + if(buffer.data) { + delete[] buffer.data; + buffer.data = 0; + } + } + + pAudioPulseAudio(AudioPulseAudio &self_) : self(self_) { + device.handle = 0; + buffer.data = 0; + settings.frequency = 22050; + } + + ~pAudioPulseAudio() { + term(); + } +}; + +bool AudioPulseAudio::cap(Setting setting) { return p.cap(setting); } +uintptr_t AudioPulseAudio::get(Setting setting) { return p.get(setting); } +bool AudioPulseAudio::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +void AudioPulseAudio::sample(uint16_t left, uint16_t right) { return p.sample(left, right); } +bool AudioPulseAudio::init() { return p.init(); } +void AudioPulseAudio::term() { p.term(); } +AudioPulseAudio::AudioPulseAudio() : p(*new pAudioPulseAudio(*this)) {} +AudioPulseAudio::~AudioPulseAudio() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/audio/pulseaudio.hpp b/bsnes/lib/ruby/audio/pulseaudio.hpp new file mode 100755 index 0000000..e5cc80c --- /dev/null +++ b/bsnes/lib/ruby/audio/pulseaudio.hpp @@ -0,0 +1,23 @@ +/* + audio.pulseaudio (2008-10-31) + author: byuu +*/ + +class pAudioPulseAudio; + +class AudioPulseAudio : public Audio { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + void sample(uint16_t left, uint16_t right); + bool init(); + void term(); + + AudioPulseAudio(); + ~AudioPulseAudio(); + +private: + pAudioPulseAudio &p; +}; diff --git a/bsnes/lib/ruby/input.hpp b/bsnes/lib/ruby/input.hpp new file mode 100755 index 0000000..5387155 --- /dev/null +++ b/bsnes/lib/ruby/input.hpp @@ -0,0 +1,24 @@ +class Input { +public: + enum Setting { + Handle, + KeyboardSupport, + MouseSupport, + JoypadSupport, + }; + + virtual bool cap(Setting) { return false; } + virtual uintptr_t get(Setting) { return false; } + virtual bool set(Setting, uintptr_t) { return false; } + + virtual bool acquire() { return false; } + virtual bool unacquire() { return false; } + virtual bool acquired() { return false; } + + virtual bool poll(int16_t *table) { return false; } + virtual bool init() { return true; } + virtual void term() {} + + Input() {} + virtual ~Input() {} +}; diff --git a/bsnes/lib/ruby/input/directinput.cpp b/bsnes/lib/ruby/input/directinput.cpp new file mode 100755 index 0000000..4bdfe23 --- /dev/null +++ b/bsnes/lib/ruby/input/directinput.cpp @@ -0,0 +1,403 @@ +#define DIRECTINPUT_VERSION 0x0800 +#include +#include + +namespace ruby { + +#include "directinput.hpp" + +static BOOL CALLBACK DI_EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*); +static BOOL CALLBACK DI_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE*, void*); + +using namespace nall; + +class pInputDI { +public: + InputDI &self; + + struct { + LPDIRECTINPUT8 context; + LPDIRECTINPUTDEVICE8 keyboard; + LPDIRECTINPUTDEVICE8 mouse; + LPDIRECTINPUTDEVICE8 gamepad[joypad<>::count]; + bool mouseacquired; + } device; + + struct { + HWND handle; + } settings; + + bool cap(Input::Setting setting) { + if(setting == Input::Handle) return true; + if(setting == Input::KeyboardSupport) return true; + if(setting == Input::MouseSupport) return true; + if(setting == Input::JoypadSupport) return true; + return false; + } + + uintptr_t get(Input::Setting setting) { + if(setting == Input::Handle) return (uintptr_t)settings.handle; + return false; + } + + bool set(Input::Setting setting, uintptr_t param) { + if(setting == Input::Handle) { + settings.handle = (HWND)param; + return true; + } + + return false; + } + + bool poll(int16_t *table) { + memset(table, 0, nall::input_limit * sizeof(int16_t)); + + //======== + //Keyboard + //======== + + if(device.keyboard) { + uint8_t state[256]; + if(FAILED(device.keyboard->GetDeviceState(sizeof state, state))) { + device.keyboard->Acquire(); + if(FAILED(device.keyboard->GetDeviceState(sizeof state, state))) { + memset(state, 0, sizeof state); + } + } + + table[keyboard<0>::escape] = (bool)(state[0x01] & 0x80); + table[keyboard<0>::f1 ] = (bool)(state[0x3b] & 0x80); + table[keyboard<0>::f2 ] = (bool)(state[0x3c] & 0x80); + table[keyboard<0>::f3 ] = (bool)(state[0x3d] & 0x80); + table[keyboard<0>::f4 ] = (bool)(state[0x3e] & 0x80); + table[keyboard<0>::f5 ] = (bool)(state[0x3f] & 0x80); + table[keyboard<0>::f6 ] = (bool)(state[0x40] & 0x80); + table[keyboard<0>::f7 ] = (bool)(state[0x41] & 0x80); + table[keyboard<0>::f8 ] = (bool)(state[0x42] & 0x80); + table[keyboard<0>::f9 ] = (bool)(state[0x43] & 0x80); + table[keyboard<0>::f10 ] = (bool)(state[0x44] & 0x80); + table[keyboard<0>::f11 ] = (bool)(state[0x57] & 0x80); + table[keyboard<0>::f12 ] = (bool)(state[0x58] & 0x80); + + table[keyboard<0>::print_screen] = (bool)(state[0xb7] & 0x80); + table[keyboard<0>::scroll_lock ] = (bool)(state[0x46] & 0x80); + table[keyboard<0>::pause ] = (bool)(state[0xc5] & 0x80); + table[keyboard<0>::tilde ] = (bool)(state[0x29] & 0x80); + + table[keyboard<0>::num_1] = (bool)(state[0x02] & 0x80); + table[keyboard<0>::num_2] = (bool)(state[0x03] & 0x80); + table[keyboard<0>::num_3] = (bool)(state[0x04] & 0x80); + table[keyboard<0>::num_4] = (bool)(state[0x05] & 0x80); + table[keyboard<0>::num_5] = (bool)(state[0x06] & 0x80); + table[keyboard<0>::num_6] = (bool)(state[0x07] & 0x80); + table[keyboard<0>::num_7] = (bool)(state[0x08] & 0x80); + table[keyboard<0>::num_8] = (bool)(state[0x09] & 0x80); + table[keyboard<0>::num_9] = (bool)(state[0x0a] & 0x80); + table[keyboard<0>::num_0] = (bool)(state[0x0b] & 0x80); + + table[keyboard<0>::dash ] = (bool)(state[0x0c] & 0x80); + table[keyboard<0>::equal ] = (bool)(state[0x0d] & 0x80); + table[keyboard<0>::backspace] = (bool)(state[0x0e] & 0x80); + + table[keyboard<0>::insert ] = (bool)(state[0xd2] & 0x80); + table[keyboard<0>::delete_ ] = (bool)(state[0xd3] & 0x80); + table[keyboard<0>::home ] = (bool)(state[0xc7] & 0x80); + table[keyboard<0>::end ] = (bool)(state[0xcf] & 0x80); + table[keyboard<0>::page_up ] = (bool)(state[0xc9] & 0x80); + table[keyboard<0>::page_down] = (bool)(state[0xd1] & 0x80); + + table[keyboard<0>::a] = (bool)(state[0x1e] & 0x80); + table[keyboard<0>::b] = (bool)(state[0x30] & 0x80); + table[keyboard<0>::c] = (bool)(state[0x2e] & 0x80); + table[keyboard<0>::d] = (bool)(state[0x20] & 0x80); + table[keyboard<0>::e] = (bool)(state[0x12] & 0x80); + table[keyboard<0>::f] = (bool)(state[0x21] & 0x80); + table[keyboard<0>::g] = (bool)(state[0x22] & 0x80); + table[keyboard<0>::h] = (bool)(state[0x23] & 0x80); + table[keyboard<0>::i] = (bool)(state[0x17] & 0x80); + table[keyboard<0>::j] = (bool)(state[0x24] & 0x80); + table[keyboard<0>::k] = (bool)(state[0x25] & 0x80); + table[keyboard<0>::l] = (bool)(state[0x26] & 0x80); + table[keyboard<0>::m] = (bool)(state[0x32] & 0x80); + table[keyboard<0>::n] = (bool)(state[0x31] & 0x80); + table[keyboard<0>::o] = (bool)(state[0x18] & 0x80); + table[keyboard<0>::p] = (bool)(state[0x19] & 0x80); + table[keyboard<0>::q] = (bool)(state[0x10] & 0x80); + table[keyboard<0>::r] = (bool)(state[0x13] & 0x80); + table[keyboard<0>::s] = (bool)(state[0x1f] & 0x80); + table[keyboard<0>::t] = (bool)(state[0x14] & 0x80); + table[keyboard<0>::u] = (bool)(state[0x16] & 0x80); + table[keyboard<0>::v] = (bool)(state[0x2f] & 0x80); + table[keyboard<0>::w] = (bool)(state[0x11] & 0x80); + table[keyboard<0>::x] = (bool)(state[0x2d] & 0x80); + table[keyboard<0>::y] = (bool)(state[0x15] & 0x80); + table[keyboard<0>::z] = (bool)(state[0x2c] & 0x80); + + table[keyboard<0>::lbracket ] = (bool)(state[0x1a] & 0x80); + table[keyboard<0>::rbracket ] = (bool)(state[0x1b] & 0x80); + table[keyboard<0>::backslash ] = (bool)(state[0x2b] & 0x80); + table[keyboard<0>::semicolon ] = (bool)(state[0x27] & 0x80); + table[keyboard<0>::apostrophe] = (bool)(state[0x28] & 0x80); + table[keyboard<0>::comma ] = (bool)(state[0x33] & 0x80); + table[keyboard<0>::period ] = (bool)(state[0x34] & 0x80); + table[keyboard<0>::slash ] = (bool)(state[0x35] & 0x80); + + table[keyboard<0>::pad_0] = (bool)(state[0x4f] & 0x80); + table[keyboard<0>::pad_1] = (bool)(state[0x50] & 0x80); + table[keyboard<0>::pad_2] = (bool)(state[0x51] & 0x80); + table[keyboard<0>::pad_3] = (bool)(state[0x4b] & 0x80); + table[keyboard<0>::pad_4] = (bool)(state[0x4c] & 0x80); + table[keyboard<0>::pad_5] = (bool)(state[0x4d] & 0x80); + table[keyboard<0>::pad_6] = (bool)(state[0x47] & 0x80); + table[keyboard<0>::pad_7] = (bool)(state[0x48] & 0x80); + table[keyboard<0>::pad_8] = (bool)(state[0x49] & 0x80); + table[keyboard<0>::pad_9] = (bool)(state[0x52] & 0x80); + table[keyboard<0>::point] = (bool)(state[0x53] & 0x80); + + table[keyboard<0>::add] = (bool)(state[0x4e] & 0x80); + table[keyboard<0>::subtract] = (bool)(state[0x4a] & 0x80); + table[keyboard<0>::multiply] = (bool)(state[0x37] & 0x80); + table[keyboard<0>::divide] = (bool)(state[0xb5] & 0x80); + table[keyboard<0>::enter] = (bool)(state[0x9c] & 0x80); + + table[keyboard<0>::num_lock ] = (bool)(state[0x45] & 0x80); + table[keyboard<0>::caps_lock] = (bool)(state[0x3a] & 0x80); + + table[keyboard<0>::up ] = (bool)(state[0xc8] & 0x80); + table[keyboard<0>::down ] = (bool)(state[0xd0] & 0x80); + table[keyboard<0>::left ] = (bool)(state[0xcb] & 0x80); + table[keyboard<0>::right] = (bool)(state[0xcd] & 0x80); + + table[keyboard<0>::tab ] = (bool)(state[0x0f] & 0x80); + table[keyboard<0>::return_ ] = (bool)(state[0x1c] & 0x80); + table[keyboard<0>::spacebar] = (bool)(state[0x39] & 0x80); + + table[keyboard<0>::lctrl ] = (bool)(state[0x1d] & 0x80); + table[keyboard<0>::rctrl ] = (bool)(state[0x9d] & 0x80); + table[keyboard<0>::lalt ] = (bool)(state[0x38] & 0x80); + table[keyboard<0>::ralt ] = (bool)(state[0xb8] & 0x80); + table[keyboard<0>::lshift] = (bool)(state[0x2a] & 0x80); + table[keyboard<0>::rshift] = (bool)(state[0x36] & 0x80); + table[keyboard<0>::lsuper] = (bool)(state[0xdb] & 0x80); + table[keyboard<0>::rsuper] = (bool)(state[0xdc] & 0x80); + table[keyboard<0>::menu ] = (bool)(state[0xdd] & 0x80); + } + + //===== + //Mouse + //===== + + if(device.mouse) { + DIMOUSESTATE2 state; + if(FAILED(device.mouse->GetDeviceState(sizeof(DIMOUSESTATE2), (void*)&state))) { + device.mouse->Acquire(); + if(FAILED(device.mouse->GetDeviceState(sizeof(DIMOUSESTATE2), (void*)&state))) { + memset(&state, 0, sizeof(DIMOUSESTATE2)); + } + } + + table[mouse<0>::x] = state.lX; + table[mouse<0>::y] = state.lY; + table[mouse<0>::z] = state.lZ / WHEEL_DELTA; + for(unsigned n = 0; n < mouse<>::buttons; n++) { + table[mouse<0>::button + n] = (bool)state.rgbButtons[n]; + } + + //on Windows, 0 = left, 1 = right, 2 = middle + //swap middle and right buttons for consistency with Linux + int16_t temp = table[mouse<0>::button + 1]; + table[mouse<0>::button + 1] = table[mouse<0>::button + 2]; + table[mouse<0>::button + 2] = temp; + } + + //========= + //Joypad(s) + //========= + + for(unsigned i = 0; i < joypad<>::count; i++) { + if(!device.gamepad[i]) continue; + unsigned index = joypad<>::index(i, joypad<>::none); + + if(FAILED(device.gamepad[i]->Poll())) { + device.gamepad[i]->Acquire(); + continue; + } + + DIJOYSTATE2 state; + device.gamepad[i]->GetDeviceState(sizeof(DIJOYSTATE2), &state); + + //POV hats + for(unsigned n = 0; n < min((unsigned)joypad<>::hats, 4); n++) { + //POV value is in clockwise-hundredth degree units. + unsigned pov = state.rgdwPOV[n]; + //some drivers report a centered POV hat as -1U, others as 65535U. + //>= 36000 will match both, as well as invalid ranges. + if(pov < 36000) { + if(pov >= 31500 || pov <= 4500) table[index + joypad<>::hat + n] |= joypad<>::hat_up; + if(pov >= 4500 && pov <= 13500) table[index + joypad<>::hat + n] |= joypad<>::hat_right; + if(pov >= 13500 && pov <= 22500) table[index + joypad<>::hat + n] |= joypad<>::hat_down; + if(pov >= 22500 && pov <= 31500) table[index + joypad<>::hat + n] |= joypad<>::hat_left; + } + } + + //axes + table[index + joypad<>::axis + 0] = state.lX; + table[index + joypad<>::axis + 1] = state.lY; + table[index + joypad<>::axis + 2] = state.lZ; + table[index + joypad<>::axis + 3] = state.lRx; + table[index + joypad<>::axis + 4] = state.lRy; + table[index + joypad<>::axis + 5] = state.lRz; + + //buttons + for(unsigned n = 0; n < min((unsigned)joypad<>::buttons, 128); n++) { + table[index + joypad<>::button + n] = (bool)state.rgbButtons[n]; + } + } + + return true; + } + + bool init_joypad(const DIDEVICEINSTANCE *instance) { + unsigned n; + for(n = 0; n < joypad<>::count; n++) { if(!device.gamepad[n]) break; } + if(n >= joypad<>::count) return DIENUM_STOP; + + if(FAILED(device.context->CreateDevice(instance->guidInstance, &device.gamepad[n], 0))) { + return DIENUM_CONTINUE; //continue and try next gamepad + } + + device.gamepad[n]->SetDataFormat(&c_dfDIJoystick2); + device.gamepad[n]->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + device.gamepad[n]->EnumObjects(DI_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS); + + return DIENUM_CONTINUE; + } + + bool init_axis(const DIDEVICEOBJECTINSTANCE *instance) { + signed n; + for(n = joypad<>::count - 1; n >= 0; n--) { if(device.gamepad[n]) break; } + if(n < 0) return DIENUM_STOP; + + DIPROPRANGE range; + range.diph.dwSize = sizeof(DIPROPRANGE); + range.diph.dwHeaderSize = sizeof(DIPROPHEADER); + range.diph.dwHow = DIPH_BYID; + range.diph.dwObj = instance->dwType; + range.lMin = -32768; + range.lMax = +32767; + device.gamepad[n]->SetProperty(DIPROP_RANGE, &range.diph); + + return DIENUM_CONTINUE; + } + + bool init() { + device.context = 0; + device.keyboard = 0; + device.mouse = 0; + for(unsigned i = 0; i < joypad<>::count; i++) device.gamepad[i] = 0; + device.mouseacquired = false; + + DirectInput8Create(GetModuleHandle(0), 0x0800, IID_IDirectInput8, (void**)&device.context, 0); + + device.context->CreateDevice(GUID_SysKeyboard, &device.keyboard, 0); + device.keyboard->SetDataFormat(&c_dfDIKeyboard); + device.keyboard->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + device.keyboard->Acquire(); + + device.context->CreateDevice(GUID_SysMouse, &device.mouse, 0); + device.mouse->SetDataFormat(&c_dfDIMouse2); + HRESULT hr = device.mouse->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + device.mouse->Acquire(); + + device.context->EnumDevices(DI8DEVCLASS_GAMECTRL, DI_EnumJoypadsCallback, (void*)this, DIEDFL_ATTACHEDONLY); + + return true; + } + + void term() { + if(device.keyboard) { + device.keyboard->Unacquire(); + device.keyboard->Release(); + device.keyboard = 0; + } + + if(device.mouse) { + device.mouse->Unacquire(); + device.mouse->Release(); + device.mouse = 0; + } + + for(unsigned i = 0; i < joypad<>::count; i++) { + if(device.gamepad[i]) { + device.gamepad[i]->Unacquire(); + device.gamepad[i]->Release(); + device.gamepad[i] = 0; + } + } + + if(device.context) { + device.context->Release(); + device.context = 0; + } + } + + bool acquire() { + if(!device.mouse) return false; + if(acquired() == false) { + device.mouse->Unacquire(); + device.mouse->SetCooperativeLevel(settings.handle, DISCL_EXCLUSIVE | DISCL_FOREGROUND); + device.mouse->Acquire(); + device.mouseacquired = true; + } + return true; + } + + bool unacquire() { + if(!device.mouse) return false; + if(acquired() == true) { + device.mouse->Unacquire(); + device.mouse->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + device.mouse->Acquire(); + device.mouseacquired = false; + } + return true; + } + + bool acquired() { + return device.mouseacquired; + } + + pInputDI(InputDI &self_) : self(self_) { + device.context = 0; + device.keyboard = 0; + device.mouse = 0; + for(unsigned i = 0; i < joypad<>::count; i++) device.gamepad[i] = 0; + device.mouseacquired = false; + + settings.handle = 0; + } + + ~pInputDI() { term(); } +}; + +BOOL CALLBACK DI_EnumJoypadsCallback(const DIDEVICEINSTANCE *instance, void *p) { + return ((pInputDI*)p)->init_joypad(instance); +} + +BOOL CALLBACK DI_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE *instance, void *p) { + return ((pInputDI*)p)->init_axis(instance); +} + +bool InputDI::cap(Setting setting) { return p.cap(setting); } +uintptr_t InputDI::get(Setting setting) { return p.get(setting); } +bool InputDI::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +bool InputDI::acquire() { return p.acquire(); } +bool InputDI::unacquire() { return p.unacquire(); } +bool InputDI::acquired() { return p.acquired(); } +bool InputDI::poll(int16_t *table) { return p.poll(table); } +bool InputDI::init() { return p.init(); } +void InputDI::term() { p.term(); } +InputDI::InputDI() : p(*new pInputDI(*this)) {} +InputDI::~InputDI() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/input/directinput.hpp b/bsnes/lib/ruby/input/directinput.hpp new file mode 100755 index 0000000..17c9756 --- /dev/null +++ b/bsnes/lib/ruby/input/directinput.hpp @@ -0,0 +1,22 @@ +class pInputDI; + +class InputDI : public Input { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + bool acquire(); + bool unacquire(); + bool acquired(); + + bool poll(int16_t *table); + bool init(); + void term(); + + InputDI(); + ~InputDI(); + +private: + pInputDI &p; +}; diff --git a/bsnes/lib/ruby/input/rawinput.cpp b/bsnes/lib/ruby/input/rawinput.cpp new file mode 100755 index 0000000..a576c7c --- /dev/null +++ b/bsnes/lib/ruby/input/rawinput.cpp @@ -0,0 +1,781 @@ +//RawInput driver +//author: byuu + +//this driver utilizes RawInput (WM_INPUT) to capture keyboard and mouse input. +//although this requires WinXP or newer, it is the only way to uniquely identify +//and independently map multiple keyboards and mice. DirectInput merges all +//keyboards and mice into one device per. +// +//as WM_INPUT lacks specific RAWINPUT structures for gamepads, giving only raw +//data, and because DirectInput supports up to 16 joypads, DirectInput is used +//for joypad mapping. +// +//further, Xbox 360 controllers are explicitly detected and supported through +//XInput. this is because under DirectInput, the LT / RT (trigger) buttons are +//merged into a single Z-axis -- making it impossible to detect both buttons +//being pressed at the same time. with XInput, the state of both trigger +//buttons can be read independently. +// +//so in essence, this is actually more of a hybrid driver. + +#define DIRECTINPUT_VERSION 0x0800 +#include +#include + +namespace ruby { + +#include "rawinput.hpp" + +DWORD WINAPI RawInputThreadProc(void*); +LRESULT CALLBACK RawInputWindowProc(HWND, UINT, WPARAM, LPARAM); + +class RawInput { +public: + HANDLE mutex; + HWND hwnd; + bool initialized; + bool ready; + + struct Device { + HANDLE handle; + }; + + struct Keyboard : Device { + bool state[keyboard<>::length]; + + void update(RAWINPUT *input) { + unsigned code = input->data.keyboard.MakeCode; + unsigned flags = input->data.keyboard.Flags; + + #define map(id, flag, name) if(code == id) state[name] = (bool)(flags == flag); + map(0x0001, 0, keyboard<>::escape) + map(0x003b, 0, keyboard<>::f1) + map(0x003c, 0, keyboard<>::f2) + map(0x003d, 0, keyboard<>::f3) + map(0x003e, 0, keyboard<>::f4) + map(0x003f, 0, keyboard<>::f5) + map(0x0040, 0, keyboard<>::f6) + map(0x0041, 0, keyboard<>::f7) + map(0x0042, 0, keyboard<>::f8) + map(0x0043, 0, keyboard<>::f9) + map(0x0044, 0, keyboard<>::f10) + map(0x0057, 0, keyboard<>::f11) + map(0x0058, 0, keyboard<>::f12) + + map(0x0037, 2, keyboard<>::print_screen) + map(0x0046, 0, keyboard<>::scroll_lock) + map(0x001d, 4, keyboard<>::pause) + map(0x0029, 0, keyboard<>::tilde) + + map(0x0002, 0, keyboard<>::num_1) + map(0x0003, 0, keyboard<>::num_2) + map(0x0004, 0, keyboard<>::num_3) + map(0x0005, 0, keyboard<>::num_4) + map(0x0006, 0, keyboard<>::num_5) + map(0x0007, 0, keyboard<>::num_6) + map(0x0008, 0, keyboard<>::num_7) + map(0x0009, 0, keyboard<>::num_8) + map(0x000a, 0, keyboard<>::num_9) + map(0x000b, 0, keyboard<>::num_0) + + map(0x000c, 0, keyboard<>::dash) + map(0x000d, 0, keyboard<>::equal) + map(0x000e, 0, keyboard<>::backspace) + + map(0x0052, 2, keyboard<>::insert) + map(0x0053, 2, keyboard<>::delete_) + map(0x0047, 2, keyboard<>::home) + map(0x004f, 2, keyboard<>::end) + map(0x0049, 2, keyboard<>::page_up) + map(0x0051, 2, keyboard<>::page_down) + + map(0x001e, 0, keyboard<>::a) + map(0x0030, 0, keyboard<>::b) + map(0x002e, 0, keyboard<>::c) + map(0x0020, 0, keyboard<>::d) + map(0x0012, 0, keyboard<>::e) + map(0x0021, 0, keyboard<>::f) + map(0x0022, 0, keyboard<>::g) + map(0x0023, 0, keyboard<>::h) + map(0x0017, 0, keyboard<>::i) + map(0x0024, 0, keyboard<>::j) + map(0x0025, 0, keyboard<>::k) + map(0x0026, 0, keyboard<>::l) + map(0x0032, 0, keyboard<>::m) + map(0x0031, 0, keyboard<>::n) + map(0x0018, 0, keyboard<>::o) + map(0x0019, 0, keyboard<>::p) + map(0x0010, 0, keyboard<>::q) + map(0x0013, 0, keyboard<>::r) + map(0x001f, 0, keyboard<>::s) + map(0x0014, 0, keyboard<>::t) + map(0x0016, 0, keyboard<>::u) + map(0x002f, 0, keyboard<>::v) + map(0x0011, 0, keyboard<>::w) + map(0x002d, 0, keyboard<>::x) + map(0x0015, 0, keyboard<>::y) + map(0x002c, 0, keyboard<>::z) + + map(0x001a, 0, keyboard<>::lbracket) + map(0x001b, 0, keyboard<>::rbracket) + map(0x002b, 0, keyboard<>::backslash) + map(0x0027, 0, keyboard<>::semicolon) + map(0x0028, 0, keyboard<>::apostrophe) + map(0x0033, 0, keyboard<>::comma) + map(0x0034, 0, keyboard<>::period) + map(0x0035, 0, keyboard<>::slash) + + map(0x004f, 0, keyboard<>::pad_1) + map(0x0050, 0, keyboard<>::pad_2) + map(0x0051, 0, keyboard<>::pad_3) + map(0x004b, 0, keyboard<>::pad_4) + map(0x004c, 0, keyboard<>::pad_5) + map(0x004d, 0, keyboard<>::pad_6) + map(0x0047, 0, keyboard<>::pad_7) + map(0x0048, 0, keyboard<>::pad_8) + map(0x0049, 0, keyboard<>::pad_9) + map(0x0052, 0, keyboard<>::pad_0) + + map(0x0053, 0, keyboard<>::point) + map(0x001c, 2, keyboard<>::enter) + map(0x004e, 0, keyboard<>::add) + map(0x004a, 0, keyboard<>::subtract) + map(0x0037, 0, keyboard<>::multiply) + map(0x0035, 2, keyboard<>::divide) + + map(0x0045, 0, keyboard<>::num_lock) + map(0x003a, 0, keyboard<>::caps_lock) + + //pause signals 0x1d:4 + 0x45:0, whereas num_lock signals only 0x45:0. + //this makes it impractical to detect both pause+num_lock independently. + //workaround: always detect pause; detect num_lock only when pause is released. + if(state[keyboard<>::pause]) state[keyboard<>::num_lock] = false; + + map(0x0048, 2, keyboard<>::up) + map(0x0050, 2, keyboard<>::down) + map(0x004b, 2, keyboard<>::left) + map(0x004d, 2, keyboard<>::right) + + map(0x000f, 0, keyboard<>::tab) + map(0x001c, 0, keyboard<>::return_) + map(0x0039, 0, keyboard<>::spacebar) + + map(0x001d, 0, keyboard<>::lctrl) + map(0x001d, 2, keyboard<>::rctrl) + map(0x0038, 0, keyboard<>::lalt) + map(0x0038, 2, keyboard<>::ralt) + map(0x002a, 0, keyboard<>::lshift) + map(0x0036, 0, keyboard<>::rshift) + map(0x005b, 2, keyboard<>::lsuper) + map(0x005c, 2, keyboard<>::rsuper) + map(0x005d, 2, keyboard<>::menu) + #undef map + } + + Keyboard() { + for(unsigned i = 0; i < keyboard<>::length; i++) state[i] = false; + } + }; + + struct Mouse : Device { + signed xDistance; + signed yDistance; + signed zDistance; + unsigned buttonState; + + void sync() { + xDistance = 0; + yDistance = 0; + zDistance = 0; + } + + void update(RAWINPUT *input) { + if((input->data.mouse.usFlags & 1) == MOUSE_MOVE_RELATIVE) { + xDistance += input->data.mouse.lLastX; + yDistance += input->data.mouse.lLastY; + } + + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) buttonState |= 1 << 0; + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP ) buttonState &=~ 1 << 0; + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) buttonState |= 1 << 2; //swap middle and right buttons, + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP ) buttonState &=~ 1 << 2; //for consistency with Linux: + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) buttonState |= 1 << 1; //left = 0, middle = 1, right = 2 + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP ) buttonState &=~ 1 << 1; + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) buttonState |= 1 << 3; + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP ) buttonState &=~ 1 << 3; + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) buttonState |= 1 << 4; + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP ) buttonState &=~ 1 << 4; + + if(input->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) { + zDistance += (int16_t)input->data.mouse.usButtonData; + } + } + + Mouse() { + xDistance = yDistance = zDistance = 0; + buttonState = 0; + } + }; + + //keep track of gamepads for the sole purpose of distinguishing XInput devices + //from all other devices. this is necessary, as DirectInput does not provide + //a way to retrieve the necessary RIDI_DEVICENAME string. + struct Gamepad : Device { + bool isXInputDevice; + uint16_t vendorId; + uint16_t productId; + }; + + vector lkeyboard; + vector lmouse; + vector lgamepad; + + LRESULT window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + if(msg == WM_INPUT) { + unsigned size = 0; + GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); + RAWINPUT *input = new RAWINPUT[size]; + GetRawInputData((HRAWINPUT)lparam, RID_INPUT, input, &size, sizeof(RAWINPUTHEADER)); + WaitForSingleObject(mutex, INFINITE); + + if(input->header.dwType == RIM_TYPEKEYBOARD) { + for(unsigned i = 0; i < lkeyboard.size(); i++) { + if(input->header.hDevice == lkeyboard[i].handle) { + lkeyboard[i].update(input); + break; + } + } + } else if(input->header.dwType == RIM_TYPEMOUSE) { + for(unsigned i = 0; i < lmouse.size(); i++) { + if(input->header.hDevice == lmouse[i].handle) { + lmouse[i].update(input); + break; + } + } + } + + ReleaseMutex(mutex); + //allow propogation of WM_INPUT message + LRESULT result = DefRawInputProc(&input, size, sizeof(RAWINPUTHEADER)); + delete[] input; + return result; + } + + return DefWindowProc(hwnd, msg, wparam, lparam); + } + + //this is used to sort device IDs + struct DevicePool { + HANDLE handle; + char name[4096]; + bool operator<(const DevicePool &pool) const { return strcmp(name, pool.name) < 0; } + }; + + int main() { + //create an invisible window to act as a sink, capturing all WM_INPUT messages + WNDCLASS wc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = (HBRUSH)COLOR_WINDOW; + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = RawInputWindowProc; + wc.lpszClassName = "RawInputClass"; + wc.lpszMenuName = 0; + wc.style = CS_VREDRAW | CS_HREDRAW; + RegisterClass(&wc); + + hwnd = CreateWindow("RawInputClass", "RawInputClass", WS_POPUP, + 0, 0, 64, 64, 0, 0, GetModuleHandle(0), 0); + + //enumerate all HID devices + unsigned devices = 0; + GetRawInputDeviceList(NULL, &devices, sizeof(RAWINPUTDEVICELIST)); + RAWINPUTDEVICELIST *list = new RAWINPUTDEVICELIST[devices]; + GetRawInputDeviceList(list, &devices, sizeof(RAWINPUTDEVICELIST)); + + //sort all devices by name. this has two important properties: + //1) it consistently orders peripherals, so mapped IDs remain constant + //2) it sorts the virtual keyboard and mouse to the bottom of the list + // (real devices start with \\?\HID#, virtual with \\?\Root#) + DevicePool pool[devices]; + for(unsigned i = 0; i < devices; i++) { + pool[i].handle = list[i].hDevice; + unsigned size = sizeof(pool[i].name) - 1; + GetRawInputDeviceInfo(list[i].hDevice, RIDI_DEVICENAME, &pool[i].name, &size); + } + nall::sort(pool, devices); + delete[] list; + + for(unsigned i = 0; i < devices; i++) { + RID_DEVICE_INFO info; + info.cbSize = sizeof(RID_DEVICE_INFO); + + unsigned size = info.cbSize; + GetRawInputDeviceInfo(pool[i].handle, RIDI_DEVICEINFO, &info, &size); + + if(info.dwType == RIM_TYPEKEYBOARD) { + unsigned n = lkeyboard.size(); + lkeyboard[n].handle = pool[i].handle; + } else if(info.dwType == RIM_TYPEMOUSE) { + unsigned n = lmouse.size(); + lmouse[n].handle = pool[i].handle; + } else if(info.dwType == RIM_TYPEHID) { + //if this is a gamepad or joystick device ... + if(info.hid.usUsagePage == 1 && (info.hid.usUsage == 4 || info.hid.usUsage == 5)) { + //... then cache device information for later use + unsigned n = lgamepad.size(); + lgamepad[n].handle = pool[i].handle; + lgamepad[n].vendorId = (uint16_t)info.hid.dwVendorId; + lgamepad[n].productId = (uint16_t)info.hid.dwProductId; + + //per MSDN: XInput devices have "IG_" in their device strings, + //which is how they should be identified. + const char *p = strstr(pool[i].name, "IG_"); + lgamepad[n].isXInputDevice = (bool)p; + } + } + } + + RAWINPUTDEVICE device[2]; + //capture all keyboard input + device[0].usUsagePage = 1; + device[0].usUsage = 6; + device[0].dwFlags = RIDEV_INPUTSINK; + device[0].hwndTarget = hwnd; + //capture all mouse input + device[1].usUsagePage = 1; + device[1].usUsage = 2; + device[1].dwFlags = RIDEV_INPUTSINK; + device[1].hwndTarget = hwnd; + RegisterRawInputDevices(device, 2, sizeof(RAWINPUTDEVICE)); + + WaitForSingleObject(mutex, INFINITE); + ready = true; + ReleaseMutex(mutex); + + while(true) { + MSG msg; + GetMessage(&msg, hwnd, 0, 0); + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; + } + + RawInput() : initialized(false), ready(false) { + } +}; + +static RawInput rawinput; + +DWORD WINAPI RawInputThreadProc(void*) { + return rawinput.main(); +} + +LRESULT CALLBACK RawInputWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + return rawinput.window_proc(hwnd, msg, wparam, lparam); +} + +class XInput { +public: + struct Gamepad { + unsigned id; + + int16_t hat; + int16_t axis[6]; + bool button[10]; + + void poll(XINPUT_STATE &state) { + hat = joypad<>::hat_center; + if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hat |= joypad<>::hat_up; + if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hat |= joypad<>::hat_right; + if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hat |= joypad<>::hat_down; + if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hat |= joypad<>::hat_left; + + axis[0] = (int16_t)state.Gamepad.sThumbLX; + axis[1] = (int16_t)state.Gamepad.sThumbLY; + axis[2] = (int16_t)state.Gamepad.sThumbRX; + axis[3] = (int16_t)state.Gamepad.sThumbRY; + + //transform left and right trigger ranges: + //from: 0 (low, eg released) to 255 (high, eg pressed all the way down) + //to: +32767 (low) to -32768 (high) + uint16_t triggerX = state.Gamepad.bLeftTrigger; + uint16_t triggerY = state.Gamepad.bRightTrigger; + + triggerX = (triggerX << 8) | triggerX; + triggerY = (triggerY << 8) | triggerY; + + axis[4] = (~triggerX) - 32768; + axis[5] = (~triggerY) - 32768; + + button[0] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_A); + button[1] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_B); + button[2] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_X); + button[3] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y); + button[4] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK); + button[5] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_START); + button[6] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER); + button[7] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER); + button[8] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB); + button[9] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB); + } + + Gamepad() { + hat = joypad<>::hat_center; + for(unsigned n = 0; n < 6; n++) axis[n] = 0; + for(unsigned n = 0; n < 10; n++) button[n] = false; + } + }; + + vector lgamepad; + + void poll() { + for(unsigned i = 0; i < lgamepad.size(); i++) { + XINPUT_STATE state; + DWORD result = XInputGetState(lgamepad[i].id, &state); + if(result == ERROR_SUCCESS) lgamepad[i].poll(state); + } + } + + void init() { + //XInput only supports up to four controllers + for(unsigned i = 0; i <= 3; i++) { + XINPUT_STATE state; + DWORD result = XInputGetState(i, &state); + if(result == ERROR_SUCCESS) { + //valid controller detected, add to gamepad list + unsigned n = lgamepad.size(); + lgamepad[n].id = i; + } + } + } +}; + +static BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*); +static BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE*, void*); + +class DirectInput { +public: + HWND handle; + LPDIRECTINPUT8 context; + struct Gamepad { + LPDIRECTINPUTDEVICE8 handle; + + int16_t hat[4]; + int16_t axis[6]; + bool button[128]; + + void poll(DIJOYSTATE2 &state) { + //POV hats + for(unsigned n = 0; n < 4; n++) { + hat[n] = joypad<>::hat_center; + + //POV value is in clockwise-hundredth degree units + unsigned pov = state.rgdwPOV[n]; + + //some drivers report a centered POV hat as -1U, others as 65535U. + //>= 36000 will match both, as well as invalid ranges. + if(pov >= 36000) continue; + + if(pov >= 31500 || pov <= 4500) hat[n] |= joypad<>::hat_up; + if(pov >= 4500 && pov <= 13500) hat[n] |= joypad<>::hat_right; + if(pov >= 13500 && pov <= 22500) hat[n] |= joypad<>::hat_down; + if(pov >= 22500 && pov <= 31500) hat[n] |= joypad<>::hat_left; + } + + //axes + axis[0] = state.lX; + axis[1] = state.lY; + axis[2] = state.lZ; + axis[3] = state.lRx; + axis[4] = state.lRy; + axis[5] = state.lRz; + + //buttons + for(unsigned n = 0; n < 128; n++) { + button[n] = (bool)state.rgbButtons[n]; + } + } + + Gamepad() { + handle = 0; + for(unsigned n = 0; n < 4; n++) hat[n] = joypad<>::hat_center; + for(unsigned n = 0; n < 6; n++) axis[n] = 0; + for(unsigned n = 0; n < 128; n++) button[n] = false; + } + }; + vector lgamepad; + + void poll() { + for(unsigned i = 0; i < lgamepad.size(); i++) { + if(FAILED(lgamepad[i].handle->Poll())) { + lgamepad[i].handle->Acquire(); + continue; + } + + DIJOYSTATE2 state; + lgamepad[i].handle->GetDeviceState(sizeof(DIJOYSTATE2), &state); + lgamepad[i].poll(state); + } + } + + bool init_joypad(const DIDEVICEINSTANCE *instance) { + //if this is an XInput device, do not acquire it via DirectInput ... + //the XInput driver above will handle said device. + for(unsigned i = 0; i < rawinput.lgamepad.size(); i++) { + uint32_t guid = MAKELONG(rawinput.lgamepad[i].vendorId, rawinput.lgamepad[i].productId); + if(guid == instance->guidProduct.Data1) { + if(rawinput.lgamepad[i].isXInputDevice == true) { + return DIENUM_CONTINUE; + } + } + } + + if(FAILED(context->CreateDevice(instance->guidInstance, &device, 0))) { + return DIENUM_CONTINUE; + } + + device->SetDataFormat(&c_dfDIJoystick2); + device->SetCooperativeLevel(handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + device->EnumObjects(DirectInput_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS); + unsigned n = lgamepad.size(); + lgamepad[n].handle = device; + return DIENUM_CONTINUE; + } + + bool init_axis(const DIDEVICEOBJECTINSTANCE *instance) { + DIPROPRANGE range; + range.diph.dwSize = sizeof(DIPROPRANGE); + range.diph.dwHeaderSize = sizeof(DIPROPHEADER); + range.diph.dwHow = DIPH_BYID; + range.diph.dwObj = instance->dwType; + range.lMin = -32768; + range.lMax = +32767; + device->SetProperty(DIPROP_RANGE, &range.diph); + return DIENUM_CONTINUE; + } + + void init(HWND handle_) { + handle = handle_; + DirectInput8Create(GetModuleHandle(0), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&context, 0); + context->EnumDevices(DI8DEVCLASS_GAMECTRL, DirectInput_EnumJoypadsCallback, (void*)this, DIEDFL_ATTACHEDONLY); + } + + void term() { + for(unsigned i = 0; i < lgamepad.size(); i++) { + lgamepad[i].handle->Unacquire(); + lgamepad[i].handle->Release(); + } + lgamepad.reset(); + + if(context) { + context->Release(); + context = 0; + } + } + +private: + LPDIRECTINPUTDEVICE8 device; +}; + +BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE *instance, void *p) { + return ((DirectInput*)p)->init_joypad(instance); +} + +BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE *instance, void *p) { + return ((DirectInput*)p)->init_axis(instance); +} + +class pInputRaw { +public: + InputRaw &self; + XInput xinput; + DirectInput dinput; + + bool acquire_mouse; + bool cursor_visible; + + struct { + HWND handle; + } settings; + + bool cap(Input::Setting setting) { + if(setting == Input::Handle) return true; + if(setting == Input::KeyboardSupport) return true; + if(setting == Input::MouseSupport) return true; + if(setting == Input::JoypadSupport) return true; + return false; + } + + uintptr_t get(Input::Setting setting) { + if(setting == Input::Handle) return (uintptr_t)settings.handle; + return false; + } + + bool set(Input::Setting setting, uintptr_t param) { + if(setting == Input::Handle) { + settings.handle = (HWND)param; + return true; + } + return false; + } + + bool acquire() { + acquire_mouse = true; + if(cursor_visible == true) { + ShowCursor(cursor_visible = false); + } + return acquired(); + } + + bool unacquire() { + acquire_mouse = false; + ReleaseCapture(); + ClipCursor(NULL); + if(cursor_visible == false) { + ShowCursor(cursor_visible = true); + } + return true; + } + + bool acquired() { + if(acquire_mouse == true) { + SetFocus(settings.handle); + SetCapture(settings.handle); + RECT rc; + GetWindowRect(settings.handle, &rc); + ClipCursor(&rc); + } + return GetCapture() == settings.handle; + } + + bool poll(int16_t *table) { + memset(table, 0, nall::input_limit * sizeof(int16_t)); + + WaitForSingleObject(rawinput.mutex, INFINITE); + + //========= + //Keyboards + //========= + for(unsigned i = 0; i < min(rawinput.lkeyboard.size(), (unsigned)keyboard<>::count); i++) { + unsigned index = keyboard<>::index(i, keyboard<>::none); + + for(unsigned n = 0; n < keyboard<>::length; n++) { + table[index + n] = rawinput.lkeyboard[i].state[n]; + } + } + + //==== + //Mice + //==== + for(unsigned i = 0; i < min(rawinput.lmouse.size(), (unsigned)mouse<>::count); i++) { + unsigned index = mouse<>::index(i, mouse<>::none); + + table[index + mouse<>::x] = rawinput.lmouse[i].xDistance; + table[index + mouse<>::y] = rawinput.lmouse[i].yDistance; + table[index + mouse<>::z] = rawinput.lmouse[i].zDistance; + + for(unsigned n = 0; n < min(5U, (unsigned)mouse<>::buttons); n++) { + table[index + mouse<>::button + n] = (bool)(rawinput.lmouse[i].buttonState & (1 << n)); + } + + rawinput.lmouse[i].sync(); + } + + ReleaseMutex(rawinput.mutex); + + unsigned joy = 0; + + //================== + //XInput controllers + //================== + xinput.poll(); + for(unsigned i = 0; i < xinput.lgamepad.size(); i++) { + if(joy >= joypad<>::count) break; + unsigned index = joypad<>::index(joy++, joypad<>::none); + + table[index + joypad<>::hat + 0] = xinput.lgamepad[i].hat; + + for(unsigned axis = 0; axis < min(6U, (unsigned)joypad<>::axes); axis++) { + table[index + joypad<>::axis + axis] = xinput.lgamepad[i].axis[axis]; + } + + for(unsigned button = 0; button < min(10U, (unsigned)joypad<>::buttons); button++) { + table[index + joypad<>::button + button] = xinput.lgamepad[i].button[button]; + } + } + + //======================= + //DirectInput controllers + //======================= + dinput.poll(); + for(unsigned i = 0; i < dinput.lgamepad.size(); i++) { + if(joy >= joypad<>::count) break; + unsigned index = joypad<>::index(joy++, joypad<>::none); + + for(unsigned hat = 0; hat < min(4U, (unsigned)joypad<>::hats); hat++) { + table[index + joypad<>::hat + hat] = dinput.lgamepad[i].hat[hat]; + } + + for(unsigned axis = 0; axis < min(6U, (unsigned)joypad<>::axes); axis++) { + table[index + joypad<>::axis + axis] = dinput.lgamepad[i].axis[axis]; + } + + for(unsigned button = 0; button < min(128U, (unsigned)joypad<>::buttons); button++) { + table[index + joypad<>::button + button] = dinput.lgamepad[i].button[button]; + } + } + } + + bool init() { + //only spawn RawInput processing thread one time + if(rawinput.initialized == false) { + rawinput.initialized = true; + rawinput.mutex = CreateMutex(NULL, FALSE, NULL); + CreateThread(NULL, 0, RawInputThreadProc, 0, 0, NULL); + + //RawInput device calibration needs to finish before initializing DirectInput; + //as it needs device GUIDs to distinguish XInput devices from ordinary joypads. + bool ready = false; + do { + Sleep(10); + WaitForSingleObject(rawinput.mutex, INFINITE); + ready = rawinput.ready; + ReleaseMutex(rawinput.mutex); + } while(ready == false); + } + + xinput.init(); + dinput.init(settings.handle); + + acquire_mouse = false; + cursor_visible = true; + return true; + } + + void term() { + unacquire(); + dinput.term(); + } + + pInputRaw(InputRaw &self_) : self(self_) { + } +}; + +bool InputRaw::cap(Setting setting) { return p.cap(setting); } +uintptr_t InputRaw::get(Setting setting) { return p.get(setting); } +bool InputRaw::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +bool InputRaw::acquire() { return p.acquire(); } +bool InputRaw::unacquire() { return p.unacquire(); } +bool InputRaw::acquired() { return p.acquired(); } +bool InputRaw::poll(int16_t *table) { return p.poll(table); } +bool InputRaw::init() { return p.init(); } +void InputRaw::term() { p.term(); } +InputRaw::InputRaw() : p(*new pInputRaw(*this)) {} +InputRaw::~InputRaw() { delete &p; } + +} diff --git a/bsnes/lib/ruby/input/rawinput.hpp b/bsnes/lib/ruby/input/rawinput.hpp new file mode 100755 index 0000000..02b7d2d --- /dev/null +++ b/bsnes/lib/ruby/input/rawinput.hpp @@ -0,0 +1,22 @@ +class pInputRaw; + +class InputRaw : public Input { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + bool acquire(); + bool unacquire(); + bool acquired(); + + bool poll(int16_t *table); + bool init(); + void term(); + + InputRaw(); + ~InputRaw(); + +private: + pInputRaw &p; +}; diff --git a/bsnes/lib/ruby/input/sdl.cpp b/bsnes/lib/ruby/input/sdl.cpp new file mode 100755 index 0000000..f5c4607 --- /dev/null +++ b/bsnes/lib/ruby/input/sdl.cpp @@ -0,0 +1,254 @@ +//================ +//SDL input driver +//================ +//Keyboard and mouse are controlled directly via Xlib, +//as SDL cannot capture input from windows it does not create itself. +//SDL is used only to handle joysticks / gamepads. + +#include +#include +#include +#include +#include +#include + +namespace ruby { + +#include "sdl.hpp" + +struct pInputSDL { + #include "xlibkeys.hpp" + InputSDL &self; + + struct { + Display *display; + Window rootwindow; + Cursor InvisibleCursor; + SDL_Joystick *gamepad[joypad<>::count]; + + unsigned screenwidth, screenheight; + unsigned relativex, relativey; + bool mouseacquired; + + //mouse device settings + int accel_numerator; + int accel_denominator; + int threshold; + } device; + + struct { + uintptr_t handle; + } settings; + + bool cap(Input::Setting setting) { + if(setting == Input::Handle) return true; + if(setting == Input::KeyboardSupport) return true; + if(setting == Input::MouseSupport) return true; + if(setting == Input::JoypadSupport) return true; + return false; + } + + uintptr_t get(Input::Setting setting) { + if(setting == Input::Handle) return settings.handle; + return false; + } + + bool set(Input::Setting setting, uintptr_t param) { + if(setting == Input::Handle) { + settings.handle = param; + return true; + } + + return false; + } + + bool acquire() { + if(acquired()) return true; + + if(XGrabPointer(device.display, settings.handle, True, 0, GrabModeAsync, GrabModeAsync, + device.rootwindow, device.InvisibleCursor, CurrentTime) == GrabSuccess) { + //backup existing cursor acceleration settings + XGetPointerControl(device.display, &device.accel_numerator, &device.accel_denominator, &device.threshold); + + //disable cursor acceleration + XChangePointerControl(device.display, True, False, 1, 1, 0); + + //center cursor (so that first relative poll returns 0, 0 if mouse has not moved) + XWarpPointer(device.display, None, device.rootwindow, 0, 0, 0, 0, device.screenwidth / 2, device.screenheight / 2); + + return device.mouseacquired = true; + } else { + return device.mouseacquired = false; + } + } + + bool unacquire() { + if(acquired()) { + //restore cursor acceleration and release cursor + XChangePointerControl(device.display, True, True, device.accel_numerator, device.accel_denominator, device.threshold); + XUngrabPointer(device.display, CurrentTime); + device.mouseacquired = false; + } + return true; + } + + bool acquired() { + return device.mouseacquired; + } + + bool poll(int16_t *table) { + memset(table, 0, nall::input_limit * sizeof(int16_t)); + + //======== + //Keyboard + //======== + + char state[32]; + XQueryKeymap(device.display, state); + + for(unsigned i = 0; i < keyboard<>::length; i++) { + uint8_t code = keycode[i]; + if(code == 0) continue; //unmapped + table[i] = (bool)(state[code >> 3] & (1 << (code & 7))); + } + + //===== + //Mouse + //===== + + Window root_return, child_return; + int root_x_return = 0, root_y_return = 0; + int win_x_return = 0, win_y_return = 0; + unsigned int mask_return = 0; + XQueryPointer(device.display, settings.handle, + &root_return, &child_return, &root_x_return, &root_y_return, + &win_x_return, &win_y_return, &mask_return); + + if(acquired()) { + XWindowAttributes attributes; + XGetWindowAttributes(device.display, settings.handle, &attributes); + + //absolute -> relative conversion + table[mouse<0>::x] = (int16_t)(root_x_return - device.screenwidth / 2); + table[mouse<0>::y] = (int16_t)(root_y_return - device.screenheight / 2); + + if(table[mouse<0>::x] != 0 || table[mouse<0>::y] != 0) { + //if mouse movement occurred, re-center mouse for next poll + XWarpPointer(device.display, None, device.rootwindow, 0, 0, 0, 0, device.screenwidth / 2, device.screenheight / 2); + } + } else { + table[mouse<0>::x] = (int16_t)(root_x_return - device.relativex); + table[mouse<0>::y] = (int16_t)(root_y_return - device.relativey); + + device.relativex = root_x_return; + device.relativey = root_y_return; + } + + //manual device polling is limited to only five buttons ... + table[mouse<0>::button + 0] = (bool)(mask_return & Button1Mask); + table[mouse<0>::button + 1] = (bool)(mask_return & Button2Mask); + table[mouse<0>::button + 2] = (bool)(mask_return & Button3Mask); + table[mouse<0>::button + 3] = (bool)(mask_return & Button4Mask); + table[mouse<0>::button + 4] = (bool)(mask_return & Button5Mask); + + //========= + //Joypad(s) + //========= + + SDL_JoystickUpdate(); + for(unsigned i = 0; i < joypad<>::count; i++) { + if(!device.gamepad[i]) continue; + unsigned index = joypad<>::index(i, joypad<>::none); + + //POV hats + unsigned hats = min((unsigned)joypad<>::hats, SDL_JoystickNumHats(device.gamepad[i])); + for(unsigned hat = 0; hat < hats; hat++) { + uint8_t state = SDL_JoystickGetHat(device.gamepad[i], hat); + if(state & SDL_HAT_UP ) table[index + joypad<>::hat + hat] |= joypad<>::hat_up; + if(state & SDL_HAT_RIGHT) table[index + joypad<>::hat + hat] |= joypad<>::hat_right; + if(state & SDL_HAT_DOWN ) table[index + joypad<>::hat + hat] |= joypad<>::hat_down; + if(state & SDL_HAT_LEFT ) table[index + joypad<>::hat + hat] |= joypad<>::hat_left; + } + + //axes + unsigned axes = min((unsigned)joypad<>::axes, SDL_JoystickNumAxes(device.gamepad[i])); + for(unsigned axis = 0; axis < axes; axis++) { + table[index + joypad<>::axis + axis] = (int16_t)SDL_JoystickGetAxis(device.gamepad[i], axis); + } + + //buttons + for(unsigned button = 0; button < joypad<>::buttons; button++) { + table[index + joypad<>::button + button] = (bool)SDL_JoystickGetButton(device.gamepad[i], button); + } + } + + return true; + } + + bool init() { + init_keycodes(); + SDL_InitSubSystem(SDL_INIT_JOYSTICK); + SDL_JoystickEventState(SDL_IGNORE); + + device.display = XOpenDisplay(0); + device.rootwindow = DefaultRootWindow(device.display); + XWindowAttributes attributes; + XGetWindowAttributes(device.display, device.rootwindow, &attributes); + device.screenwidth = attributes.width; + device.screenheight = attributes.height; + + //Xlib: "because XShowCursor(false) would be too easy." + //create a fully transparent cursor named InvisibleCursor, + //for use while acquire() / XGrabPointer() is active. + Pixmap pixmap; + XColor black, unused; + static char invisible_data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + Colormap colormap = DefaultColormap(device.display, DefaultScreen(device.display)); + XAllocNamedColor(device.display, colormap, "black", &black, &unused); + pixmap = XCreateBitmapFromData(device.display, settings.handle, invisible_data, 8, 8); + device.InvisibleCursor = XCreatePixmapCursor(device.display, pixmap, pixmap, &black, &black, 0, 0); + XFreePixmap(device.display, pixmap); + XFreeColors(device.display, colormap, &black.pixel, 1, 0); + + device.mouseacquired = false; + device.relativex = 0; + device.relativey = 0; + + unsigned joypads = min((unsigned)joypad<>::count, SDL_NumJoysticks()); + for(unsigned i = 0; i < joypads; i++) device.gamepad[i] = SDL_JoystickOpen(i); + + return true; + } + + void term() { + unacquire(); + XFreeCursor(device.display, device.InvisibleCursor); + + for(unsigned i = 0; i < joypad<>::count; i++) { + if(device.gamepad[i]) SDL_JoystickClose(device.gamepad[i]); + device.gamepad[i] = 0; + } + + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + } + + pInputSDL(InputSDL &self_) : self(self_) { + for(unsigned i = 0; i < joypad<>::count; i++) device.gamepad[i] = 0; + settings.handle = 0; + } +}; + + +bool InputSDL::cap(Setting setting) { return p.cap(setting); } +uintptr_t InputSDL::get(Setting setting) { return p.get(setting); } +bool InputSDL::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +bool InputSDL::acquire() { return p.acquire(); } +bool InputSDL::unacquire() { return p.unacquire(); } +bool InputSDL::acquired() { return p.acquired(); } +bool InputSDL::poll(int16_t *table) { return p.poll(table); } +bool InputSDL::init() { return p.init(); } +void InputSDL::term() { p.term(); } +InputSDL::InputSDL() : p(*new pInputSDL(*this)) {} +InputSDL::~InputSDL() { delete &p; } + +} diff --git a/bsnes/lib/ruby/input/sdl.hpp b/bsnes/lib/ruby/input/sdl.hpp new file mode 100755 index 0000000..dfee634 --- /dev/null +++ b/bsnes/lib/ruby/input/sdl.hpp @@ -0,0 +1,22 @@ +class pInputSDL; + +class InputSDL : public Input { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + bool acquire(); + bool unacquire(); + bool acquired(); + + bool poll(int16_t *table); + bool init(); + void term(); + + InputSDL(); + ~InputSDL(); + +private: + pInputSDL &p; +}; diff --git a/bsnes/lib/ruby/input/x.cpp b/bsnes/lib/ruby/input/x.cpp new file mode 100755 index 0000000..db1b38f --- /dev/null +++ b/bsnes/lib/ruby/input/x.cpp @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include + +namespace ruby { + +#include "x.hpp" + +class pInputX { +public: + InputX &self; + Display *display; + #include "xlibkeys.hpp" + + bool cap(Input::Setting setting) { + if(setting == Input::KeyboardSupport) return true; + return false; + } + + uintptr_t get(Input::Setting setting) { + return false; + } + + bool set(Input::Setting setting, uintptr_t param) { + return false; + } + + bool poll(int16_t *table) { + memset(table, 0, input_limit * sizeof(int16_t)); + + char state[32]; + XQueryKeymap(display, state); + + for(unsigned i = 0; i < keyboard<>::length; i++) { + uint8_t code = keycode[i]; + if(code == 0) continue; //unmapped + table[i] = (bool)(state[code >> 3] & (1 << (code & 7))); + } + + return true; + } + + bool init() { + init_keycodes(); + display = XOpenDisplay(0); + return true; + } + + void term() { + } + + pInputX(InputX &self_) : self(self_) {} +}; + +bool InputX::cap(Setting setting) { return p.cap(setting); } +uintptr_t InputX::get(Setting setting) { return p.get(setting); } +bool InputX::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +bool InputX::poll(int16_t *table) { return p.poll(table); } +bool InputX::init() { return p.init(); } +void InputX::term() { p.term(); } +InputX::InputX() : p(*new pInputX(*this)) {} +InputX::~InputX() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/input/x.hpp b/bsnes/lib/ruby/input/x.hpp new file mode 100755 index 0000000..72f9979 --- /dev/null +++ b/bsnes/lib/ruby/input/x.hpp @@ -0,0 +1,18 @@ +class pInputX; + +class InputX : public Input { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + bool poll(int16_t *table); + bool init(); + void term(); + + InputX(); + ~InputX(); + +private: + pInputX &p; +}; diff --git a/bsnes/lib/ruby/input/xlibkeys.hpp b/bsnes/lib/ruby/input/xlibkeys.hpp new file mode 100755 index 0000000..bbce472 --- /dev/null +++ b/bsnes/lib/ruby/input/xlibkeys.hpp @@ -0,0 +1,138 @@ +//shared keycode lookup table + initialization routine: +//#include inside a class interface to use + +//Xlib keycodes for each key can vary between platforms, so this header file +//will lookup keycodes from static keysyms, and map them to nall/input.hpp's +//keyboard identifiers. +// +//this allows input capture routine to iterate quickly over all keycodes and +//map their states to ruby's input state table. + +uint8_t keycode[256]; + +bool init_keycodes() { + Display *display = XOpenDisplay(0); + memset(&keycode, 0, sizeof keycode); + + #define assign(x, y) keycode[x] = XKeysymToKeycode(display, y) + assign(keyboard<0>::escape, XK_Escape); + + assign(keyboard<0>::f1, XK_F1); + assign(keyboard<0>::f2, XK_F2); + assign(keyboard<0>::f3, XK_F3); + assign(keyboard<0>::f4, XK_F4); + assign(keyboard<0>::f5, XK_F5); + assign(keyboard<0>::f6, XK_F6); + assign(keyboard<0>::f7, XK_F7); + assign(keyboard<0>::f8, XK_F8); + assign(keyboard<0>::f9, XK_F9); + assign(keyboard<0>::f10, XK_F10); + assign(keyboard<0>::f11, XK_F11); + assign(keyboard<0>::f12, XK_F12); + + //assign(keyboard<0>::print_screen, XK_???); + assign(keyboard<0>::scroll_lock, XK_Scroll_Lock); + assign(keyboard<0>::pause, XK_Pause); + + assign(keyboard<0>::tilde, XK_asciitilde); + + assign(keyboard<0>::num_0, XK_0); + assign(keyboard<0>::num_1, XK_1); + assign(keyboard<0>::num_2, XK_2); + assign(keyboard<0>::num_3, XK_3); + assign(keyboard<0>::num_4, XK_4); + assign(keyboard<0>::num_5, XK_5); + assign(keyboard<0>::num_6, XK_6); + assign(keyboard<0>::num_7, XK_7); + assign(keyboard<0>::num_8, XK_8); + assign(keyboard<0>::num_9, XK_9); + + assign(keyboard<0>::dash, XK_minus); + assign(keyboard<0>::equal, XK_equal); + assign(keyboard<0>::backspace, XK_BackSpace); + + assign(keyboard<0>::insert, XK_Insert); + assign(keyboard<0>::delete_, XK_Delete); + assign(keyboard<0>::home, XK_Home); + assign(keyboard<0>::end, XK_End); + assign(keyboard<0>::page_up, XK_Prior); + assign(keyboard<0>::page_down, XK_Next); + + assign(keyboard<0>::a, XK_A); + assign(keyboard<0>::b, XK_B); + assign(keyboard<0>::c, XK_C); + assign(keyboard<0>::d, XK_D); + assign(keyboard<0>::e, XK_E); + assign(keyboard<0>::f, XK_F); + assign(keyboard<0>::g, XK_G); + assign(keyboard<0>::h, XK_H); + assign(keyboard<0>::i, XK_I); + assign(keyboard<0>::j, XK_J); + assign(keyboard<0>::k, XK_K); + assign(keyboard<0>::l, XK_L); + assign(keyboard<0>::m, XK_M); + assign(keyboard<0>::n, XK_N); + assign(keyboard<0>::o, XK_O); + assign(keyboard<0>::p, XK_P); + assign(keyboard<0>::q, XK_Q); + assign(keyboard<0>::r, XK_R); + assign(keyboard<0>::s, XK_S); + assign(keyboard<0>::t, XK_T); + assign(keyboard<0>::u, XK_U); + assign(keyboard<0>::v, XK_V); + assign(keyboard<0>::w, XK_W); + assign(keyboard<0>::x, XK_X); + assign(keyboard<0>::y, XK_Y); + assign(keyboard<0>::z, XK_Z); + + assign(keyboard<0>::lbracket, XK_bracketleft); + assign(keyboard<0>::rbracket, XK_bracketright); + assign(keyboard<0>::backslash, XK_backslash); + assign(keyboard<0>::semicolon, XK_semicolon); + assign(keyboard<0>::apostrophe, XK_apostrophe); + assign(keyboard<0>::comma, XK_comma); + assign(keyboard<0>::period, XK_period); + assign(keyboard<0>::slash, XK_slash); + + assign(keyboard<0>::pad_0, XK_KP_0); + assign(keyboard<0>::pad_1, XK_KP_1); + assign(keyboard<0>::pad_2, XK_KP_2); + assign(keyboard<0>::pad_3, XK_KP_3); + assign(keyboard<0>::pad_4, XK_KP_4); + assign(keyboard<0>::pad_5, XK_KP_5); + assign(keyboard<0>::pad_6, XK_KP_6); + assign(keyboard<0>::pad_7, XK_KP_7); + assign(keyboard<0>::pad_8, XK_KP_8); + assign(keyboard<0>::pad_9, XK_KP_9); + + assign(keyboard<0>::add, XK_KP_Add); + assign(keyboard<0>::subtract, XK_KP_Subtract); + assign(keyboard<0>::multiply, XK_KP_Multiply); + assign(keyboard<0>::divide, XK_KP_Divide); + assign(keyboard<0>::enter, XK_KP_Enter); + + //assign(keyboard<0>::num_lock, XK_???); + //assign(keyboard<0>::caps_lock, XK_???); + + assign(keyboard<0>::up, XK_Up); + assign(keyboard<0>::down, XK_Down); + assign(keyboard<0>::left, XK_Left); + assign(keyboard<0>::right, XK_Right); + + assign(keyboard<0>::tab, XK_Tab); + assign(keyboard<0>::return_, XK_Return); + assign(keyboard<0>::spacebar, XK_space); + + assign(keyboard<0>::lctrl, XK_Control_L); + assign(keyboard<0>::rctrl, XK_Control_R); + assign(keyboard<0>::lalt, XK_Alt_L); + assign(keyboard<0>::ralt, XK_Alt_R); + assign(keyboard<0>::lshift, XK_Shift_L); + assign(keyboard<0>::rshift, XK_Shift_R); + assign(keyboard<0>::lsuper, XK_Super_L); + assign(keyboard<0>::rsuper, XK_Super_R); + assign(keyboard<0>::menu, XK_Menu); + #undef assign + + XCloseDisplay(display); +} diff --git a/bsnes/lib/ruby/ruby.cpp b/bsnes/lib/ruby/ruby.cpp new file mode 100755 index 0000000..ee844a5 --- /dev/null +++ b/bsnes/lib/ruby/ruby.cpp @@ -0,0 +1,317 @@ +#include +using namespace nall; + +#include + +namespace ruby { + +VideoInterface video; +AudioInterface audio; +InputInterface input; + +/* VideoInterface */ + +void VideoInterface::driver(const char *driver) { + if(p) term(); + + if(!driver || !*driver) driver = default_driver(); + + if(0); + + #ifdef VIDEO_DIRECT3D + else if(!strcmp(driver, "Direct3D")) p = new VideoD3D(); + #endif + + #ifdef VIDEO_DIRECTDRAW + else if(!strcmp(driver, "DirectDraw")) p = new VideoDD(); + #endif + + #ifdef VIDEO_GDI + else if(!strcmp(driver, "GDI")) p = new VideoGDI(); + #endif + + #ifdef VIDEO_GLX + else if(!strcmp(driver, "OpenGL")) p = new VideoGLX(); + #endif + + #ifdef VIDEO_SDL + else if(!strcmp(driver, "SDL")) p = new VideoSDL(); + #endif + + #ifdef VIDEO_WGL + else if(!strcmp(driver, "OpenGL")) p = new VideoWGL(); + #endif + + #ifdef VIDEO_XV + else if(!strcmp(driver, "X-Video")) p = new VideoXv(); + #endif + + else p = new Video(); +} + +//select the *safest* available driver, not the fastest +const char* VideoInterface::default_driver() { + #if defined(VIDEO_DIRECT3D) + return "Direct3D"; + #elif defined(VIDEO_WGL) + return "OpenGL"; + #elif defined(VIDEO_DIRECTDRAW) + return "DirectDraw"; + #elif defined(VIDEO_GDI) + return "GDI"; + #elif defined(VIDEO_SDL) + return "SDL"; + #elif defined(VIDEO_XV) + return "X-Video"; + #elif defined(VIDEO_GLX) + return "OpenGL"; + #else + return "None"; + #endif +} + +//returns list of available drivers, sorted from most to least optimal +const char* VideoInterface::driver_list() { + return + + //Windows + + #if defined(VIDEO_DIRECT3D) + "Direct3D;" + #endif + + #if defined(VIDEO_WGL) + "OpenGL;" + #endif + + #if defined(VIDEO_DIRECTDRAW) + "DirectDraw;" + #endif + + #if defined(VIDEO_GDI) + "GDI;" + #endif + + //Linux + + #if defined(VIDEO_GLX) + "OpenGL;" + #endif + + #if defined(VIDEO_XV) + "X-Video;" + #endif + + #if defined(VIDEO_SDL) + "SDL;" + #endif + + "None"; +} + +bool VideoInterface::init() { + if(!p) driver(); + return p->init(); +} + +void VideoInterface::term() { + if(p) { + delete p; + p = 0; + } +} + +bool VideoInterface::cap(Video::Setting setting) { return p ? p->cap(setting) : false; } +uintptr_t VideoInterface::get(Video::Setting setting) { return p ? p->get(setting) : false; } +bool VideoInterface::set(Video::Setting setting, uintptr_t param) { return p ? p->set(setting, param) : false; } +bool VideoInterface::lock(uint32_t *&data, unsigned &pitch) { return p ? p->lock(data, pitch) : false; } +void VideoInterface::unlock() { if(p) p->unlock(); } +void VideoInterface::clear() { if(p) p->clear(); } +void VideoInterface::refresh(unsigned width, unsigned height) { if(p) p->refresh(width, height); } +VideoInterface::VideoInterface() : p(0) {} +VideoInterface::~VideoInterface() { term(); } + +/* AudioInterface */ + +void AudioInterface::driver(const char *driver) { + if(p) term(); + + if(!driver || !*driver) driver = default_driver(); + + if(0); + + #ifdef AUDIO_ALSA + else if(!strcmp(driver, "ALSA")) p = new AudioALSA(); + #endif + + #ifdef AUDIO_AO + else if(!strcmp(driver, "libao")) p = new AudioAO(); + #endif + + #ifdef AUDIO_DIRECTSOUND + else if(!strcmp(driver, "DirectSound")) p = new AudioDS(); + #endif + + #ifdef AUDIO_OPENAL + else if(!strcmp(driver, "OpenAL")) p = new AudioOpenAL(); + #endif + + #ifdef AUDIO_OSS + else if(!strcmp(driver, "OSS")) p = new AudioOSS(); + #endif + + #ifdef AUDIO_PULSEAUDIO + else if(!strcmp(driver, "PulseAudio")) p = new AudioPulseAudio(); + #endif + + else p = new Audio(); +} + +//select the *safest* available driver, not the fastest +const char* AudioInterface::default_driver() { + #if defined(AUDIO_DIRECTSOUND) + return "DirectSound"; + #elif defined(AUDIO_ALSA) + return "ALSA"; + #elif defined(AUDIO_OPENAL) + return "OpenAL"; + #elif defined(AUDIO_PULSEAUDIO) + return "PulseAudio"; + #elif defined(AUDIO_AO) + return "libao"; + #elif defined(AUDIO_OSS) + return "OSS"; + #else + return "None"; + #endif +} + +//returns list of available drivers, sorted from most to least optimal +const char* AudioInterface::driver_list() { + return + + //Windows + + #if defined(AUDIO_DIRECTSOUND) + "DirectSound;" + #endif + + //Linux + + #if defined(AUDIO_ALSA) + "ALSA;" + #endif + + #if defined(AUDIO_OPENAL) + "OpenAL;" + #endif + + #if defined(AUDIO_OSS) + "OSS;" + #endif + + #if defined(AUDIO_PULSEAUDIO) + "PulseAudio;" + #endif + + #if defined(AUDIO_AO) + "libao;" + #endif + + "None"; +} + +#include "ruby_audio.cpp" + +/* InputInterface */ + +void InputInterface::driver(const char *driver) { + if(p) term(); + + if(!driver || !*driver) driver = default_driver(); + + if(0); + + #ifdef INPUT_DIRECTINPUT + else if(!strcmp(driver, "DirectInput")) p = new InputDI(); + #endif + + #ifdef INPUT_RAWINPUT + else if(!strcmp(driver, "RawInput")) p = new InputRaw(); + #endif + + #ifdef INPUT_SDL + else if(!strcmp(driver, "SDL")) p = new InputSDL(); + #endif + + #ifdef INPUT_X + else if(!strcmp(driver, "X-Windows")) p = new InputX(); + #endif + + else p = new Input(); +} + +//select the *safest* available driver, not the fastest +const char* InputInterface::default_driver() { + #if defined(INPUT_RAWINPUT) + return "RawInput"; + #elif defined(INPUT_DIRECTINPUT) + return "DirectInput"; + #elif defined(INPUT_SDL) + return "SDL"; + #elif defined(INPUT_X) + return "X-Windows"; + #else + return "none"; + #endif +} + +const char* InputInterface::driver_list() { + return + + //Windows + + #if defined(INPUT_RAWINPUT) + "RawInput;" + #endif + + #if defined(INPUT_DIRECTINPUT) + "DirectInput;" + #endif + + //Linux + + #if defined(INPUT_SDL) + "SDL;" + #endif + + #if defined(INPUT_X) + "X-Windows;" + #endif + + "None"; +} + +bool InputInterface::init() { + if(!p) driver(); + return p->init(); +} + +void InputInterface::term() { + if(p) { + delete p; + p = 0; + } +} + +bool InputInterface::cap(Input::Setting setting) { return p ? p->cap(setting) : false; } +uintptr_t InputInterface::get(Input::Setting setting) { return p ? p->get(setting) : false; } +bool InputInterface::set(Input::Setting setting, uintptr_t param) { return p ? p->set(setting, param) : false; } +bool InputInterface::acquire() { return p ? p->acquire() : false; } +bool InputInterface::unacquire() { return p ? p->unacquire() : false; } +bool InputInterface::acquired() { return p ? p->acquired() : false; } +bool InputInterface::poll(int16_t *table) { return p ? p->poll(table) : false; } +InputInterface::InputInterface() : p(0) {} +InputInterface::~InputInterface() { term(); } + +} //namespace ruby diff --git a/bsnes/lib/ruby/ruby.hpp b/bsnes/lib/ruby/ruby.hpp new file mode 100755 index 0000000..1f0cbc4 --- /dev/null +++ b/bsnes/lib/ruby/ruby.hpp @@ -0,0 +1,106 @@ +/* + ruby + version: 0.05 (2009-03-21) + license: public domain +*/ + +#ifndef RUBY_H +#define RUBY_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ruby { + +#include +#include +#include + +class VideoInterface { +public: + void driver(const char *driver = ""); + const char* default_driver(); + const char* driver_list(); + bool init(); + void term(); + + bool cap(Video::Setting setting); + uintptr_t get(Video::Setting setting); + bool set(Video::Setting setting, uintptr_t param); + bool lock(uint32_t *&data, unsigned &pitch); + void unlock(); + void clear(); + void refresh(unsigned width, unsigned height); + VideoInterface(); + ~VideoInterface(); + +private: + Video *p; +}; + +class AudioInterface { +public: + void driver(const char *driver = ""); + const char* default_driver(); + const char* driver_list(); + bool init(); + void term(); + + bool cap(Audio::Setting setting); + uintptr_t get(Audio::Setting setting); + bool set(Audio::Setting setting, uintptr_t param); + void sample(uint16_t left, uint16_t right); + void clear(); + AudioInterface(); + ~AudioInterface(); + +private: + Audio *p; + + unsigned volume; + + //resample unit + double hermite(double mu, double a, double b, double c, double d); + bool resample_enabled; + double r_outfreq, r_infreq; + double r_step, r_frac; + int r_left[4], r_right[4]; +}; + +class InputInterface { +public: + void driver(const char *driver = ""); + const char* default_driver(); + const char* driver_list(); + bool init(); + void term(); + + bool cap(Input::Setting setting); + uintptr_t get(Input::Setting setting); + bool set(Input::Setting setting, uintptr_t param); + + bool acquire(); + bool unacquire(); + bool acquired(); + + bool poll(int16_t *table); + InputInterface(); + ~InputInterface(); + +private: + Input *p; +}; + +extern VideoInterface video; +extern AudioInterface audio; +extern InputInterface input; + +} //namespace ruby + +#endif //ifndef RUBY_H diff --git a/bsnes/lib/ruby/ruby_audio.cpp b/bsnes/lib/ruby/ruby_audio.cpp new file mode 100755 index 0000000..6f52401 --- /dev/null +++ b/bsnes/lib/ruby/ruby_audio.cpp @@ -0,0 +1,135 @@ +bool AudioInterface::init() { + if(!p) driver(); + return p->init(); +} + +void AudioInterface::term() { + if(p) { + delete p; + p = 0; + } +} + +bool AudioInterface::cap(Audio::Setting setting) { + if(setting == Audio::Volume) return true; + if(setting == Audio::Resample) return true; + if(setting == Audio::ResampleOutputFrequency) return true; + if(setting == Audio::ResampleInputFrequency) return true; + + return p ? p->cap(setting) : false; +} + +uintptr_t AudioInterface::get(Audio::Setting setting) { + if(setting == Audio::Volume) return volume; + if(setting == Audio::Resample) return resample_enabled; + if(setting == Audio::ResampleOutputFrequency) return r_outfreq; + if(setting == Audio::ResampleInputFrequency) return r_infreq; + + return p ? p->get(setting) : false; +} + +bool AudioInterface::set(Audio::Setting setting, uintptr_t param) { + if(setting == Audio::Volume) { + volume = param; + return true; + } + + if(setting == Audio::Resample) { + resample_enabled = param; + return true; + } + + if(setting == Audio::ResampleOutputFrequency) { + r_outfreq = max(1, param); + r_step = (double)r_infreq / (double)r_outfreq; + r_frac = 0; + return true; + } + + if(setting == Audio::ResampleInputFrequency) { + r_infreq = max(1, param); + r_step = (double)r_infreq / (double)r_outfreq; + r_frac = 0; + return true; + } + + return p ? p->set(setting, param) : false; +} + +//4-tap hermite interpolation +double AudioInterface::hermite(double mu1, double a, double b, double c, double d) { + const double tension = 0.0; //-1 = low, 0 = normal, 1 = high + const double bias = 0.0; //-1 = left, 0 = even, 1 = right + + double mu2, mu3, m0, m1, a0, a1, a2, a3; + + mu2 = mu1 * mu1; + mu3 = mu2 * mu1; + + m0 = (b - a) * (1 + bias) * (1 - tension) / 2; + m0 += (c - b) * (1 - bias) * (1 - tension) / 2; + m1 = (c - b) * (1 + bias) * (1 - tension) / 2; + m1 += (d - c) * (1 - bias) * (1 - tension) / 2; + + a0 = +2 * mu3 - 3 * mu2 + 1; + a1 = mu3 - 2 * mu2 + mu1; + a2 = mu3 - mu2; + a3 = -2 * mu3 + 3 * mu2; + + return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c); +} + +void AudioInterface::sample(uint16_t left, uint16_t right) { + int s_left = (int16_t)left; + int s_right = (int16_t)right; + + if(volume != 100) { + s_left = sclamp<16>((double)s_left * (double)volume / 100.0); + s_right = sclamp<16>((double)s_right * (double)volume / 100.0); + } + + r_left [0] = r_left [1]; + r_left [1] = r_left [2]; + r_left [2] = r_left [3]; + r_left [3] = s_left; + + r_right[0] = r_right[1]; + r_right[1] = r_right[2]; + r_right[2] = r_right[3]; + r_right[3] = s_right; + + if(resample_enabled == false) { + if(p) p->sample(left, right); + return; + } + + while(r_frac <= 1.0) { + int output_left = sclamp<16>(hermite(r_frac, r_left [0], r_left [1], r_left [2], r_left [3])); + int output_right = sclamp<16>(hermite(r_frac, r_right[0], r_right[1], r_right[2], r_right[3])); + r_frac += r_step; + if(p) p->sample(output_left, output_right); + } + + r_frac -= 1.0; +} + +void AudioInterface::clear() { + r_frac = 0; + r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0; + r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0; + if(p) p->clear(); +} + +AudioInterface::AudioInterface() { + p = 0; + volume = 100; + resample_enabled = false; + r_outfreq = r_infreq = 1; + r_step = r_frac = 0; + r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0; + r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0; +} + +AudioInterface::~AudioInterface() { + term(); +} diff --git a/bsnes/lib/ruby/ruby_impl.cpp b/bsnes/lib/ruby/ruby_impl.cpp new file mode 100755 index 0000000..86d8fe4 --- /dev/null +++ b/bsnes/lib/ruby/ruby_impl.cpp @@ -0,0 +1,78 @@ +/* Video */ + +#ifdef _WIN32 + #define _WIN32_WINNT 0x0501 + #include +#endif + +#ifdef VIDEO_DIRECT3D + #include +#endif + +#ifdef VIDEO_DIRECTDRAW + #include +#endif + +#ifdef VIDEO_GDI + #include +#endif + +#ifdef VIDEO_GLX + #include +#endif + +#ifdef VIDEO_SDL + #include +#endif + +#ifdef VIDEO_WGL + #include +#endif + +#ifdef VIDEO_XV + #include +#endif + +/* Audio */ + +#ifdef AUDIO_ALSA + #include +#endif + +#ifdef AUDIO_AO + #include +#endif + +#ifdef AUDIO_DIRECTSOUND + #include +#endif + +#ifdef AUDIO_OPENAL + #include +#endif + +#ifdef AUDIO_OSS + #include +#endif + +#ifdef AUDIO_PULSEAUDIO + #include +#endif + +/* Input */ + +#ifdef INPUT_DIRECTINPUT + #include +#endif + +#ifdef INPUT_RAWINPUT + #include +#endif + +#ifdef INPUT_SDL + #include +#endif + +#ifdef INPUT_X + #include +#endif diff --git a/bsnes/lib/ruby/video.hpp b/bsnes/lib/ruby/video.hpp new file mode 100755 index 0000000..c23a54b --- /dev/null +++ b/bsnes/lib/ruby/video.hpp @@ -0,0 +1,28 @@ +class Video { +public: + enum Setting { + Handle, + Synchronize, + Filter, + }; + + enum Filter { + FilterPoint, + FilterLinear, + }; + + virtual bool cap(Setting) { return false; } + virtual uintptr_t get(Setting) { return false; } + virtual bool set(Setting, uintptr_t) { return false; } + + virtual bool lock(uint32_t *&data, unsigned &pitch) { return false; } + virtual void unlock() {} + + virtual void clear() {} + virtual void refresh(unsigned width, unsigned height) {} + virtual bool init() { return true; } + virtual void term() {} + + Video() {} + virtual ~Video() {} +}; diff --git a/bsnes/lib/ruby/video/direct3d.cpp b/bsnes/lib/ruby/video/direct3d.cpp new file mode 100755 index 0000000..56a7385 --- /dev/null +++ b/bsnes/lib/ruby/video/direct3d.cpp @@ -0,0 +1,346 @@ +#include +#include + +#define D3DVERTEX (D3DFVF_XYZRHW | D3DFVF_TEX1) + +namespace ruby { + +#include "direct3d.hpp" + +class pVideoD3D { +public: + VideoD3D &self; + + LPDIRECT3D9 lpd3d; + LPDIRECT3DDEVICE9 device; + LPDIRECT3DVERTEXBUFFER9 vertex_buffer, *vertex_ptr; + D3DPRESENT_PARAMETERS presentation; + D3DSURFACE_DESC d3dsd; + D3DLOCKED_RECT d3dlr; + D3DRASTER_STATUS d3drs; + D3DCAPS9 d3dcaps; + LPDIRECT3DTEXTURE9 texture; + LPDIRECT3DSURFACE9 surface; + + struct d3dvertex { + float x, y, z, rhw; //screen coords + float u, v; //texture coords + }; + + struct { + uint32_t t_usage, v_usage; + uint32_t t_pool, v_pool; + uint32_t lock; + uint32_t filter; + } flags; + + struct { + bool dynamic; //device supports dynamic textures + bool stretchrect; //device supports StretchRect + } caps; + + struct { + HWND handle; + bool synchronize; + unsigned filter; + } settings; + + struct { + unsigned width; + unsigned height; + } state; + + bool cap(Video::Setting setting) { + if(setting == Video::Handle) return true; + if(setting == Video::Synchronize) return true; + if(setting == Video::Filter) return true; + return false; + } + + uintptr_t get(Video::Setting setting) { + if(setting == Video::Handle) return (uintptr_t)settings.handle; + if(setting == Video::Synchronize) return settings.synchronize; + if(setting == Video::Filter) return settings.filter; + return false; + } + + bool set(Video::Setting setting, uintptr_t param) { + if(setting == Video::Handle) { + settings.handle = (HWND)param; + return true; + } + + if(setting == Video::Synchronize) { + settings.synchronize = param; + return true; + } + + if(setting == Video::Filter) { + settings.filter = param; + if(lpd3d) update_filter(); + return true; + } + + return false; + } + + void update_filter() { + if(!device) return; + + switch(settings.filter) { default: + case Video::FilterPoint: flags.filter = D3DTEXF_POINT; break; + case Video::FilterLinear: flags.filter = D3DTEXF_LINEAR; break; + } + + device->SetSamplerState(0, D3DSAMP_MINFILTER, flags.filter); + device->SetSamplerState(0, D3DSAMP_MAGFILTER, flags.filter); + } + + /* Vertex format: + + 0----------1 + | /| + | / | + | / | + | / | + | / | + 2----------3 + + (x,y) screen coords, in pixels + (u,v) texture coords, betweeen 0.0 (top, left) to 1.0 (bottom, right) + */ + void set_vertex( + uint32_t px, uint32_t py, uint32_t pw, uint32_t ph, + uint32_t tw, uint32_t th, + uint32_t x, uint32_t y, uint32_t w, uint32_t h + ) { + d3dvertex vertex[4]; + vertex[0].x = vertex[2].x = (double)(x ); + vertex[1].x = vertex[3].x = (double)(x + w); + vertex[0].y = vertex[1].y = (double)(y ); + vertex[2].y = vertex[3].y = (double)(y + h); + + //Z-buffer and RHW are unused for 2D blit, set to normal values + vertex[0].z = vertex[1].z = vertex[2].z = vertex[3].z = 0.0; + vertex[0].rhw = vertex[1].rhw = vertex[2].rhw = vertex[3].rhw = 1.0; + + double rw = (double)w / (double)pw * (double)tw; + double rh = (double)h / (double)ph * (double)th; + vertex[0].u = vertex[2].u = (double)(px ) / rw; + vertex[1].u = vertex[3].u = (double)(px + w) / rw; + vertex[0].v = vertex[1].v = (double)(py ) / rh; + vertex[2].v = vertex[3].v = (double)(py + h) / rh; + + vertex_buffer->Lock(0, sizeof(d3dvertex) * 4, (void**)&vertex_ptr, 0); + memcpy(vertex_ptr, vertex, sizeof(d3dvertex) * 4); + vertex_buffer->Unlock(); + + device->SetStreamSource(0, vertex_buffer, 0, sizeof(d3dvertex)); + } + + void clear() { + if(!device) return; + if(caps.stretchrect == false && !texture) return; + + if(caps.stretchrect == false) { + texture->GetLevelDesc(0, &d3dsd); + texture->GetSurfaceLevel(0, &surface); + } + + if(surface) { + device->ColorFill(surface, 0, D3DCOLOR_XRGB(0x00, 0x00, 0x00)); + if(caps.stretchrect == false) { + surface->Release(); + } + } + + //clear primary display and all backbuffers + for(int i = 0; i < 3; i++) { + device->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0x00, 0x00, 0x00), 1.0f, 0); + device->Present(0, 0, 0, 0); + } + } + + bool lock(uint32_t *&data, unsigned &pitch) { + if(caps.stretchrect == false) { + texture->GetLevelDesc(0, &d3dsd); + texture->GetSurfaceLevel(0, &surface); + } + surface->LockRect(&d3dlr, 0, flags.lock); + pitch = d3dlr.Pitch; + return data = (uint32_t*)d3dlr.pBits; + } + + void unlock() { + surface->UnlockRect(); + if(caps.stretchrect == false) surface->Release(); + } + + void refresh(unsigned width, unsigned height) { + if(!device) return; + + RECT rd, rs; //dest, source rectangles + GetClientRect(settings.handle, &rd); + SetRect(&rs, 0, 0, width, height); + + if(state.width != rd.right || state.height != rd.bottom) { + //if window size changed, D3DPRESENT_PARAMETERS must be updated + //failure to do so causes scaling issues on some ATI drivers + init(); + } + + device->BeginScene(); + + if(caps.stretchrect == true) { + LPDIRECT3DSURFACE9 temp; + device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &temp); + device->StretchRect(surface, &rs, temp, 0, static_cast(flags.filter)); + temp->Release(); + } else { + set_vertex(0, 0, width, height, 1024, 1024, 0, 0, rd.right, rd.bottom); + device->SetTexture(0, texture); + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + } + + device->EndScene(); + + if(settings.synchronize) { + while(true) { + D3DRASTER_STATUS status; + device->GetRasterStatus(0, &status); + if(status.InVBlank == true) break; + } + } + + device->Present(0, 0, 0, 0); + } + + bool init() { + term(); + + RECT rd; + GetClientRect(settings.handle, &rd); + state.width = rd.right; + state.height = rd.bottom; + + lpd3d = Direct3DCreate9(D3D_SDK_VERSION); + if(!lpd3d) return false; + + memset(&presentation, 0, sizeof(presentation)); + presentation.Flags = D3DPRESENTFLAG_VIDEO; + presentation.SwapEffect = D3DSWAPEFFECT_FLIP; + presentation.hDeviceWindow = settings.handle; + presentation.BackBufferCount = 1; + presentation.MultiSampleType = D3DMULTISAMPLE_NONE; + presentation.MultiSampleQuality = 0; + presentation.EnableAutoDepthStencil = false; + presentation.AutoDepthStencilFormat = D3DFMT_UNKNOWN; + presentation.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + presentation.Windowed = true; + presentation.BackBufferFormat = D3DFMT_UNKNOWN; + presentation.BackBufferWidth = 0; + presentation.BackBufferHeight = 0; + + if(lpd3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, settings.handle, + D3DCREATE_SOFTWARE_VERTEXPROCESSING, &presentation, &device) != D3D_OK) { + return false; + } + + //detect device capabilities + device->GetDeviceCaps(&d3dcaps); + if(d3dcaps.MaxTextureWidth < 1024 || d3dcaps.MaxTextureWidth < 1024) return false; + + caps.dynamic = bool(d3dcaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES); + caps.stretchrect = (d3dcaps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES) && + (d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT) && + (d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT) && + (d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) && + (d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR); + + if(caps.dynamic == true) { + flags.t_usage = D3DUSAGE_DYNAMIC; + flags.v_usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC; + flags.t_pool = D3DPOOL_DEFAULT; + flags.v_pool = D3DPOOL_DEFAULT; + flags.lock = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD; + } else { + flags.t_usage = 0; + flags.v_usage = D3DUSAGE_WRITEONLY; + flags.t_pool = D3DPOOL_MANAGED; + flags.v_pool = D3DPOOL_MANAGED; + flags.lock = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD; + } + + device->SetDialogBoxMode(false); + + device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); + device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + + device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + + device->SetRenderState(D3DRS_LIGHTING, false); + device->SetRenderState(D3DRS_ZENABLE, false); + device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + + device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + device->SetRenderState(D3DRS_ALPHABLENDENABLE, false); + + device->SetVertexShader(NULL); + device->SetFVF(D3DVERTEX); + + if(caps.stretchrect == true) { + device->CreateOffscreenPlainSurface(1024, 1024, D3DFMT_X8R8G8B8, + D3DPOOL_DEFAULT, &surface, NULL); + } else { + device->CreateTexture(1024, 1024, 1, flags.t_usage, D3DFMT_X8R8G8B8, + static_cast(flags.t_pool), &texture, NULL); + } + + device->CreateVertexBuffer(sizeof(d3dvertex) * 4, flags.v_usage, D3DVERTEX, + static_cast(flags.v_pool), &vertex_buffer, NULL); + + update_filter(); + clear(); + return true; + } + + void term() { + if(vertex_buffer) { vertex_buffer->Release(); vertex_buffer = 0; } + if(surface) { surface->Release(); surface = 0; } + if(texture) { texture->Release(); texture = 0; } + if(device) { device->Release(); device = 0; } + if(lpd3d) { lpd3d->Release(); lpd3d = 0; } + } + + pVideoD3D(VideoD3D &self_) : self(self_) { + vertex_buffer = 0; + surface = 0; + texture = 0; + device = 0; + lpd3d = 0; + + settings.handle = 0; + settings.synchronize = false; + settings.filter = Video::FilterLinear; + } +}; + +bool VideoD3D::cap(Setting setting) { return p.cap(setting); } +uintptr_t VideoD3D::get(Setting setting) { return p.get(setting); } +bool VideoD3D::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +bool VideoD3D::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +void VideoD3D::unlock() { p.unlock(); } +void VideoD3D::clear() { p.clear(); } +void VideoD3D::refresh(unsigned width, unsigned height) { p.refresh(width, height); } +bool VideoD3D::init() { return p.init(); } +void VideoD3D::term() { p.term(); } +VideoD3D::VideoD3D() : p(*new pVideoD3D(*this)) {} +VideoD3D::~VideoD3D() { delete &p; } + +} //namespace ruby + +#undef D3DVERTEX diff --git a/bsnes/lib/ruby/video/direct3d.hpp b/bsnes/lib/ruby/video/direct3d.hpp new file mode 100755 index 0000000..9f2fd06 --- /dev/null +++ b/bsnes/lib/ruby/video/direct3d.hpp @@ -0,0 +1,22 @@ +class pVideoD3D; + +class VideoD3D : public Video { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + bool lock(uint32_t *&data, unsigned &pitch); + void unlock(); + + void clear(); + void refresh(unsigned width, unsigned height); + bool init(); + void term(); + + VideoD3D(); + ~VideoD3D(); + +private: + pVideoD3D &p; +}; diff --git a/bsnes/lib/ruby/video/directdraw.cpp b/bsnes/lib/ruby/video/directdraw.cpp new file mode 100755 index 0000000..0092e13 --- /dev/null +++ b/bsnes/lib/ruby/video/directdraw.cpp @@ -0,0 +1,178 @@ +#include +#include + +namespace ruby { + +#include "directdraw.hpp" + +class pVideoDD { +public: + VideoDD &self; + + LPDIRECTDRAW lpdd; + LPDIRECTDRAW7 lpdd7; + LPDIRECTDRAWSURFACE7 screen, raster; + LPDIRECTDRAWCLIPPER clipper; + DDSURFACEDESC2 ddsd; + DDSCAPS2 ddscaps; + + struct { + HWND handle; + bool synchronize; + } settings; + + bool cap(Video::Setting setting) { + if(setting == Video::Handle) return true; + if(setting == Video::Synchronize) return true; + return false; + } + + uintptr_t get(Video::Setting setting) { + if(setting == Video::Handle) return (uintptr_t)settings.handle; + if(setting == Video::Synchronize) return settings.synchronize; + return false; + } + + bool set(Video::Setting setting, uintptr_t param) { + if(setting == Video::Handle) { + settings.handle = (HWND)param; + return true; + } + if(setting == Video::Synchronize) { + settings.synchronize = param; + return true; + } + return false; + } + + void clear() { + DDBLTFX fx; + fx.dwSize = sizeof(DDBLTFX); + fx.dwFillColor = 0x00000000; + screen->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); + raster->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); + } + + bool lock(uint32_t *&data, unsigned &pitch) { + if(raster->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK) return false; + pitch = ddsd.lPitch; + return data = (uint32_t*)ddsd.lpSurface; + } + + void unlock() { + raster->Unlock(0); + } + + void refresh(unsigned r_width, unsigned r_height) { + if(settings.synchronize) { + while(true) { + BOOL in_vblank; + lpdd7->GetVerticalBlankStatus(&in_vblank); + if(in_vblank == true) break; + } + } + + HRESULT hr; + RECT rd, rs; + SetRect(&rs, 0, 0, r_width, r_height); + + POINT p = { 0, 0 }; + ClientToScreen(settings.handle, &p); + GetClientRect(settings.handle, &rd); + OffsetRect(&rd, p.x, p.y); + + if(screen->Blt(&rd, raster, &rs, DDBLT_WAIT, 0) == DDERR_SURFACELOST) { + screen->Restore(); + raster->Restore(); + } + } + + bool init() { + term(); + + DirectDrawCreate(0, &lpdd, 0); + lpdd->QueryInterface(IID_IDirectDraw7, (void**)&lpdd7); + if(lpdd) { lpdd->Release(); lpdd = 0; } + + lpdd7->SetCooperativeLevel(settings.handle, DDSCL_NORMAL); + + memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + lpdd7->CreateSurface(&ddsd, &screen, 0); + + lpdd7->CreateClipper(0, &clipper, 0); + clipper->SetHWnd(0, settings.handle); + screen->SetClipper(clipper); + + create_raster(); + clear(); + return true; + } + + void create_raster() { + screen->GetSurfaceDesc(&ddsd); + int depth = ddsd.ddpfPixelFormat.dwRGBBitCount; + if(depth == 32) goto try_native_surface; + + memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY + ddsd.dwWidth = 1024; + ddsd.dwHeight = 1024; + + ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; + ddsd.ddpfPixelFormat.dwRGBBitCount = 32; + ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000; + ddsd.ddpfPixelFormat.dwGBitMask = 0x00ff00; + ddsd.ddpfPixelFormat.dwBBitMask = 0x0000ff; + + if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return; + + try_native_surface: + memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY + ddsd.dwWidth = 1024; + ddsd.dwHeight = 1024; + + if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return; + } + + void term() { + if(clipper) { clipper->Release(); clipper = 0; } + if(raster) { raster->Release(); raster = 0; } + if(screen) { screen->Release(); screen = 0; } + if(lpdd7) { lpdd7->Release(); lpdd7 = 0; } + if(lpdd) { lpdd->Release(); lpdd = 0; } + } + + pVideoDD(VideoDD &self_) : self(self_) { + lpdd = 0; + lpdd7 = 0; + screen = 0; + raster = 0; + clipper = 0; + + settings.handle = 0; + } +}; + +bool VideoDD::cap(Setting setting) { return p.cap(setting); } +uintptr_t VideoDD::get(Setting setting) { return p.get(setting); } +bool VideoDD::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +bool VideoDD::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +void VideoDD::unlock() { p.unlock(); } +void VideoDD::clear() { p.clear(); } +void VideoDD::refresh(unsigned width, unsigned height) { p.refresh(width, height); } +bool VideoDD::init() { return p.init(); } +void VideoDD::term() { p.term(); } +VideoDD::VideoDD() : p(*new pVideoDD(*this)) {} +VideoDD::~VideoDD() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/video/directdraw.hpp b/bsnes/lib/ruby/video/directdraw.hpp new file mode 100755 index 0000000..e4e8eb9 --- /dev/null +++ b/bsnes/lib/ruby/video/directdraw.hpp @@ -0,0 +1,22 @@ +class pVideoDD; + +class VideoDD : public Video { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + bool lock(uint32_t *&data, unsigned &pitch); + void unlock(); + + void clear(); + void refresh(unsigned width, unsigned height); + bool init(); + void term(); + + VideoDD(); + ~VideoDD(); + +private: + pVideoDD &p; +}; diff --git a/bsnes/lib/ruby/video/gdi.cpp b/bsnes/lib/ruby/video/gdi.cpp new file mode 100755 index 0000000..41bce7b --- /dev/null +++ b/bsnes/lib/ruby/video/gdi.cpp @@ -0,0 +1,103 @@ +#include +#include + +namespace ruby { + +#include "gdi.hpp" + +class pVideoGDI { +public: + VideoGDI &self; + + uint32_t *buffer; + HBITMAP bitmap; + HDC bitmapdc; + BITMAPINFO bmi; + + struct { + HWND handle; + } settings; + + bool cap(Video::Setting setting) { + if(setting == Video::Handle) return true; + return false; + } + + uintptr_t get(Video::Setting setting) { + if(setting == Video::Handle) return (uintptr_t)settings.handle; + return false; + } + + bool set(Video::Setting setting, uintptr_t param) { + if(setting == Video::Handle) { + settings.handle = (HWND)param; + return true; + } + return false; + } + + bool lock(uint32_t *&data, unsigned &pitch) { + pitch = 1024 * 4; + return data = buffer; + } + + void unlock() {} + + void refresh(unsigned r_width, unsigned r_height) { + RECT rc; + GetClientRect(settings.handle, &rc); + + SetDIBits(bitmapdc, bitmap, 0, r_height, (void*)buffer, &bmi, DIB_RGB_COLORS); + HDC hdc = GetDC(settings.handle); + StretchBlt(hdc, rc.left, rc.top, rc.right, rc.bottom, bitmapdc, 0, 1024 - r_height, r_width, r_height, SRCCOPY); + ReleaseDC(settings.handle, hdc); + } + + bool init() { + HDC hdc = GetDC(settings.handle); + bitmapdc = CreateCompatibleDC(hdc); + assert(bitmapdc); + bitmap = CreateCompatibleBitmap(hdc, 1024, 1024); + assert(bitmap); + SelectObject(bitmapdc, bitmap); + ReleaseDC(settings.handle, hdc); + + memset(&bmi, 0, sizeof(BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = 1024; + bmi.bmiHeader.biHeight = -1024; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; //biBitCount of 15 is invalid, biBitCount of 16 is really RGB555 + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = 1024 * 1024 * sizeof(uint32_t); + + return true; + } + + void term() { + DeleteObject(bitmap); + DeleteDC(bitmapdc); + } + + pVideoGDI(VideoGDI &self_) : self(self_) { + buffer = (uint32_t*)malloc(1024 * 1024 * sizeof(uint32_t)); + settings.handle = 0; + } + + ~pVideoGDI() { + if(buffer) free(buffer); + } +}; + +bool VideoGDI::cap(Setting setting) { return p.cap(setting); } +uintptr_t VideoGDI::get(Setting setting) { return p.get(setting); } +bool VideoGDI::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +bool VideoGDI::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +void VideoGDI::unlock() { p.unlock(); } +void VideoGDI::refresh(unsigned width, unsigned height) { p.refresh(width, height); } +bool VideoGDI::init() { return p.init(); } +void VideoGDI::term() { p.term(); } +VideoGDI::VideoGDI() : p(*new pVideoGDI(*this)) {} +VideoGDI::~VideoGDI() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/video/gdi.hpp b/bsnes/lib/ruby/video/gdi.hpp new file mode 100755 index 0000000..e701f70 --- /dev/null +++ b/bsnes/lib/ruby/video/gdi.hpp @@ -0,0 +1,21 @@ +class pVideoGDI; + +class VideoGDI : public Video { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + bool lock(uint32_t *&data, unsigned &pitch); + void unlock(); + + void refresh(unsigned width, unsigned height); + bool init(); + void term(); + + VideoGDI(); + ~VideoGDI(); + +private: + pVideoGDI &p; +}; diff --git a/bsnes/lib/ruby/video/glx.cpp b/bsnes/lib/ruby/video/glx.cpp new file mode 100755 index 0000000..909390c --- /dev/null +++ b/bsnes/lib/ruby/video/glx.cpp @@ -0,0 +1,292 @@ +/* + video.glx + author: byuu + license: public domain + last updated: 2008-08-20 + + Design notes: + SGI's GLX is the X11/Xlib interface to OpenGL. + At the time of this writing, there are three relevant versions of the API: versions 1.2, 1.3 and 1.4. + + Version 1.2 was released on March 4th, 1997. + Version 1.3 was released on October 19th, 1998. + Version 1.4 was released on December 16th, 2005. + + Despite version 1.3 being roughly ten years old at this time, there are still many modern X11 GLX drivers + that lack full support for the specification. Most notable would be the official video drivers from ATI. + Given this, 1.4 support is pretty much hopeless to target. + + Luckily, each version has been designed to be backwards compatible with the previous version. As well, + version 1.2 is wholly sufficient, albeit less convenient, to implement this video module. + + Therefore, for the purpose of compatibility, this driver only uses GLX 1.2 or earlier API commands. + As well, it only uses raw Xlib API commands, so that it is compatible with any toolkit. +*/ + +#include +#include + +namespace ruby { + +#include "glx.hpp" + +//returns true once window is mapped (created and displayed onscreen) +static Bool glx_wait_for_map_notify(Display *d, XEvent *e, char *arg) { + return (e->type == MapNotify) && (e->xmap.window == (Window)arg); +} + +class pVideoGLX { +public: + VideoGLX &self; + uint32_t *buffer; + + Display *display; + int screen; + Window xwindow; + Colormap colormap; + GLXContext glxcontext; + GLXWindow glxwindow; + GLuint gltexture; + + struct { + int version_major, version_minor; + bool double_buffer; + bool is_direct; + } glx; + + struct { + Window handle; + bool synchronize; + unsigned filter; + } settings; + + bool cap(Video::Setting setting) { + if(setting == Video::Handle) return true; + if(setting == Video::Synchronize) return true; + if(setting == Video::Filter) return true; + return false; + } + + uintptr_t get(Video::Setting setting) { + if(setting == Video::Handle) return settings.handle; + if(setting == Video::Synchronize) return settings.synchronize; + if(setting == Video::Filter) return settings.filter; + return false; + } + + bool set(Video::Setting setting, uintptr_t param) { + if(setting == Video::Handle) { + settings.handle = param; + return true; + } + + if(setting == Video::Synchronize) { + if(settings.synchronize != param) { + settings.synchronize = param; + if(glxcontext) { + term(); + init(); + } + return true; + } + } + + if(setting == Video::Filter) { + settings.filter = param; + return true; + } + + return false; + } + + bool lock(uint32_t *&data, unsigned &pitch) { + pitch = 1024 * 4; + return data = buffer; + } + + void unlock() { + } + + void clear() { + memset(buffer, 0, 1024 * 1024 * sizeof(uint32_t)); + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + glFlush(); + if(glx.double_buffer) glXSwapBuffers(display, glxwindow); + } + + void refresh(unsigned width, unsigned height) { + //we must ensure that the child window is the same size as the parent window. + //unfortunately, we cannot hook the parent window resize event notification, + //as we did not create the parent window, nor have any knowledge of the toolkit used. + //therefore, inelegant as it may be, we query each window size and resize as needed. + XWindowAttributes parent, child; + XGetWindowAttributes(display, settings.handle, &parent); + XGetWindowAttributes(display, xwindow, &child); + if(child.width != parent.width || child.height != parent.height) { + XResizeWindow(display, xwindow, parent.width, parent.height); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + settings.filter == Video::FilterPoint ? GL_NEAREST : GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + settings.filter == Video::FilterPoint ? GL_NEAREST : GL_LINEAR); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, parent.width, 0, parent.height, -1.0, 1.0); + glViewport(0, 0, parent.width, parent.height); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 1024); //length of buffer in pixels + glTexSubImage2D(GL_TEXTURE_2D, + /* mip-map level = */ 0, /* x = */ 0, /* y = */ 0, + width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer); + + //OpenGL projection sets 0,0 as *bottom-left* of screen. + //therefore, below vertices flip image to support top-left source. + //texture range = x1:0.0, y1:0.0, x2:1.0, y2:1.0 + //vertex range = x1:0, y1:0, x2:width, y2:height + double w = double(width) / 1024.0; + double h = double(height) / 1024.0; + int u = parent.width; + int v = parent.height; + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0, 0); glVertex3i(0, v, 0); + glTexCoord2f(w, 0); glVertex3i(u, v, 0); + glTexCoord2f(0, h); glVertex3i(0, 0, 0); + glTexCoord2f(w, h); glVertex3i(u, 0, 0); + glEnd(); + + glFlush(); + if(glx.double_buffer) glXSwapBuffers(display, glxwindow); + } + + bool init() { + display = XOpenDisplay(0); + screen = DefaultScreen(display); + glXQueryVersion(display, &glx.version_major, &glx.version_minor); + //require GLX 1.2+ API + if(glx.version_major < 1 || (glx.version_major == 1 && glx.version_minor < 2)) return false; + + buffer = new(zeromemory) uint32_t[1024 * 1024]; + + XWindowAttributes window_attributes; + XGetWindowAttributes(display, settings.handle, &window_attributes); + + //let GLX determine the best Visual to use for GL output; provide a few hints + //note: some video drivers will override double buffering attribute + int elements = 0; + int attributelist[] = { GLX_RGBA, None }; + int attributelist_sync[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None }; + XVisualInfo *vi = glXChooseVisual(display, screen, + settings.synchronize ? attributelist_sync : attributelist); + + //Window settings.handle has already been realized, most likely with DefaultVisual. + //GLX requires that the GL output window has the same Visual as the GLX context. + //it is not possible to change the Visual of an already realized (created) window. + //therefore a new child window, using the same GLX Visual, must be created and binded to settings.handle. + colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone); + XSetWindowAttributes attributes; + attributes.colormap = colormap; + attributes.border_pixel = 0; + attributes.event_mask = StructureNotifyMask; + xwindow = XCreateWindow(display, /* parent = */ settings.handle, + /* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height, + /* border_width = */ 0, vi->depth, InputOutput, vi->visual, + CWColormap | CWBorderPixel | CWEventMask, &attributes); + XSetWindowBackground(display, xwindow, /* color = */ 0); + XMapWindow(display, xwindow); + XEvent event; + //window must be realized (appear onscreen) before we make the context current + XIfEvent(display, &event, glx_wait_for_map_notify, (char*)xwindow); + + glxcontext = glXCreateContext(display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE); + glXMakeCurrent(display, glxwindow = xwindow, glxcontext); + + //read attributes of frame buffer for later use, as requested attributes from above are not always granted + int value = 0; + glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &value); + glx.double_buffer = value; + glx.is_direct = glXIsDirect(display, glxcontext); + + //disable unused features + glDisable(GL_ALPHA_TEST); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_POLYGON_SMOOTH); + glDisable(GL_STENCIL_TEST); + + //enable useful and required features + glEnable(GL_DITHER); + glEnable(GL_TEXTURE_2D); + + //create GL texture to copy buffer to + gltexture = 0; + glGenTextures(1, &gltexture); + glBindTexture(GL_TEXTURE_2D, gltexture); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 1024); + glTexImage2D(GL_TEXTURE_2D, + /* mip-map level = */ 0, /* internal format = */ GL_RGB, + /* width = */ 1024, /* height = */ 1024, /* border = */ 0, + /* format = */ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer); + + return true; + } + + void term() { + if(gltexture) { + glDeleteTextures(1, &gltexture); + gltexture = 0; + } + + if(glxcontext) { + glXDestroyContext(display, glxcontext); + glxcontext = 0; + } + + if(xwindow) { + XUnmapWindow(display, xwindow); + xwindow = 0; + } + + if(colormap) { + XFreeColormap(display, colormap); + colormap = 0; + } + + if(buffer) { + delete[] buffer; + buffer = 0; + } + } + + pVideoGLX(VideoGLX &self_) : self(self_) { + settings.handle = 0; + settings.synchronize = false; + xwindow = 0; + colormap = 0; + glxcontext = 0; + glxwindow = 0; + gltexture = 0; + } + + ~pVideoGLX() { term(); } +}; + +bool VideoGLX::cap(Setting setting) { return p.cap(setting); } +uintptr_t VideoGLX::get(Setting setting) { return p.get(setting); } +bool VideoGLX::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +bool VideoGLX::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +void VideoGLX::unlock() { p.unlock(); } +void VideoGLX::clear() { p.clear(); } +void VideoGLX::refresh(unsigned width, unsigned height) { p.refresh(width, height); } +bool VideoGLX::init() { return p.init(); } +void VideoGLX::term() { p.term(); } +VideoGLX::VideoGLX() : p(*new pVideoGLX(*this)) {} +VideoGLX::~VideoGLX() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/video/glx.hpp b/bsnes/lib/ruby/video/glx.hpp new file mode 100755 index 0000000..f80b20f --- /dev/null +++ b/bsnes/lib/ruby/video/glx.hpp @@ -0,0 +1,22 @@ +class pVideoGLX; + +class VideoGLX : public Video { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + bool lock(uint32_t *&data, unsigned &pitch); + void unlock(); + + void clear(); + void refresh(unsigned width, unsigned height); + bool init(); + void term(); + + VideoGLX(); + ~VideoGLX(); + +private: + pVideoGLX &p; +}; diff --git a/bsnes/lib/ruby/video/sdl.cpp b/bsnes/lib/ruby/video/sdl.cpp new file mode 100755 index 0000000..3d80515 --- /dev/null +++ b/bsnes/lib/ruby/video/sdl.cpp @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ruby { + +#include "sdl.hpp" + +class pVideoSDL { +public: + VideoSDL &self; + Display *display; + SDL_Surface *screen, *buffer; + + struct { + uintptr_t handle; + } settings; + + bool cap(Video::Setting setting) { + if(setting == Video::Handle) return true; + return false; + } + + uintptr_t get(Video::Setting setting) { + if(setting == Video::Handle) return settings.handle; + return false; + } + + bool set(Video::Setting setting, uintptr_t param) { + if(setting == Video::Handle) { + settings.handle = param; + return true; + } + return false; + } + + bool lock(uint32_t *&data, unsigned &pitch) { + if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer); + pitch = buffer->pitch; + return data = (uint32_t*)buffer->pixels; + } + + void unlock() { + if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer); + } + + void clear() { + if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer); + uint32_t *data = (uint32_t*)buffer->pixels; + for(unsigned y = 0; y < 1024; y++) { + for(unsigned x = 0; x < 1024; x++) { + *data++ |= 0xff000000; + } + data += (buffer->pitch >> 2) - 1024; + } + if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer); + refresh(1024, 1024); + } + + void refresh(unsigned width, unsigned height) { + //ruby input is X8R8G8B8, top 8-bits are ignored. + //as SDL forces us to use a 32-bit buffer, we must set alpha to 255 (full opacity) + //to prevent blending against the window beneath when X window visual is 32-bits. + if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer); + uint32_t *data = (uint32_t*)buffer->pixels; + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width; x++) { + *data++ |= 0xff000000; + } + data += (buffer->pitch >> 2) - width; + } + if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer); + + XWindowAttributes attributes; + XGetWindowAttributes(display, settings.handle, &attributes); + + SDL_Rect src, dest; + + src.x = 0; + src.y = 0; + src.w = width; + src.h = height; + + dest.x = 0; + dest.y = 0; + dest.w = attributes.width; + dest.h = attributes.height; + + SDL_SoftStretch(buffer, &src, screen, &dest); + SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h); + } + + bool init() { + display = XOpenDisplay(0); + + char env[512]; + sprintf(env, "SDL_WINDOWID=%ld", settings.handle); + putenv(env); + + SDL_InitSubSystem(SDL_INIT_VIDEO); + //screen depth must be 32, as 24bpp with a 32-bit X window visual produces no output. + screen = SDL_SetVideoMode(2560, 1600, 32, SDL_HWSURFACE); + //buffer depth must be 32, as this is the input format used by all ruby drivers. + buffer = SDL_CreateRGBSurface(SDL_HWSURFACE, + 1024, 1024, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 + ); + return true; + } + + void term() { + SDL_QuitSubSystem(SDL_INIT_VIDEO); + } + + pVideoSDL(VideoSDL &self_) : self(self_) { + settings.handle = 0; + } +}; + +bool VideoSDL::cap(Setting setting) { return p.cap(setting); } +uintptr_t VideoSDL::get(Setting setting) { return p.get(setting); } +bool VideoSDL::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +bool VideoSDL::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +void VideoSDL::unlock() { p.unlock(); } +void VideoSDL::clear() { p.clear(); } +void VideoSDL::refresh(unsigned width, unsigned height) { p.refresh(width, height); } +bool VideoSDL::init() { return p.init(); } +void VideoSDL::term() { p.term(); } +VideoSDL::VideoSDL() : p(*new pVideoSDL(*this)) {} +VideoSDL::~VideoSDL() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/video/sdl.hpp b/bsnes/lib/ruby/video/sdl.hpp new file mode 100755 index 0000000..5dcf350 --- /dev/null +++ b/bsnes/lib/ruby/video/sdl.hpp @@ -0,0 +1,22 @@ +class pVideoSDL; + +class VideoSDL : public Video { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + bool lock(uint32_t *&data, unsigned &pitch); + void unlock(); + + void clear(); + void refresh(unsigned width, unsigned height); + bool init(); + void term(); + + VideoSDL(); + ~VideoSDL(); + +private: + pVideoSDL &p; +}; diff --git a/bsnes/lib/ruby/video/wgl.cpp b/bsnes/lib/ruby/video/wgl.cpp new file mode 100755 index 0000000..3719675 --- /dev/null +++ b/bsnes/lib/ruby/video/wgl.cpp @@ -0,0 +1,214 @@ +/* + video.wgl + authors: byuu, krom + license: public domain + last updated: 2008-08-20 +*/ + +#include +#include +#include + +namespace ruby { + +#include "wgl.hpp" + +class pVideoWGL { +public: + VideoWGL &self; + uint32_t *buffer; + + HDC display; + HGLRC wglcontext; + HWND window; + HINSTANCE glwindow; + GLuint gltexture; + + struct { + HWND handle; + bool synchronize; + unsigned filter; + } settings; + + bool cap(Video::Setting setting) { + if(setting == Video::Handle) return true; + if(setting == Video::Synchronize) return true; + if(setting == Video::Filter) return true; + return false; + } + + uintptr_t get(Video::Setting setting) { + if(setting == Video::Handle) return (uintptr_t)settings.handle; + if(setting == Video::Synchronize) return settings.synchronize; + if(setting == Video::Filter) return settings.filter; + return false; + } + + bool set(Video::Setting setting, uintptr_t param) { + if(setting == Video::Handle) { + settings.handle = (HWND)param; + return true; + } + + if(setting == Video::Synchronize) { + if(settings.synchronize != param) { + settings.synchronize = param; + if(wglcontext) { + term(); + init(); + } + } + } + + if(setting == Video::Filter) { + settings.filter = param; + return true; + } + + return false; + } + + bool lock(uint32_t *&data, unsigned &pitch) { + pitch = 1024 * 4; + return data = buffer; + } + + void unlock() { + } + + void clear() { + memset(buffer, 0, 1024 * 1024 * sizeof(uint32_t)); + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + glFlush(); + SwapBuffers(display); + } + + void refresh(unsigned width, unsigned height) { + RECT rc; + GetClientRect(settings.handle, &rc); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + settings.filter == Video::FilterPoint ? GL_NEAREST : GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + settings.filter == Video::FilterPoint ? GL_NEAREST : GL_LINEAR); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, 0, height, -1.0, 1.0); + glViewport(0, 0, rc.right, rc.bottom); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 1024); //length of buffer in pixels + glTexSubImage2D(GL_TEXTURE_2D, + /* mip-map level = */ 0, /* x = */ 0, /* y = */ 0, + width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer); + + //OpenGL projection sets 0,0 as *bottom-left* of screen. + //therefore, below vertices flip image to support top-left source. + //texture range = x1:0.0, y1:0.0, x2:1.0, y2:1.0 + //vertex range = x1:0, y1:0, x2:width, y2:height + double w = double(width) / 1024.0; + double h = double(height) / 1024.0; + int u = width; + int v = height; + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0, 0); glVertex3i(0, v, 0); + glTexCoord2f(w, 0); glVertex3i(u, v, 0); + glTexCoord2f(0, h); glVertex3i(0, 0, 0); + glTexCoord2f(w, h); glVertex3i(u, 0, 0); + glEnd(); + + glFlush(); + SwapBuffers(display); + } + + bool init() { + buffer = new(zeromemory) uint32_t[1024 * 1024]; + + GLuint pixel_format; + PIXELFORMATDESCRIPTOR pfd; + memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | (settings.synchronize ? PFD_DOUBLEBUFFER : 0); + pfd.iPixelType = PFD_TYPE_RGBA; + + display = GetDC(settings.handle); + pixel_format = ChoosePixelFormat(display, &pfd); + SetPixelFormat(display, pixel_format, &pfd); + + wglcontext = wglCreateContext(display); + wglMakeCurrent(display, wglcontext); + + //disable unused features + glDisable(GL_ALPHA_TEST); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_POLYGON_SMOOTH); + glDisable(GL_STENCIL_TEST); + + //enable useful and required features + glEnable(GL_DITHER); + glEnable(GL_TEXTURE_2D); + + //create GL texture to copy buffer to + gltexture = 0; + glGenTextures(1, &gltexture); + glBindTexture(GL_TEXTURE_2D, gltexture); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 1024); + glTexImage2D(GL_TEXTURE_2D, + /* mip-map level = */ 0, /* internal format = */ GL_RGB, + /* width = */ 1024, /* height = */ 1024, /* border = */ 0, + /* format = */ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer); + + return true; + } + + void term() { + if(gltexture) { + glDeleteTextures(1, &gltexture); + gltexture = 0; + } + + if(wglcontext) { + wglDeleteContext(wglcontext); + wglcontext = 0; + } + + if(buffer) { + delete[] buffer; + buffer = 0; + } + } + + pVideoWGL(VideoWGL &self_) : self(self_) { + settings.handle = 0; + settings.synchronize = false; + settings.filter = 0; + + window = 0; + wglcontext = 0; + glwindow = 0; + gltexture = 0; + } + + ~pVideoWGL() { term(); } +}; + +bool VideoWGL::cap(Setting setting) { return p.cap(setting); } +uintptr_t VideoWGL::get(Setting setting) { return p.get(setting); } +bool VideoWGL::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +bool VideoWGL::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +void VideoWGL::unlock() { p.unlock(); } +void VideoWGL::clear() { p.clear(); } +void VideoWGL::refresh(unsigned width, unsigned height) { p.refresh(width, height); } +bool VideoWGL::init() { return p.init(); } +void VideoWGL::term() { p.term(); } +VideoWGL::VideoWGL() : p(*new pVideoWGL(*this)) {} +VideoWGL::~VideoWGL() { delete &p; } + +} //namespace ruby diff --git a/bsnes/lib/ruby/video/wgl.hpp b/bsnes/lib/ruby/video/wgl.hpp new file mode 100755 index 0000000..c3e8c4e --- /dev/null +++ b/bsnes/lib/ruby/video/wgl.hpp @@ -0,0 +1,22 @@ +class pVideoWGL; + +class VideoWGL : public Video { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + bool lock(uint32_t *&data, unsigned &pitch); + void unlock(); + + void clear(); + void refresh(unsigned width, unsigned height); + bool init(); + void term(); + + VideoWGL(); + ~VideoWGL(); + +private: + pVideoWGL &p; +}; diff --git a/bsnes/lib/ruby/video/xv.cpp b/bsnes/lib/ruby/video/xv.cpp new file mode 100755 index 0000000..62614fe --- /dev/null +++ b/bsnes/lib/ruby/video/xv.cpp @@ -0,0 +1,469 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" XvImage* XvShmCreateImage(Display*, XvPortID, int, char*, int, int, XShmSegmentInfo*); + +namespace ruby { + +#include "xv.hpp" + +class pVideoXv { +public: + VideoXv &self; + + uint32_t *buffer; + uint8_t *ytable, *utable, *vtable; + + enum XvFormat { + XvFormatRGB32, + XvFormatRGB24, + XvFormatRGB16, + XvFormatRGB15, + XvFormatYUY2, + XvFormatUYVY, + XvFormatUnknown + }; + + struct { + Display *display; + GC gc; + Window window; + Colormap colormap; + XShmSegmentInfo shminfo; + + int port; + int depth; + int visualid; + + XvImage *image; + XvFormat format; + uint32_t fourcc; + } device; + + struct { + Window handle; + bool synchronize; + } settings; + + bool cap(Video::Setting setting) { + if(setting == Video::Handle) return true; + if(setting == Video::Synchronize) { + return XInternAtom(XOpenDisplay(0), "XV_SYNC_TO_VBLANK", true) != None; + } + return false; + } + + uintptr_t get(Video::Setting setting) { + if(setting == Video::Handle) return settings.handle; + if(setting == Video::Synchronize) return settings.synchronize; + return false; + } + + bool set(Video::Setting setting, uintptr_t param) { + if(setting == Video::Handle) { + settings.handle = param; + return true; + } + + if(setting == Video::Synchronize) { + Display *display = XOpenDisplay(0); + Atom atom = XInternAtom(display, "XV_SYNC_TO_VBLANK", true); + if(atom != None && device.port >= 0) { + settings.synchronize = param; + XvSetPortAttribute(display, device.port, atom, settings.synchronize); + return true; + } + return false; + } + + return false; + } + + bool lock(uint32_t *&data, unsigned &pitch) { + pitch = 1024 * 4; + return data = buffer; + } + + void unlock() { + } + + void clear() { + memset(buffer, 0, 1024 * 1024 * sizeof(uint32_t)); + //clear twice in case video is double buffered ... + refresh(1024, 1024); + refresh(1024, 1024); + } + + void refresh(unsigned width, unsigned height) { + XWindowAttributes target; + XGetWindowAttributes(device.display, device.window, &target); + + //we must ensure that the child window is the same size as the parent window. + //unfortunately, we cannot hook the parent window resize event notification, + //as we did not create the parent window, nor have any knowledge of the toolkit used. + //therefore, query each window size and resize as needed. + XWindowAttributes parent; + XGetWindowAttributes(device.display, settings.handle, &parent); + if(target.width != parent.width || target.height != parent.height) { + XResizeWindow(device.display, device.window, parent.width, parent.height); + } + + //update target width and height attributes + XGetWindowAttributes(device.display, device.window, &target); + + switch(device.format) { + case XvFormatRGB32: render_rgb32(width, height); break; + case XvFormatRGB24: render_rgb24(width, height); break; + case XvFormatRGB16: render_rgb16(width, height); break; + case XvFormatRGB15: render_rgb15(width, height); break; + case XvFormatYUY2: render_yuy2 (width, height); break; + case XvFormatUYVY: render_uyvy (width, height); break; + } + + XvShmPutImage(device.display, device.port, device.window, device.gc, device.image, + 0, 0, width, height, + 0, 0, target.width, target.height, + true); + } + + bool init() { + device.display = XOpenDisplay(0); + + if(!XShmQueryExtension(device.display)) { + fprintf(stderr, "VideoXv: XShm extension not found.\n"); + return false; + } + + //find an appropriate Xv port + device.port = -1; + XvAdaptorInfo *adaptor_info; + unsigned adaptor_count; + XvQueryAdaptors(device.display, DefaultRootWindow(device.display), &adaptor_count, &adaptor_info); + for(unsigned i = 0; i < adaptor_count; i++) { + //find adaptor that supports both input (memory->drawable) and image (drawable->screen) masks + if(adaptor_info[i].num_formats < 1) continue; + if(!(adaptor_info[i].type & XvInputMask)) continue; + if(!(adaptor_info[i].type & XvImageMask)) continue; + + device.port = adaptor_info[i].base_id; + device.depth = adaptor_info[i].formats->depth; + device.visualid = adaptor_info[i].formats->visual_id; + break; + } + XvFreeAdaptorInfo(adaptor_info); + if(device.port < 0) { + fprintf(stderr, "VideoXv: failed to find valid XvPort.\n"); + return false; + } + + //create child window to attach to parent window. + //this is so that even if parent window visual depth doesn't match Xv visual + //(common with composited windows), Xv can still render to child window. + XWindowAttributes window_attributes; + XGetWindowAttributes(device.display, settings.handle, &window_attributes); + + XVisualInfo visualtemplate; + visualtemplate.visualid = device.visualid; + visualtemplate.screen = DefaultScreen(device.display); + visualtemplate.depth = device.depth; + visualtemplate.visual = 0; + int visualmatches = 0; + XVisualInfo *visualinfo = XGetVisualInfo(device.display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches); + if(visualmatches < 1 || !visualinfo->visual) { + if(visualinfo) XFree(visualinfo); + fprintf(stderr, "VideoXv: unable to find Xv-compatible visual.\n"); + return false; + } + + device.colormap = XCreateColormap(device.display, settings.handle, visualinfo->visual, AllocNone); + XSetWindowAttributes attributes; + attributes.colormap = device.colormap; + attributes.border_pixel = 0; + attributes.event_mask = StructureNotifyMask; + device.window = XCreateWindow(device.display, /* parent = */ settings.handle, + /* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height, + /* border_width = */ 0, device.depth, InputOutput, visualinfo->visual, + CWColormap | CWBorderPixel | CWEventMask, &attributes); + XFree(visualinfo); + XSetWindowBackground(device.display, device.window, /* color = */ 0); + XMapWindow(device.display, device.window); + + device.gc = XCreateGC(device.display, device.window, 0, 0); + + //set colorkey to auto paint, so that Xv video output is always visible + Atom atom = XInternAtom(device.display, "XV_AUTOPAINT_COLORKEY", true); + if(atom != None) XvSetPortAttribute(device.display, device.port, atom, 1); + + //find optimal rendering format + device.format = XvFormatUnknown; + signed format_count; + XvImageFormatValues *format = XvListImageFormats(device.display, device.port, &format_count); + + if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) { + if(format[i].type == XvRGB && format[i].bits_per_pixel == 32) { + device.format = XvFormatRGB32; + device.fourcc = format[i].id; + break; + } + } + + if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) { + if(format[i].type == XvRGB && format[i].bits_per_pixel == 24) { + device.format = XvFormatRGB24; + device.fourcc = format[i].id; + break; + } + } + + if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) { + if(format[i].type == XvRGB && format[i].bits_per_pixel == 16) { + device.format = XvFormatRGB16; + device.fourcc = format[i].id; + break; + } + } + + if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) { + if(format[i].type == XvRGB && format[i].bits_per_pixel == 15) { + device.format = XvFormatRGB15; + device.fourcc = format[i].id; + break; + } + } + + if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) { + if(format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked) { + if(format[i].component_order[0] == 'Y' && format[i].component_order[1] == 'U' + && format[i].component_order[2] == 'Y' && format[i].component_order[3] == 'V' + ) { + device.format = XvFormatYUY2; + device.fourcc = format[i].id; + break; + } + } + } + + if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) { + if(format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked) { + if(format[i].component_order[0] == 'U' && format[i].component_order[1] == 'Y' + && format[i].component_order[2] == 'V' && format[i].component_order[3] == 'Y' + ) { + device.format = XvFormatUYVY; + device.fourcc = format[i].id; + break; + } + } + } + + free(format); + if(device.format == XvFormatUnknown) { + fprintf(stderr, "VideoXv: unable to find a supported image format.\n"); + return false; + } + + device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, 1024, 1024, &device.shminfo); + if(!device.image) { + fprintf(stderr, "VideoXv: XShmCreateImage failed.\n"); + return false; + } + + device.shminfo.shmid = shmget(IPC_PRIVATE, device.image->data_size, IPC_CREAT | 0777); + device.shminfo.shmaddr = device.image->data = (char*)shmat(device.shminfo.shmid, 0, 0); + device.shminfo.readOnly = false; + if(!XShmAttach(device.display, &device.shminfo)) { + fprintf(stderr, "VideoXv: XShmAttach failed.\n"); + return false; + } + + buffer = new uint32_t[1024 * 1024]; + init_yuv_tables(); + clear(); + return true; + } + + void term() { + XShmDetach(device.display, &device.shminfo); + + if(device.window) { + XUnmapWindow(device.display, device.window); + device.window = 0; + } + + if(device.colormap) { + XFreeColormap(device.display, device.colormap); + device.colormap = 0; + } + + if(buffer) { delete[] buffer; buffer = 0; } + if(ytable) { delete[] ytable; ytable = 0; } + if(utable) { delete[] utable; utable = 0; } + if(vtable) { delete[] vtable; vtable = 0; } + } + + void render_rgb32(unsigned width, unsigned height) { + uint32_t *input = (uint32_t*)buffer; + uint32_t *output = (uint32_t*)device.image->data; + + for(unsigned y = 0; y < height; y++) { + memcpy(output, input, width * 4); + input += 1024 - width; + output += 1024 - width; + } + } + + void render_rgb24(unsigned width, unsigned height) { + uint32_t *input = (uint32_t*)buffer; + uint8_t *output = (uint8_t*)device.image->data; + + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width; x++) { + uint32_t p = *input++; + *output++ = p; + *output++ = p >> 8; + *output++ = p >> 16; + } + + input += (1024 - width); + output += (1024 - width) * 3; + } + } + + void render_rgb16(unsigned width, unsigned height) { + uint32_t *input = (uint32_t*)buffer; + uint16_t *output = (uint16_t*)device.image->data; + + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width; x++) { + uint32_t p = *input++; + *output++ = ((p >> 8) & 0xf800) | ((p >> 5) & 0x07e0) | ((p >> 3) & 0x001f); //RGB32->RGB16 + } + + input += 1024 - width; + output += 1024 - width; + } + } + + void render_rgb15(unsigned width, unsigned height) { + uint32_t *input = (uint32_t*)buffer; + uint16_t *output = (uint16_t*)device.image->data; + + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width; x++) { + uint32_t p = *input++; + *output++ = ((p >> 9) & 0x7c00) | ((p >> 6) & 0x03e0) | ((p >> 3) & 0x001f); //RGB32->RGB15 + } + + input += 1024 - width; + output += 1024 - width; + } + } + + void render_yuy2(unsigned width, unsigned height) { + uint32_t *input = (uint32_t*)buffer; + uint16_t *output = (uint16_t*)device.image->data; + + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width >> 1; x++) { + uint32_t p0 = *input++; + uint32_t p1 = *input++; + p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f); //RGB32->RGB16 + p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f); //RGB32->RGB16 + + uint8_t u = (utable[p0] + utable[p1]) >> 1; + uint8_t v = (vtable[p0] + vtable[p1]) >> 1; + + *output++ = (u << 8) | ytable[p0]; + *output++ = (v << 8) | ytable[p1]; + } + + input += 1024 - width; + output += 1024 - width; + } + } + + void render_uyvy(unsigned width, unsigned height) { + uint32_t *input = (uint32_t*)buffer; + uint16_t *output = (uint16_t*)device.image->data; + + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width >> 1; x++) { + uint32_t p0 = *input++; + uint32_t p1 = *input++; + p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f); + p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f); + + uint8_t u = (utable[p0] + utable[p1]) >> 1; + uint8_t v = (vtable[p0] + vtable[p1]) >> 1; + + *output++ = (ytable[p0] << 8) | u; + *output++ = (ytable[p1] << 8) | v; + } + + input += 1024 - width; + output += 1024 - width; + } + } + + void init_yuv_tables() { + ytable = new uint8_t[65536]; + utable = new uint8_t[65536]; + vtable = new uint8_t[65536]; + + for(unsigned i = 0; i < 65536; i++) { + //extract RGB565 color data from i + uint8_t r = (i >> 11) & 31, g = (i >> 5) & 63, b = (i) & 31; + r = (r << 3) | (r >> 2); //R5->R8 + g = (g << 2) | (g >> 4); //G6->G8 + b = (b << 3) | (b >> 2); //B5->B8 + + //ITU-R Recommendation BT.601 + //double lr = 0.299, lg = 0.587, lb = 0.114; + int y = int( +(double(r) * 0.257) + (double(g) * 0.504) + (double(b) * 0.098) + 16.0 ); + int u = int( -(double(r) * 0.148) - (double(g) * 0.291) + (double(b) * 0.439) + 128.0 ); + int v = int( +(double(r) * 0.439) - (double(g) * 0.368) - (double(b) * 0.071) + 128.0 ); + + //ITU-R Recommendation BT.709 + //double lr = 0.2126, lg = 0.7152, lb = 0.0722; + //int y = int( double(r) * lr + double(g) * lg + double(b) * lb ); + //int u = int( (double(b) - y) / (2.0 - 2.0 * lb) + 128.0 ); + //int v = int( (double(r) - y) / (2.0 - 2.0 * lr) + 128.0 ); + + ytable[i] = y < 0 ? 0 : y > 255 ? 255 : y; + utable[i] = u < 0 ? 0 : u > 255 ? 255 : u; + vtable[i] = v < 0 ? 0 : v > 255 ? 255 : v; + } + } + + pVideoXv(VideoXv &self_) : self(self_) { + device.window = 0; + device.colormap = 0; + device.port = -1; + + ytable = 0; + utable = 0; + vtable = 0; + + settings.handle = 0; + settings.synchronize = false; + } +}; + +bool VideoXv::cap(Setting setting) { return p.cap(setting); } +uintptr_t VideoXv::get(Setting setting) { return p.get(setting); } +bool VideoXv::set(Setting setting, uintptr_t param) { return p.set(setting, param); } +bool VideoXv::lock(uint32_t *&data, unsigned &pitch) { return p.lock(data, pitch); } +void VideoXv::unlock() { p.unlock(); } +void VideoXv::clear() { p.clear(); } +void VideoXv::refresh(unsigned width, unsigned height) { p.refresh(width, height); } +bool VideoXv::init() { return p.init(); } +void VideoXv::term() { p.term(); } +VideoXv::VideoXv() : p(*new pVideoXv(*this)) {} +VideoXv::~VideoXv() { delete &p; } + +} diff --git a/bsnes/lib/ruby/video/xv.hpp b/bsnes/lib/ruby/video/xv.hpp new file mode 100755 index 0000000..258fe4e --- /dev/null +++ b/bsnes/lib/ruby/video/xv.hpp @@ -0,0 +1,22 @@ +class pVideoXv; + +class VideoXv : public Video { +public: + bool cap(Setting); + uintptr_t get(Setting); + bool set(Setting, uintptr_t); + + bool lock(uint32_t *&data, unsigned &pitch); + void unlock(); + + void clear(); + void refresh(unsigned width, unsigned height); + bool init(); + void term(); + + VideoXv(); + ~VideoXv(); + +private: + pVideoXv &p; +}; diff --git a/bsnes/lib/sync.bat b/bsnes/lib/sync.bat new file mode 100755 index 0000000..f886b63 --- /dev/null +++ b/bsnes/lib/sync.bat @@ -0,0 +1,9 @@ +rmdir /Q /S nall +rmdir /Q /S ruby + +mkdir nall +mkdir ruby +xcopy /E ..\..\..\nall nall +xcopy /E ..\..\..\ruby ruby +del ruby\test* +del ruby\cc.* diff --git a/bsnes/lib/sync.sh b/bsnes/lib/sync.sh new file mode 100755 index 0000000..81a8970 --- /dev/null +++ b/bsnes/lib/sync.sh @@ -0,0 +1,6 @@ +rm -r nall +rm -r ruby +cp -r ../../../nall ./nall +cp -r ../../../ruby ./ruby +rm ruby/test* +rm ruby/cc.* diff --git a/bsnes/lib/tool/opgen.cpp b/bsnes/lib/tool/opgen.cpp new file mode 100755 index 0000000..ed50771 --- /dev/null +++ b/bsnes/lib/tool/opgen.cpp @@ -0,0 +1,157 @@ +/* broken -- need to port libstring to bstring */ + +#include "libbase.h" +#include "libstring.h" +#include "libstring.cpp" + +FILE *fp, *fph, *fpt; + +stringarray data, line, part, subpart; +stringarray output_table, output_header, output_op; + +struct _op_list { + stringarray name, arg; +} op_list[64]; + +int32 op_count, line_num; + +void clear_op_list() { + op_count = 0; + for(int i = 0; i < 64; i++) { + strcpy(op_list[i].name, ""); + for(int l = 0; l < 8; l++) { + strcpy(op_list[i].arg[l], ""); + } + } +} + +void gen_header() { +int i = line_num; +char t[4096]; + clear_op_list(); + while(1) { + int z = op_count++; + strcpy(part, line[i]); + strrtrim(part, "),"); + strrtrim(part, ") {"); + split(subpart, "(", part); + strcpy(op_list[z].name, subpart[0]); + split(part, ", ", subpart[1]); + for(int l = 0; l < count(part); l++) { + strcpy(op_list[z].arg[l], part[l]); + } + if(strend(line[i], " {"))break; + i++; + } + + sprintf(output_op, "void " CLASS_NAME "::op_$$() {\r\n switch(status.cycle_pos++) {\r\n"); + sprintf(output_header, "void op_$$();\r\n"); + sprintf(output_table, "optbl[$0] = &" CLASS_NAME "::op_$$;\r\n"); + + line_num = i + 1; +} + +void update_line(int i, int n) { +char t[4096]; + replace(line[i], "end;", "status.cycle_pos = 0;"); + replace(line[i], "skip;", "status.cycle_pos++;"); +} + +void gen_op() { +int i = line_num, n, c; +char t[4096]; + while(1) { + if(strmatch(line[i], "}"))break; + + n = strdec(line[i]); + sprintf(t, "%d:", n); + strltrim(line[i], t); + sprintf(t, " case %d: {\r\n", n); + strcat(output_op, t); + + update_line(i, n); + if(!strmatch(line[i], "")) { + strcat(output_op, " "); + strcat(output_op, line[i]); + strcat(output_op, "\r\n"); + } + + i++; + while(1) { + if(strptr(line[i])[1] == ':' || strptr(line[i])[2] == ':' || strmatch(line[i], "}"))break; + + update_line(i, n); + strcat(output_op, " "); + strcat(output_op, line[i]); + strcat(output_op, "\r\n"); + + i++; + } + + if(strmatch(line[i], "}")) { + strcat(output_op, " status.cycle_pos = 0;\r\n"); + } + + strcat(output_op, " } break;\r\n"); + } + strcat(output_op, " }\r\n}"); + + line_num = i + 1; +} + +void gen_final() { +string t; + for(int i = 0; i < op_count; i++) { + strcpy(t, output_op); + replace(t, "$$", op_list[i].name); + replace(t, "$0", op_list[i].arg[0]); + replace(t, "$1", op_list[i].arg[1]); + replace(t, "$2", op_list[i].arg[2]); + replace(t, "$3", op_list[i].arg[3]); + replace(t, "$4", op_list[i].arg[4]); + replace(t, "$5", op_list[i].arg[5]); + replace(t, "$6", op_list[i].arg[6]); + replace(t, "$7", op_list[i].arg[7]); + fprintf(fp, "%s\r\n\r\n", strptr(t)); + + strcpy(t, output_header); + replace(t, "$$", op_list[i].name); + fprintf(fph, "%s", strptr(t)); + + strcpy(t, output_table); + replace(t, "$$", op_list[i].name); + replace(t, "$0", op_list[i].arg[0]); + fprintf(fpt, "%s", strptr(t)); + } +} + +void generate(char *dest, char *src) { + fp = fopen(src, "rb"); + + fseek(fp, 0, SEEK_END); +int fsize = ftell(fp); + fseek(fp, 0, SEEK_SET); +char *buf = (char*)malloc(fsize + 1); + fread(buf, 1, fsize, fp); + fclose(fp); + buf[fsize] = 0; + + strcpy(data, buf); + free(buf); + replace(data, "\r\n", "\n"); + split(line, "\n", data); + + fp = fopen(dest, "wb"); + + line_num = 0; + while(line_num < count(line)) { + while(line_num < count(line) && strmatch(line[line_num], ""))line_num++; + if(line_num >= count(line))break; + + gen_header(); + gen_op(); + gen_final(); + } + + fclose(fp); +} diff --git a/bsnes/lib/tool/opgen_fnptr.cpp b/bsnes/lib/tool/opgen_fnptr.cpp new file mode 100755 index 0000000..1f948bd --- /dev/null +++ b/bsnes/lib/tool/opgen_fnptr.cpp @@ -0,0 +1,149 @@ +/* broken -- need to port libstring to bstring */ + +#include "libbase.h" +#include "libstring.h" +#include "libstring.cpp" + +FILE *fp, *fph, *fpt; + +stringarray data, line, part, subpart; +stringarray output_table, output_header, output_op; + +struct _op_list { + stringarray name, arg; +} op_list[64]; + +int32 op_count, line_num; + +void clear_op_list() { + op_count = 0; + for(int i = 0; i < 64; i++) { + strcpy(op_list[i].name, ""); + for(int l = 0; l < 8; l++) { + strcpy(op_list[i].arg[l], ""); + } + } +} + +void gen_header() { +int i = line_num; +char t[4096]; + clear_op_list(); + while(1) { + int z = op_count++; + strcpy(part, line[i]); + strrtrim(part, "),"); + strrtrim(part, ") {"); + split(subpart, "(", part); + strcpy(op_list[z].name, subpart[0]); + split(part, ", ", subpart[1]); + for(int l = 0; l < count(part); l++) { + strcpy(op_list[z].arg[l], part[l]); + } + if(strend(line[i], " {"))break; + i++; + } + + sprintf(output_op, "void " CLASS_NAME "::op_$$() {\r\n"); + sprintf(output_header, "void op_$$();\r\n"); + sprintf(output_table, "optbl[$0] = &" CLASS_NAME "::op_$$;\r\n"); + + line_num = i + 1; +} + +void update_line(int i) { +char t[4096]; + replace(line[i], "end;", "return;"); +} + +void gen_op() { +int i = line_num, n, c; +char t[4096]; + while(1) { + if(!strcmp(line[i], "}"))break; + + n = strdec(line[i]); + sprintf(t, "%d:", n); + strltrim(line[i], t); + //sprintf(t, " case %d: {\r\n", n); + //strcat(output_op, t); + + update_line(i); + if(strcmp(line[i], "")) { + strcat(output_op, " "); + strcat(output_op, line[i]); + strcat(output_op, "\r\n"); + } + + i++; + while(1) { + if(strptr(line[i])[1] == ':' || strptr(line[i])[2] == ':' || !strcmp(line[i], "}"))break; + + update_line(i); + strcat(output_op, line[i]); + strcat(output_op, "\r\n"); + + i++; + } + } + + strcat(output_op, "}"); + line_num = i + 1; +} + +void gen_final() { +string t; + for(int i = 0; i < op_count; i++) { + strcpy(t, output_op); + replace(t, "$$", op_list[i].name); + replace(t, "$0", op_list[i].arg[0]); + replace(t, "$1", op_list[i].arg[1]); + replace(t, "$2", op_list[i].arg[2]); + replace(t, "$3", op_list[i].arg[3]); + replace(t, "$4", op_list[i].arg[4]); + replace(t, "$5", op_list[i].arg[5]); + replace(t, "$6", op_list[i].arg[6]); + replace(t, "$7", op_list[i].arg[7]); + fprintf(fp, "%s\r\n\r\n", strptr(t)); + + strcpy(t, output_header); + replace(t, "$$", op_list[i].name); + fprintf(fph, "%s", strptr(t)); + + strcpy(t, output_table); + replace(t, "$$", op_list[i].name); + replace(t, "$0", op_list[i].arg[0]); + fprintf(fpt, "%s", strptr(t)); + } +} + +void generate(char *dest, char *src) { + fp = fopen(src, "rb"); + + fseek(fp, 0, SEEK_END); +int fsize = ftell(fp); + fseek(fp, 0, SEEK_SET); +char *buf = (char*)malloc(fsize + 1); + fread(buf, 1, fsize, fp); + fclose(fp); + buf[fsize] = 0; + + strcpy(data, buf); + free(buf); + replace(data, "\r\n", "\n"); + split(line, "\n", data); + + fp = fopen(dest, "wb"); + + line_num = 0; + while(line_num < count(line)) { + while(line_num < count(line) && !strcmp(line[line_num], ""))line_num++; + if(line_num >= count(line))break; + + gen_header(); + gen_op(); + gen_final(); + } + + fclose(fp); +} diff --git a/bsnes/lib/tool/opgen_switch.cpp b/bsnes/lib/tool/opgen_switch.cpp new file mode 100755 index 0000000..80da12e --- /dev/null +++ b/bsnes/lib/tool/opgen_switch.cpp @@ -0,0 +1,127 @@ +#include +using namespace nall; + +FILE *fp; + +string data, output_op; +lstring line, part, subpart; + +struct OpList { + string name; + lstring arg; +} op_list[64]; + +int32_t op_count, line_num; + +void clear_op_list() { + op_count = 0; + for(unsigned i = 0; i < 64; i++) { + strcpy(op_list[i].name, ""); + for(unsigned l = 0; l < 8; l++) { + strcpy(op_list[i].arg[l], ""); + } + } +} + +void gen_begin() { + int i = line_num; + clear_op_list(); + + while(true) { + int z = op_count++; + string temp = line[i]; + rtrim(temp, "),"); + rtrim(temp, ") {"); + split(subpart, "(", temp); + strcpy(op_list[z].name, subpart[0]); + split(part, ", ", subpart[1]); + for(unsigned l = 0; l < count(part); l++) { + strcpy(op_list[z].arg[l], part[l]); + } + if(strend(line[i], " {") == true) break; + i++; + } + + strcpy(output_op, "//$$\r\ncase $0: {\r\n"); + line_num = i + 1; +} + +void update_line(int i) { + replace(line[i], "end;", "break;"); +} + +void gen_op() { + int i = line_num, n, c; + char t[4096]; + while(true) { + if(!strcmp(line[i], "}"))break; + + //remove cycle number + n = strdec((const char*)line[i]); + sprintf(t, "%d:", n); + ltrim(line[i], t); + //sprintf(t, "//%d:\r\n", n); + //strcat(output_op, t); + + update_line(i); + if(strcmp(line[i], "")) { + strcat(output_op, " "); + strcat(output_op, line[i]); + strcat(output_op, "\r\n"); + } + + i++; + while(true) { + if(line[i][1] == ':' || line[i][2] == ':' || line[i] == "}") break; + + update_line(i); + strcat(output_op, line[i]); + strcat(output_op, "\r\n"); + + i++; + } + } + + strcat(output_op, "} break;"); + line_num = i + 1; +} + +void gen_end() { + string t; + for(unsigned i = 0; i < op_count; i++) { + t = output_op; + replace(t, "$$", op_list[i].name); + replace(t, "$0", op_list[i].arg[0]); + replace(t, "$1", op_list[i].arg[1]); + replace(t, "$2", op_list[i].arg[2]); + replace(t, "$3", op_list[i].arg[3]); + replace(t, "$4", op_list[i].arg[4]); + replace(t, "$5", op_list[i].arg[5]); + replace(t, "$6", op_list[i].arg[6]); + replace(t, "$7", op_list[i].arg[7]); + fprintf(fp, "%s\r\n\r\n", (const char*)t); + } +} + +void generate(const char *dest, const char *src) { + fread(data, src); + replace(data, "\r\n", "\n"); + split(line, "\n", data); + + fp = fopen(dest, "wb"); + string header = CLASS_NAME; + fprintf(fp, "#ifdef %s_CPP\n\n", (const char*)strupper(header)); //inclusion guard + + line_num = 0; + while(line_num < count(line)) { + while(line_num < count(line) && !strcmp(line[line_num], "")) line_num++; + if(line_num >= count(line)) break; + + gen_begin(); + gen_op(); + gen_end(); + } + + fprintf(fp, "#endif\n"); + fclose(fp); +} diff --git a/bsnes/memory/memory.cpp b/bsnes/memory/memory.cpp new file mode 100755 index 0000000..2c607b1 --- /dev/null +++ b/bsnes/memory/memory.cpp @@ -0,0 +1,112 @@ +#include <../base.hpp> +#define MEMORY_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; +}; + +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; +} + +MMIO* MMIOAccess::get(unsigned addr) { + return mmio[(addr - 0x2000) & 0x3fff]; +} + +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 MapDirect: { + 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 MapLinear: { + 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 MapShadow: { + 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; + } +} diff --git a/bsnes/memory/memory.hpp b/bsnes/memory/memory.hpp new file mode 100755 index 0000000..03bb628 --- /dev/null +++ b/bsnes/memory/memory.hpp @@ -0,0 +1,135 @@ +struct Memory { + virtual unsigned size() const { return 0; } + 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 { + uint8 read(unsigned); + void write(unsigned, uint8); +}; + +struct UnmappedMMIO : MMIO { + uint8 mmio_read(unsigned); + void mmio_write(unsigned, uint8); +}; + +struct StaticRAM : Memory { + uint8* handle() { return data; } + unsigned size() const { return datasize; } + + inline uint8 read(unsigned addr) { return data[addr]; } + inline void write(unsigned addr, uint8 n) { data[addr] = n; } + inline uint8& operator[](unsigned addr) { return data[addr]; } + inline const uint8& operator[](unsigned addr) const { return data[addr]; } + + StaticRAM(unsigned n) : datasize(n) { data = new uint8[datasize]; } + ~StaticRAM() { delete[] data; } + +private: + uint8 *data; + unsigned datasize; +}; + +struct MappedRAM : Memory { + void map(uint8 *source, unsigned length) { data = source; datasize = length > 0 ? length : -1U; } + void write_protect(bool status) { write_protection = status; } + uint8* handle() { return data; } + unsigned size() const { return datasize; } + + inline uint8 read(unsigned addr) { return data[addr]; } + inline void write(unsigned addr, uint8 n) { if(!write_protection) data[addr] = n; } + inline const uint8& operator[](unsigned addr) const { return data[addr]; } + + MappedRAM() : data(0), datasize(0), write_protection(false) {} + +private: + uint8 *data; + unsigned datasize; + bool write_protection; +}; + +struct MMIOAccess : Memory { + void map(unsigned addr, MMIO &access); + MMIO* get(unsigned addr); + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + +private: + MMIO *mmio[0x4000]; +}; + +class Bus { +public: + unsigned mirror(unsigned addr, unsigned size); + void map(unsigned addr, Memory &access, unsigned offset); + enum MapMode { MapDirect, MapLinear, MapShadow }; + 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) { + #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); + } + + alwaysinline void write(unsigned addr, uint8 data) { + Page &p = page[addr >> 8]; + return p.access->write(p.offset + addr, data); + } + + void set_speed(bool fast) { + fastSpeed = fast ? 6 : 8; + } + + alwaysinline unsigned speed(unsigned addr) const { + if(addr & 0x408000) { + if(addr & 0x800000) return fastSpeed; + return 8; + } + if((addr + 0x6000) & 0x4000) return 8; + if((addr - 0x4000) & 0x7e00) return 6; + return 12; + } + + virtual bool load_cart() = 0; + virtual void unload_cart() = 0; + + virtual void power() = 0; + virtual void reset() = 0; + + Bus() {} + virtual ~Bus() {} + +protected: + struct Page { + Memory *access; + unsigned offset; + } page[65536]; + unsigned fastSpeed; +}; + +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; +}; diff --git a/bsnes/memory/smemory/mapper/chip.cpp b/bsnes/memory/smemory/mapper/chip.cpp new file mode 100755 index 0000000..dd23782 --- /dev/null +++ b/bsnes/memory/smemory/mapper/chip.cpp @@ -0,0 +1,54 @@ +#ifdef SMEMORY_CPP + +void sBus::map_cx4() { + map(MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, cx4); + map(MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, cx4); +} + +void sBus::map_dsp1() { + switch(cartridge.dsp1_mapper()) { + case Cartridge::DSP1LoROM1MB: { + map(MapDirect, 0x20, 0x3f, 0x8000, 0xffff, dsp1); + map(MapDirect, 0xa0, 0xbf, 0x8000, 0xffff, dsp1); + } break; + + case Cartridge::DSP1LoROM2MB: { + map(MapDirect, 0x60, 0x6f, 0x0000, 0x7fff, dsp1); + map(MapDirect, 0xe0, 0xef, 0x0000, 0x7fff, dsp1); + } break; + + case Cartridge::DSP1HiROM: { + map(MapDirect, 0x00, 0x1f, 0x6000, 0x7fff, dsp1); + map(MapDirect, 0x80, 0x9f, 0x6000, 0x7fff, dsp1); + } break; + } +} + +void sBus::map_dsp2() { + map(MapDirect, 0x20, 0x3f, 0x6000, 0x6fff, dsp2); + map(MapDirect, 0x20, 0x3f, 0x8000, 0xbfff, dsp2); + map(MapDirect, 0xa0, 0xbf, 0x6000, 0x6fff, dsp2); + map(MapDirect, 0xa0, 0xbf, 0x8000, 0xbfff, dsp2); +} + +void sBus::map_dsp3() { + map(MapDirect, 0x20, 0x3f, 0x8000, 0xffff, dsp3); + map(MapDirect, 0xa0, 0xbf, 0x8000, 0xffff, dsp3); +} + +void sBus::map_dsp4() { + map(MapDirect, 0x30, 0x3f, 0x8000, 0xffff, dsp4); + map(MapDirect, 0xb0, 0xbf, 0x8000, 0xffff, dsp4); +} + +void sBus::map_obc1() { + map(MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, obc1); + map(MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, obc1); +} + +void sBus::map_st010() { + map(MapDirect, 0x68, 0x6f, 0x0000, 0x0fff, st010); + map(MapDirect, 0xe8, 0xef, 0x0000, 0x0fff, st010); +} + +#endif diff --git a/bsnes/memory/smemory/mapper/generic.cpp b/bsnes/memory/smemory/mapper/generic.cpp new file mode 100755 index 0000000..cf15d32 --- /dev/null +++ b/bsnes/memory/smemory/mapper/generic.cpp @@ -0,0 +1,104 @@ +#ifdef SMEMORY_CPP + +void sBus::map_generic() { + switch(cartridge.mapper()) { + case Cartridge::LoROM: { + map(MapLinear, 0x00, 0x7f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x80, 0xff, 0x8000, 0xffff, memory::cartrom); + map_generic_sram(); + } break; + + case Cartridge::HiROM: { + map(MapShadow, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x40, 0x7f, 0x0000, 0xffff, memory::cartrom); + map(MapShadow, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom); + map_generic_sram(); + } break; + + case Cartridge::ExLoROM: { + map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x40, 0x7f, 0x0000, 0xffff, memory::cartrom); + map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom); + map_generic_sram(); + } break; + + case Cartridge::ExHiROM: { + map(MapShadow, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom, 0x400000); + map(MapLinear, 0x40, 0x7f, 0x0000, 0xffff, memory::cartrom, 0x400000); + map(MapShadow, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom, 0x000000); + map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom, 0x000000); + map_generic_sram(); + } break; + + case Cartridge::SPC7110ROM: { + map(MapDirect, 0x00, 0x00, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic + map(MapShadow, 0x00, 0x0f, 0x8000, 0xffff, memory::cartrom); //program ROM + map(MapDirect, 0x30, 0x30, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic + map(MapDirect, 0x50, 0x50, 0x0000, 0xffff, spc7110); //decompression MMIO port + map(MapShadow, 0x80, 0x8f, 0x8000, 0xffff, memory::cartrom); //program ROM + map(MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, memory::cartrom); //program ROM + map(MapDirect, 0xd0, 0xff, 0x0000, 0xffff, spc7110); //MMC-controlled data ROM + } break; + + case Cartridge::BSXROM: { + //full map is dynamically mapped by: + //src/chip/bsx/bsx_cart.cpp : BSXCart::update_memory_map(); + map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom); + } break; + + case Cartridge::BSCLoROM: { + map(MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom, 0x000000); + map(MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::cartrom, 0x100000); + map(MapLinear, 0x70, 0x7f, 0x0000, 0x7fff, memory::cartram, 0x000000); + map(MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom, 0x200000); + map(MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::cartrom, 0x100000); + map(MapLinear, 0xc0, 0xef, 0x0000, 0xffff, bsxflash); + map(MapLinear, 0xf0, 0xff, 0x0000, 0x7fff, memory::cartram, 0x000000); + } break; + + case Cartridge::BSCHiROM: { + map(MapShadow, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x20, 0x3f, 0x6000, 0x7fff, memory::cartram); + map(MapShadow, 0x20, 0x3f, 0x8000, 0xffff, bsxflash); + map(MapLinear, 0x40, 0x5f, 0x0000, 0xffff, memory::cartrom); + map(MapLinear, 0x60, 0x7f, 0x0000, 0xffff, bsxflash); + map(MapShadow, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0xa0, 0xbf, 0x6000, 0x7fff, memory::cartram); + map(MapShadow, 0xa0, 0xbf, 0x8000, 0xffff, bsxflash); + map(MapLinear, 0xc0, 0xdf, 0x0000, 0xffff, memory::cartrom); + map(MapLinear, 0xe0, 0xff, 0x0000, 0xffff, bsxflash); + } break; + + case Cartridge::STROM: { + map(MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::stArom); + map(MapLinear, 0x40, 0x5f, 0x8000, 0xffff, memory::stBrom); + map(MapLinear, 0x60, 0x63, 0x8000, 0xffff, memory::stAram); + map(MapLinear, 0x70, 0x73, 0x8000, 0xffff, memory::stBram); + map(MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::stArom); + map(MapLinear, 0xc0, 0xdf, 0x8000, 0xffff, memory::stBrom); + map(MapLinear, 0xe0, 0xe3, 0x8000, 0xffff, memory::stAram); + map(MapLinear, 0xf0, 0xf3, 0x8000, 0xffff, memory::stBram); + } break; + } +} + +void sBus::map_generic_sram() { + if(memory::cartram.size() == 0 || memory::cartram.size() == -1U) { return; } + + map(MapLinear, 0x20, 0x3f, 0x6000, 0x7fff, memory::cartram); + map(MapLinear, 0xa0, 0xbf, 0x6000, 0x7fff, memory::cartram); + + //research shows only games with very large ROM/RAM sizes require MAD-1 memory mapping of SRAM + //otherwise, default to safer, larger SRAM address window + uint16 addr_hi = (memory::cartrom.size() > 0x200000 || memory::cartram.size() > 32 * 1024) ? 0x7fff : 0xffff; + map(MapLinear, 0x70, 0x7f, 0x0000, addr_hi, memory::cartram); + if(cartridge.mapper() != Cartridge::LoROM) return; + map(MapLinear, 0xf0, 0xff, 0x0000, addr_hi, memory::cartram); +} + +#endif diff --git a/bsnes/memory/smemory/mapper/system.cpp b/bsnes/memory/smemory/mapper/system.cpp new file mode 100755 index 0000000..42d62b0 --- /dev/null +++ b/bsnes/memory/smemory/mapper/system.cpp @@ -0,0 +1,21 @@ +#ifdef SMEMORY_CPP + +void sBus::map_reset() { + for(uint32_t i = 0x0000; i <= 0xffff; i++) + map(i << 8, memory::memory_unmapped, 0); + + for(uint16_t i = 0x2000; i <= 0x5fff; i++) + memory::mmio.map(i, memory::mmio_unmapped); +} + +void sBus::map_system() { + map(MapDirect, 0x00, 0x3f, 0x2000, 0x5fff, memory::mmio); + map(MapDirect, 0x80, 0xbf, 0x2000, 0x5fff, memory::mmio); + + map(MapLinear, 0x00, 0x3f, 0x0000, 0x1fff, memory::wram, 0x000000, 0x002000); + map(MapLinear, 0x80, 0xbf, 0x0000, 0x1fff, memory::wram, 0x000000, 0x002000); + + map(MapLinear, 0x7e, 0x7f, 0x0000, 0xffff, memory::wram); +} + +#endif diff --git a/bsnes/memory/smemory/smemory.cpp b/bsnes/memory/smemory/smemory.cpp new file mode 100755 index 0000000..b3f1e2a --- /dev/null +++ b/bsnes/memory/smemory/smemory.cpp @@ -0,0 +1,45 @@ +#include <../base.hpp> +#include <../chip/chip.hpp> +#include <../cart/cart.hpp> +#define SMEMORY_CPP + +#include "mapper/system.cpp" +#include "mapper/generic.cpp" +#include "mapper/chip.cpp" + +void sBus::power() { + for(unsigned i = 0x2000; i <= 0x5fff; i++) memory::mmio.map(i, memory::mmio_unmapped); + for(unsigned i = 0; i < memory::wram.size(); i++) memory::wram[i] = snes.config.cpu.wram_init_value; + reset(); +} + +void sBus::reset() { + set_speed(false); +} + +bool sBus::load_cart() { + if(cartridge.loaded() == true) return false; + + map_reset(); + map_generic(); + map_system(); + + if(cartridge.has_cx4()) map_cx4(); + if(cartridge.has_dsp1()) map_dsp1(); + if(cartridge.has_dsp2()) map_dsp2(); + if(cartridge.has_dsp3()) map_dsp3(); + if(cartridge.has_dsp4()) map_dsp4(); + if(cartridge.has_obc1()) map_obc1(); + if(cartridge.has_st010()) map_st010(); + + return true; +} + +void sBus::unload_cart() { +} + +sBus::sBus() { +} + +sBus::~sBus() { +} diff --git a/bsnes/memory/smemory/smemory.hpp b/bsnes/memory/smemory/smemory.hpp new file mode 100755 index 0000000..0e8b07b --- /dev/null +++ b/bsnes/memory/smemory/smemory.hpp @@ -0,0 +1,25 @@ +class sBus : public Bus { +public: + bool load_cart(); + void unload_cart(); + + void power(); + void reset(); + + sBus(); + ~sBus(); + +private: + void map_reset(); + void map_system(); + void map_generic(); + void map_generic_sram(); + + void map_cx4(); + void map_dsp1(); + void map_dsp2(); + void map_dsp3(); + void map_dsp4(); + void map_obc1(); + void map_st010(); +}; diff --git a/bsnes/ppu/bppu/bppu.cpp b/bsnes/ppu/bppu/bppu.cpp new file mode 100755 index 0000000..d5481f3 --- /dev/null +++ b/bsnes/ppu/bppu/bppu.cpp @@ -0,0 +1,346 @@ +#include <../base.hpp> +#define BPPU_CPP + +#include "bppu_mmio.cpp" +#include "bppu_render.cpp" + +void bPPU::enter() { + loop: + //H = 0 (initialize) + scanline(); + if(ivcounter() == 0) frame(); + add_clocks(10); + + //H = 10 (OAM address reset) + if(ivcounter() == (!overscan() ? 225 : 240)) { + if(regs.display_disabled == false) { + regs.oam_addr = regs.oam_baseaddr << 1; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; + } + } + add_clocks(502); + + //H = 512 (render) + render_scanline(); + add_clocks(640); + + //H = 1152 (cache OBSEL) + cache.oam_basesize = regs.oam_basesize; + cache.oam_nameselect = regs.oam_nameselect; + cache.oam_tdaddr = regs.oam_tdaddr; + add_clocks(ilineclocks() - 1152); //seek to start of next scanline + + goto loop; +} + +void bPPU::add_clocks(unsigned clocks) { + tock(clocks); + scheduler.addclocks_ppu(clocks); +} + +void bPPU::scanline() { + snes.scanline(); + line = ivcounter(); + + if(line == 0) { + //RTO flag reset + regs.time_over = false; + regs.range_over = false; + } + + if(line == 1) { + //mosaic reset + for(int bg = BG1; bg <= BG4; bg++) regs.bg_y[bg] = 1; + regs.mosaic_countdown = regs.mosaic_size + 1; + regs.mosaic_countdown--; + } else { + for(int bg = BG1; bg <= BG4; bg++) { + if(!regs.mosaic_enabled[bg] || !regs.mosaic_countdown) regs.bg_y[bg] = line; + } + if(!regs.mosaic_countdown) regs.mosaic_countdown = regs.mosaic_size + 1; + regs.mosaic_countdown--; + } +} + +void bPPU::render_scanline() { + #ifdef FAST_FRAMESKIP + //note: this bypasses RTO status flag calculations, which is observable by software + if(status.render_output == false) return; + #endif + + if(line >= 1 && line < (!overscan() ? 225 : 240)) { + render_line_oam_rto(); + render_line(); + } +} + +void bPPU::frame() { + PPU::frame(); + snes.frame(); + + if(ifield() == 0) { + display.interlace = regs.interlace; + regs.scanlines = (regs.overscan == false) ? 224 : 239; + } +} + +void bPPU::power() { + PPU::power(); + + for(unsigned i = 0; i < memory::vram.size(); i++) memory::vram[i] = 0x00; + for(unsigned i = 0; i < memory::oam.size(); i++) memory::oam[i] = 0x00; + for(unsigned i = 0; i < memory::cgram.size(); i++) memory::cgram[i] = 0x00; + flush_tiledata_cache(); + + region = (snes.region() == SNES::NTSC ? 0 : 1); //0 = NTSC, 1 = PAL + + //$2100 + regs.display_disabled = 1; + regs.display_brightness = 15; + + //$2101 + regs.oam_basesize = 0; + regs.oam_nameselect = 0; + regs.oam_tdaddr = 0x0000; + + cache.oam_basesize = 0; + cache.oam_nameselect = 0; + cache.oam_tdaddr = 0x0000; + + //$2102-$2103 + regs.oam_baseaddr = 0x0000; + regs.oam_addr = 0x0000; + regs.oam_priority = false; + regs.oam_firstsprite = 0; + + //$2104 + regs.oam_latchdata = 0x00; + + //$2105 + regs.bg_tilesize[BG1] = 0; + regs.bg_tilesize[BG2] = 0; + regs.bg_tilesize[BG3] = 0; + regs.bg_tilesize[BG4] = 0; + regs.bg3_priority = 0; + regs.bg_mode = 0; + + //$2106 + regs.mosaic_size = 0; + regs.mosaic_enabled[BG1] = false; + regs.mosaic_enabled[BG2] = false; + regs.mosaic_enabled[BG3] = false; + regs.mosaic_enabled[BG4] = false; + regs.mosaic_countdown = 0; + + //$2107-$210a + regs.bg_scaddr[BG1] = 0x0000; + regs.bg_scaddr[BG2] = 0x0000; + regs.bg_scaddr[BG3] = 0x0000; + regs.bg_scaddr[BG4] = 0x0000; + regs.bg_scsize[BG1] = SC_32x32; + regs.bg_scsize[BG2] = SC_32x32; + regs.bg_scsize[BG3] = SC_32x32; + regs.bg_scsize[BG4] = SC_32x32; + + //$210b-$210c + regs.bg_tdaddr[BG1] = 0x0000; + regs.bg_tdaddr[BG2] = 0x0000; + regs.bg_tdaddr[BG3] = 0x0000; + regs.bg_tdaddr[BG4] = 0x0000; + + //$210d-$2114 + regs.bg_ofslatch = 0x00; + regs.m7_hofs = regs.m7_vofs = 0x0000; + regs.bg_hofs[BG1] = regs.bg_vofs[BG1] = 0x0000; + regs.bg_hofs[BG2] = regs.bg_vofs[BG2] = 0x0000; + regs.bg_hofs[BG3] = regs.bg_vofs[BG3] = 0x0000; + regs.bg_hofs[BG4] = regs.bg_vofs[BG4] = 0x0000; + + //$2115 + regs.vram_incmode = 1; + regs.vram_mapping = 0; + regs.vram_incsize = 1; + + //$2116-$2117 + regs.vram_addr = 0x0000; + + //$211a + regs.mode7_repeat = 0; + regs.mode7_vflip = false; + regs.mode7_hflip = false; + + //$211b-$2120 + regs.m7_latch = 0x00; + regs.m7a = 0x0000; + regs.m7b = 0x0000; + regs.m7c = 0x0000; + regs.m7d = 0x0000; + regs.m7x = 0x0000; + regs.m7y = 0x0000; + + //$2121 + regs.cgram_addr = 0x0000; + + //$2122 + regs.cgram_latchdata = 0x00; + + //$2123-$2125 + regs.window1_enabled[BG1] = false; + regs.window1_enabled[BG2] = false; + regs.window1_enabled[BG3] = false; + regs.window1_enabled[BG4] = false; + regs.window1_enabled[OAM] = false; + regs.window1_enabled[COL] = false; + + regs.window1_invert [BG1] = false; + regs.window1_invert [BG2] = false; + regs.window1_invert [BG3] = false; + regs.window1_invert [BG4] = false; + regs.window1_invert [OAM] = false; + regs.window1_invert [COL] = false; + + regs.window2_enabled[BG1] = false; + regs.window2_enabled[BG2] = false; + regs.window2_enabled[BG3] = false; + regs.window2_enabled[BG4] = false; + regs.window2_enabled[OAM] = false; + regs.window2_enabled[COL] = false; + + regs.window2_invert [BG1] = false; + regs.window2_invert [BG2] = false; + regs.window2_invert [BG3] = false; + regs.window2_invert [BG4] = false; + regs.window2_invert [OAM] = false; + regs.window2_invert [COL] = false; + + //$2126-$2129 + regs.window1_left = 0x00; + regs.window1_right = 0x00; + regs.window2_left = 0x00; + regs.window2_right = 0x00; + + //$212a-$212b + regs.window_mask[BG1] = 0; + regs.window_mask[BG2] = 0; + regs.window_mask[BG3] = 0; + regs.window_mask[BG4] = 0; + regs.window_mask[OAM] = 0; + regs.window_mask[COL] = 0; + + //$212c-$212d + regs.bg_enabled[BG1] = false; + regs.bg_enabled[BG2] = false; + regs.bg_enabled[BG3] = false; + regs.bg_enabled[BG4] = false; + regs.bg_enabled[OAM] = false; + regs.bgsub_enabled[BG1] = false; + regs.bgsub_enabled[BG2] = false; + regs.bgsub_enabled[BG3] = false; + regs.bgsub_enabled[BG4] = false; + regs.bgsub_enabled[OAM] = false; + + //$212e-$212f + regs.window_enabled[BG1] = false; + regs.window_enabled[BG2] = false; + regs.window_enabled[BG3] = false; + regs.window_enabled[BG4] = false; + regs.window_enabled[OAM] = false; + regs.sub_window_enabled[BG1] = false; + regs.sub_window_enabled[BG2] = false; + regs.sub_window_enabled[BG3] = false; + regs.sub_window_enabled[BG4] = false; + regs.sub_window_enabled[OAM] = false; + + //$2130 + regs.color_mask = 0; + regs.colorsub_mask = 0; + regs.addsub_mode = false; + regs.direct_color = false; + + //$2131 + regs.color_mode = 0; + regs.color_halve = false; + regs.color_enabled[BACK] = false; + regs.color_enabled[OAM] = false; + regs.color_enabled[BG4] = false; + regs.color_enabled[BG3] = false; + regs.color_enabled[BG2] = false; + regs.color_enabled[BG1] = false; + + //$2132 + regs.color_r = 0x00; + regs.color_g = 0x00; + regs.color_b = 0x00; + regs.color_rgb = 0x0000; + + //$2133 + regs.mode7_extbg = false; + regs.pseudo_hires = false; + regs.overscan = false; + regs.scanlines = 224; + regs.oam_interlace = false; + regs.interlace = false; + + //$2137 + regs.hcounter = 0; + regs.vcounter = 0; + regs.latch_hcounter = 0; + regs.latch_vcounter = 0; + regs.counters_latched = false; + + //$2139-$213a + regs.vram_readbuffer = 0x0000; + + //$213e + regs.time_over = false; + regs.range_over = false; + + reset(); +} + +void bPPU::reset() { + PPU::reset(); + PPU::frame(); + + display.interlace = false; + display.overscan = false; + regs.scanlines = 224; + + memset(sprite_list, 0, sizeof(sprite_list)); + + //open bus support + regs.ppu1_mdr = 0xff; + regs.ppu2_mdr = 0xff; + + //bg line counters + regs.bg_y[0] = 0; + regs.bg_y[1] = 0; + regs.bg_y[2] = 0; + regs.bg_y[3] = 0; +} + +bPPU::bPPU() { + alloc_tiledata_cache(); + + for(int l = 0; l < 16; l++) { + for(int i = 0; i < 4096; i++) { + mosaic_table[l][i] = (i / (l + 1)) * (l + 1); + } + } + + for(int l = 0; l < 16; l++) { + double m = (double)l / 15.0; + for(int i = 0; i < 32 * 32; i++) { + int r = (int)((double)((i) & 31) * m + 0.5); + int g = (int)((double)((i >> 5) & 31) * m + 0.5); + r = max(0, min(31, r)); + g = max(0, min(31, g)); + if(i < 32) light_table_b[l][i] = (r << 10); + light_table_gr[l][i] = (g << 5) | (r); + } + } +} + +bPPU::~bPPU() { + free_tiledata_cache(); +} diff --git a/bsnes/ppu/bppu/bppu.hpp b/bsnes/ppu/bppu/bppu.hpp new file mode 100755 index 0000000..5e2f213 --- /dev/null +++ b/bsnes/ppu/bppu/bppu.hpp @@ -0,0 +1,254 @@ +class bPPU : public PPU { +public: + void enter(); + void add_clocks(unsigned clocks); + + uint8 region; + unsigned line; + + enum { NTSC = 0, PAL = 1 }; + enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 }; + enum { SC_32x32 = 0, SC_64x32 = 1, SC_32x64 = 2, SC_64x64 = 3 }; + + struct { + bool interlace; + bool overscan; + } display; + + struct { + //open bus support + uint8 ppu1_mdr, ppu2_mdr; + + //bg line counters + uint16 bg_y[4]; + + //$2100 + bool display_disabled; + uint8 display_brightness; + + //$2101 + uint8 oam_basesize; + uint8 oam_nameselect; + uint16 oam_tdaddr; + + //$2102-$2103 + uint16 oam_baseaddr; + uint16 oam_addr; + bool oam_priority; + uint8 oam_firstsprite; + + //$2104 + uint8 oam_latchdata; + + //$2105 + bool bg_tilesize[4]; + bool bg3_priority; + uint8 bg_mode; + + //$2106 + uint8 mosaic_size; + bool mosaic_enabled[4]; + uint16 mosaic_countdown; + + //$2107-$210a + uint16 bg_scaddr[4]; + uint8 bg_scsize[4]; + + //$210b-$210c + uint16 bg_tdaddr[4]; + + //$210d-$2114 + uint8 bg_ofslatch; + uint16 m7_hofs, m7_vofs; + uint16 bg_hofs[4]; + uint16 bg_vofs[4]; + + //$2115 + bool vram_incmode; + uint8 vram_mapping; + uint8 vram_incsize; + + //$2116-$2117 + uint16 vram_addr; + + //$211a + uint8 mode7_repeat; + bool mode7_vflip; + bool mode7_hflip; + + //$211b-$2120 + uint8 m7_latch; + uint16 m7a, m7b, m7c, m7d, m7x, m7y; + + //$2121 + uint16 cgram_addr; + + //$2122 + uint8 cgram_latchdata; + + //$2123-$2125 + bool window1_enabled[6]; + bool window1_invert [6]; + bool window2_enabled[6]; + bool window2_invert [6]; + + //$2126-$2129 + uint8 window1_left, window1_right; + uint8 window2_left, window2_right; + + //$212a-$212b + uint8 window_mask[6]; + + //$212c-$212d + bool bg_enabled[5], bgsub_enabled[5]; + + //$212e-$212f + bool window_enabled[5], sub_window_enabled[5]; + + //$2130 + uint8 color_mask, colorsub_mask; + bool addsub_mode; + bool direct_color; + + //$2131 + bool color_mode, color_halve; + bool color_enabled[6]; + + //$2132 + uint8 color_r, color_g, color_b; + uint16 color_rgb; + + //$2133 + //overscan and interlace are checked once per frame to + //determine if entire frame should be interlaced/non-interlace + //and overscan adjusted. therefore, the variables act sort of + //like a buffer, but they do still affect internal rendering + bool mode7_extbg; + bool pseudo_hires; + bool overscan; + uint16 scanlines; + bool oam_interlace; + bool interlace; + + //$2137 + uint16 hcounter, vcounter; + bool latch_hcounter, latch_vcounter; + bool counters_latched; + + //$2139-$213a + uint16 vram_readbuffer; + + //$213e + bool time_over, range_over; + uint16 oam_itemcount, oam_tilecount; + } regs; + + struct { + //$2101 + uint8 oam_basesize; + uint8 oam_nameselect; + uint16 oam_tdaddr; + } cache; + + alwaysinline bool interlace() const { return display.interlace; } + alwaysinline bool overscan() const { return display.overscan; } + alwaysinline bool hires() const { return (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6); } + + uint16 get_vram_address(); + uint8 vram_mmio_read (uint16 addr); + void vram_mmio_write (uint16 addr, uint8 data); + uint8 oam_mmio_read (uint16 addr); + void oam_mmio_write (uint16 addr, uint8 data); + uint8 cgram_mmio_read (uint16 addr); + void cgram_mmio_write(uint16 addr, uint8 data); + + void mmio_w2100(uint8 value); //INIDISP + void mmio_w2101(uint8 value); //OBSEL + void mmio_w2102(uint8 value); //OAMADDL + void mmio_w2103(uint8 value); //OAMADDH + void mmio_w2104(uint8 value); //OAMDATA + void mmio_w2105(uint8 value); //BGMODE + void mmio_w2106(uint8 value); //MOSAIC + void mmio_w2107(uint8 value); //BG1SC + void mmio_w2108(uint8 value); //BG2SC + void mmio_w2109(uint8 value); //BG3SC + void mmio_w210a(uint8 value); //BG4SC + void mmio_w210b(uint8 value); //BG12NBA + void mmio_w210c(uint8 value); //BG34NBA + void mmio_w210d(uint8 value); //BG1HOFS + void mmio_w210e(uint8 value); //BG1VOFS + void mmio_w210f(uint8 value); //BG2HOFS + void mmio_w2110(uint8 value); //BG2VOFS + void mmio_w2111(uint8 value); //BG3HOFS + void mmio_w2112(uint8 value); //BG3VOFS + void mmio_w2113(uint8 value); //BG4HOFS + void mmio_w2114(uint8 value); //BG4VOFS + void mmio_w2115(uint8 value); //VMAIN + void mmio_w2116(uint8 value); //VMADDL + void mmio_w2117(uint8 value); //VMADDH + void mmio_w2118(uint8 value); //VMDATAL + void mmio_w2119(uint8 value); //VMDATAH + void mmio_w211a(uint8 value); //M7SEL + void mmio_w211b(uint8 value); //M7A + void mmio_w211c(uint8 value); //M7B + void mmio_w211d(uint8 value); //M7C + void mmio_w211e(uint8 value); //M7D + void mmio_w211f(uint8 value); //M7X + void mmio_w2120(uint8 value); //M7Y + void mmio_w2121(uint8 value); //CGADD + void mmio_w2122(uint8 value); //CGDATA + void mmio_w2123(uint8 value); //W12SEL + void mmio_w2124(uint8 value); //W34SEL + void mmio_w2125(uint8 value); //WOBJSEL + void mmio_w2126(uint8 value); //WH0 + void mmio_w2127(uint8 value); //WH1 + void mmio_w2128(uint8 value); //WH2 + void mmio_w2129(uint8 value); //WH3 + void mmio_w212a(uint8 value); //WBGLOG + void mmio_w212b(uint8 value); //WOBJLOG + void mmio_w212c(uint8 value); //TM + void mmio_w212d(uint8 value); //TS + void mmio_w212e(uint8 value); //TMW + void mmio_w212f(uint8 value); //TSW + void mmio_w2130(uint8 value); //CGWSEL + void mmio_w2131(uint8 value); //CGADDSUB + void mmio_w2132(uint8 value); //COLDATA + void mmio_w2133(uint8 value); //SETINI + uint8 mmio_r2134(); //MPYL + uint8 mmio_r2135(); //MPYM + uint8 mmio_r2136(); //MPYH + uint8 mmio_r2137(); //SLHV + uint8 mmio_r2138(); //OAMDATAREAD + uint8 mmio_r2139(); //VMDATALREAD + uint8 mmio_r213a(); //VMDATAHREAD + uint8 mmio_r213b(); //CGDATAREAD + uint8 mmio_r213c(); //OPHCT + uint8 mmio_r213d(); //OPVCT + uint8 mmio_r213e(); //STAT77 + uint8 mmio_r213f(); //STAT78 + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + void latch_counters(); + + //PPU render functions + #include "bppu_render.hpp" + + uint16 light_table_b[16][32]; + uint16 light_table_gr[16][32 * 32]; + uint16 mosaic_table[16][4096]; + void render_line(); + + void update_oam_status(); + //required functions + void run(); + void scanline(); + void render_scanline(); + void frame(); + void power(); + void reset(); + + bPPU(); + ~bPPU(); +}; diff --git a/bsnes/ppu/bppu/bppu_mmio.cpp b/bsnes/ppu/bppu/bppu_mmio.cpp new file mode 100755 index 0000000..8b8945f --- /dev/null +++ b/bsnes/ppu/bppu/bppu_mmio.cpp @@ -0,0 +1,839 @@ +#ifdef BPPU_CPP + +void bPPU::latch_counters() { + regs.hcounter = hdot(); + regs.vcounter = vcounter(); + regs.counters_latched = true; +} + +uint16 bPPU::get_vram_address() { + uint16 addr = regs.vram_addr; + switch(regs.vram_mapping) { + case 0: break; //direct mapping + case 1: addr = (addr & 0xff00) | ((addr & 0x001f) << 3) | ((addr >> 5) & 7); break; + case 2: addr = (addr & 0xfe00) | ((addr & 0x003f) << 3) | ((addr >> 6) & 7); break; + case 3: addr = (addr & 0xfc00) | ((addr & 0x007f) << 3) | ((addr >> 7) & 7); break; + } + return (addr << 1); +} + +//NOTE: all VRAM writes during active display are invalid. Unlike OAM and CGRAM, they will +//not be written anywhere at all. The below address ranges for where writes are invalid have +//been validated on hardware, as has the edge case where the S-CPU MDR can be written if the +//write occurs during the very last clock cycle of vblank. + +uint8 bPPU::vram_mmio_read(uint16 addr) { + uint8 data; + + if(regs.display_disabled == true) { + data = memory::vram[addr]; + } else { + uint16 v = vcounter(); + uint16 h = hcounter(); + uint16 ls = ((snes.region() == SNES::NTSC ? 525 : 625) >> 1) - 1; + if(interlace() && !field()) ls++; + + if(v == ls && h == 1362) { + data = 0x00; + } else if(v < (!overscan() ? 224 : 239)) { + data = 0x00; + } else if(v == (!overscan() ? 224 : 239)) { + if(h == 1362) { + data = memory::vram[addr]; + } else { + data = 0x00; + } + } else { + data = memory::vram[addr]; + } + } + + return data; +} + +void bPPU::vram_mmio_write(uint16 addr, uint8 data) { + if(regs.display_disabled == true) { + memory::vram[addr] = data; + } else { + uint16 v = vcounter(); + uint16 h = hcounter(); + if(v == 0) { + if(h <= 4) { + memory::vram[addr] = data; + } else if(h == 6) { + memory::vram[addr] = cpu.regs.mdr; + } else { + //no write + } + } else if(v < (!overscan() ? 225 : 240)) { + //no write + } else if(v == (!overscan() ? 225 : 240)) { + if(h <= 4) { + //no write + } else { + memory::vram[addr] = data; + } + } else { + memory::vram[addr] = data; + } + } +} + +//NOTE: OAM accesses during active display are rerouted to 0x0218 ... this can be considered +//a hack. The actual address varies during rendering, as the S-PPU reads in data itself for +//processing. Unfortunately, we have yet to determine how this works. The algorithm cannot be +//reverse engineered using a scanline renderer such as this, and at this time, there does not +//exist a more accurate SNES PPU emulator to work from. The only known game to actually access +//OAM during active display is Uniracers. It expects accesses to map to offset 0x0218. +//It was decided by public consensus to map writes to this address to match Uniracers, primarily +//because it is the only game observed to do this, but also because mapping to this address does +//not contradict any of our findings, because we have no findings whatsoever on this behavior. +//Think of this what you will, I openly admit that this is a hack. But it is more accurate than +//writing to the 'expected' address set by $2102,$2103, and will catch problems in software that +//accidentally accesses OAM during active display by virtue of not returning the expected data. + +uint8 bPPU::oam_mmio_read(uint16 addr) { + addr &= 0x03ff; + if(addr & 0x0200) addr &= 0x021f; + uint8 data; + + if(regs.display_disabled == true) { + data = memory::oam[addr]; + } else { + if(vcounter() < (!overscan() ? 225 : 240)) { + data = memory::oam[0x0218]; + } else { + data = memory::oam[addr]; + } + } + + return data; +} + +void bPPU::oam_mmio_write(uint16 addr, uint8 data) { + addr &= 0x03ff; + if(addr & 0x0200) addr &= 0x021f; + + if(regs.display_disabled == true) { + memory::oam[addr] = data; + } else { + if(vcounter() < (!overscan() ? 225 : 240)) { + memory::oam[0x0218] = data; + } else { + memory::oam[addr] = data; + } + } +} + +//NOTE: CGRAM writes during hblank are valid. During active display, the actual address the +//data is written to varies, as the S-PPU itself changes the address. Like OAM, we do not know +//the exact algorithm used, but we have zero known examples of any commercial software that +//attempts to do this. Therefore, the addresses are mapped to 0x01ff. There is nothing special +//about this address, it is simply more accurate to invalidate the 'expected' address than not. + +uint8 bPPU::cgram_mmio_read(uint16 addr) { + addr &= 0x01ff; + uint8 data; + + if(regs.display_disabled == true) { + data = memory::cgram[addr]; + } else { + uint16 v = vcounter(); + uint16 h = hcounter(); + if(v < (!overscan() ? 225 : 240) && h >= 72 && h < 1096) { + data = memory::cgram[0x01ff] & 0x7f; + } else { + data = memory::cgram[addr]; + } + } + + if(addr & 1) data &= 0x7f; + return data; +} + +void bPPU::cgram_mmio_write(uint16 addr, uint8 data) { + addr &= 0x01ff; + if(addr & 1) data &= 0x7f; + + if(regs.display_disabled == true) { + memory::cgram[addr] = data; + } else { + uint16 v = vcounter(); + uint16 h = hcounter(); + if(v < (!overscan() ? 225 : 240) && h >= 72 && h < 1096) { + memory::cgram[0x01ff] = data & 0x7f; + } else { + memory::cgram[addr] = data; + } + } +} + +//INIDISP +void bPPU::mmio_w2100(uint8 value) { + if(regs.display_disabled == true && vcounter() == (!overscan() ? 225 : 240)) { + regs.oam_addr = regs.oam_baseaddr << 1; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; + } + + regs.display_disabled = !!(value & 0x80); + regs.display_brightness = value & 15; +} + +//OBSEL +void bPPU::mmio_w2101(uint8 value) { + regs.oam_basesize = (value >> 5) & 7; + regs.oam_nameselect = (value >> 3) & 3; + regs.oam_tdaddr = (value & 3) << 14; +} + +//OAMADDL +void bPPU::mmio_w2102(uint8 data) { + regs.oam_baseaddr = (regs.oam_baseaddr & ~0xff) | (data << 0); + regs.oam_baseaddr &= 0x01ff; + regs.oam_addr = regs.oam_baseaddr << 1; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; +} + +//OAMADDH +void bPPU::mmio_w2103(uint8 data) { + regs.oam_priority = !!(data & 0x80); + regs.oam_baseaddr = (regs.oam_baseaddr & 0xff) | (data << 8); + regs.oam_baseaddr &= 0x01ff; + regs.oam_addr = regs.oam_baseaddr << 1; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; +} + +//OAMDATA +void bPPU::mmio_w2104(uint8 data) { + if(regs.oam_addr & 0x0200) { + oam_mmio_write(regs.oam_addr, data); + } else if((regs.oam_addr & 1) == 0) { + regs.oam_latchdata = data; + } else { + oam_mmio_write((regs.oam_addr & ~1) + 0, regs.oam_latchdata); + oam_mmio_write((regs.oam_addr & ~1) + 1, data); + } + + regs.oam_addr++; + regs.oam_addr &= 0x03ff; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; +} + +//BGMODE +void bPPU::mmio_w2105(uint8 value) { + regs.bg_tilesize[BG4] = !!(value & 0x80); + regs.bg_tilesize[BG3] = !!(value & 0x40); + regs.bg_tilesize[BG2] = !!(value & 0x20); + regs.bg_tilesize[BG1] = !!(value & 0x10); + regs.bg3_priority = !!(value & 0x08); + regs.bg_mode = (value & 7); +} + +//MOSAIC +void bPPU::mmio_w2106(uint8 value) { + regs.mosaic_size = (value >> 4) & 15; + regs.mosaic_enabled[BG4] = !!(value & 0x08); + regs.mosaic_enabled[BG3] = !!(value & 0x04); + regs.mosaic_enabled[BG2] = !!(value & 0x02); + regs.mosaic_enabled[BG1] = !!(value & 0x01); +} + +//BG1SC +void bPPU::mmio_w2107(uint8 value) { + regs.bg_scaddr[BG1] = (value & 0x7c) << 9; + regs.bg_scsize[BG1] = value & 3; +} + +//BG2SC +void bPPU::mmio_w2108(uint8 value) { + regs.bg_scaddr[BG2] = (value & 0x7c) << 9; + regs.bg_scsize[BG2] = value & 3; +} + +//BG3SC +void bPPU::mmio_w2109(uint8 value) { + regs.bg_scaddr[BG3] = (value & 0x7c) << 9; + regs.bg_scsize[BG3] = value & 3; +} + +//BG4SC +void bPPU::mmio_w210a(uint8 value) { + regs.bg_scaddr[BG4] = (value & 0x7c) << 9; + regs.bg_scsize[BG4] = value & 3; +} + +//BG12NBA +void bPPU::mmio_w210b(uint8 value) { + regs.bg_tdaddr[BG1] = (value & 0x07) << 13; + regs.bg_tdaddr[BG2] = (value & 0x70) << 9; +} + +//BG34NBA +void bPPU::mmio_w210c(uint8 value) { + regs.bg_tdaddr[BG3] = (value & 0x07) << 13; + regs.bg_tdaddr[BG4] = (value & 0x70) << 9; +} + +//BG1HOFS +void bPPU::mmio_w210d(uint8 value) { + regs.m7_hofs = (value << 8) | regs.m7_latch; + regs.m7_latch = value; + + regs.bg_hofs[BG1] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG1] >> 8) & 7); + regs.bg_ofslatch = value; +} + +//BG1VOFS +void bPPU::mmio_w210e(uint8 value) { + regs.m7_vofs = (value << 8) | regs.m7_latch; + regs.m7_latch = value; + + regs.bg_vofs[BG1] = (value << 8) | (regs.bg_ofslatch); + regs.bg_ofslatch = value; +} + +//BG2HOFS +void bPPU::mmio_w210f(uint8 value) { + regs.bg_hofs[BG2] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG2] >> 8) & 7); + regs.bg_ofslatch = value; +} + +//BG2VOFS +void bPPU::mmio_w2110(uint8 value) { + regs.bg_vofs[BG2] = (value << 8) | (regs.bg_ofslatch); + regs.bg_ofslatch = value; +} + +//BG3HOFS +void bPPU::mmio_w2111(uint8 value) { + regs.bg_hofs[BG3] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG3] >> 8) & 7); + regs.bg_ofslatch = value; +} + +//BG3VOFS +void bPPU::mmio_w2112(uint8 value) { + regs.bg_vofs[BG3] = (value << 8) | (regs.bg_ofslatch); + regs.bg_ofslatch = value; +} + +//BG4HOFS +void bPPU::mmio_w2113(uint8 value) { + regs.bg_hofs[BG4] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG4] >> 8) & 7); + regs.bg_ofslatch = value; +} + +//BG4VOFS +void bPPU::mmio_w2114(uint8 value) { + regs.bg_vofs[BG4] = (value << 8) | (regs.bg_ofslatch); + regs.bg_ofslatch = value; +} + +//VMAIN +void bPPU::mmio_w2115(uint8 value) { + regs.vram_incmode = !!(value & 0x80); + regs.vram_mapping = (value >> 2) & 3; + switch(value & 3) { + case 0: regs.vram_incsize = 1; break; + case 1: regs.vram_incsize = 32; break; + case 2: regs.vram_incsize = 128; break; + case 3: regs.vram_incsize = 128; break; + } +} + +//VMADDL +void bPPU::mmio_w2116(uint8 value) { + regs.vram_addr = (regs.vram_addr & 0xff00) | value; +uint16 addr = get_vram_address(); + regs.vram_readbuffer = vram_mmio_read(addr + 0); + regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8; +} + +//VMADDH +void bPPU::mmio_w2117(uint8 value) { + regs.vram_addr = (value << 8) | (regs.vram_addr & 0x00ff); +uint16 addr = get_vram_address(); + regs.vram_readbuffer = vram_mmio_read(addr + 0); + regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8; +} + +//VMDATAL +void bPPU::mmio_w2118(uint8 value) { +uint16 addr = get_vram_address(); + vram_mmio_write(addr, value); + bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1; + bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1; + bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1; + + if(regs.vram_incmode == 0) { + regs.vram_addr += regs.vram_incsize; + } +} + +//VMDATAH +void bPPU::mmio_w2119(uint8 value) { +uint16 addr = get_vram_address() + 1; + vram_mmio_write(addr, value); + bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1; + bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1; + bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1; + + if(regs.vram_incmode == 1) { + regs.vram_addr += regs.vram_incsize; + } +} + +//M7SEL +void bPPU::mmio_w211a(uint8 value) { + regs.mode7_repeat = (value >> 6) & 3; + regs.mode7_vflip = !!(value & 0x02); + regs.mode7_hflip = !!(value & 0x01); +} + +//M7A +void bPPU::mmio_w211b(uint8 value) { + regs.m7a = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7B +void bPPU::mmio_w211c(uint8 value) { + regs.m7b = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7C +void bPPU::mmio_w211d(uint8 value) { + regs.m7c = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7D +void bPPU::mmio_w211e(uint8 value) { + regs.m7d = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7X +void bPPU::mmio_w211f(uint8 value) { + regs.m7x = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7Y +void bPPU::mmio_w2120(uint8 value) { + regs.m7y = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//CGADD +void bPPU::mmio_w2121(uint8 value) { + regs.cgram_addr = value << 1; +} + +//CGDATA +//note: CGRAM palette data format is 15-bits +//(0,bbbbb,ggggg,rrrrr). Highest bit is ignored, +//as evidenced by $213b CGRAM data reads. +// +//anomie indicates writes to CGDATA work the same +//as writes to OAMDATA's low table. need to verify +//this on hardware. +void bPPU::mmio_w2122(uint8 value) { + if(!(regs.cgram_addr & 1)) { + regs.cgram_latchdata = value; + } else { + cgram_mmio_write((regs.cgram_addr & 0x01fe), regs.cgram_latchdata); + cgram_mmio_write((regs.cgram_addr & 0x01fe) + 1, value & 0x7f); + } + regs.cgram_addr++; + regs.cgram_addr &= 0x01ff; +} + +//W12SEL +void bPPU::mmio_w2123(uint8 value) { + regs.window2_enabled[BG2] = !!(value & 0x80); + regs.window2_invert [BG2] = !!(value & 0x40); + regs.window1_enabled[BG2] = !!(value & 0x20); + regs.window1_invert [BG2] = !!(value & 0x10); + regs.window2_enabled[BG1] = !!(value & 0x08); + regs.window2_invert [BG1] = !!(value & 0x04); + regs.window1_enabled[BG1] = !!(value & 0x02); + regs.window1_invert [BG1] = !!(value & 0x01); +} + +//W34SEL +void bPPU::mmio_w2124(uint8 value) { + regs.window2_enabled[BG4] = !!(value & 0x80); + regs.window2_invert [BG4] = !!(value & 0x40); + regs.window1_enabled[BG4] = !!(value & 0x20); + regs.window1_invert [BG4] = !!(value & 0x10); + regs.window2_enabled[BG3] = !!(value & 0x08); + regs.window2_invert [BG3] = !!(value & 0x04); + regs.window1_enabled[BG3] = !!(value & 0x02); + regs.window1_invert [BG3] = !!(value & 0x01); +} + +//WOBJSEL +void bPPU::mmio_w2125(uint8 value) { + regs.window2_enabled[COL] = !!(value & 0x80); + regs.window2_invert [COL] = !!(value & 0x40); + regs.window1_enabled[COL] = !!(value & 0x20); + regs.window1_invert [COL] = !!(value & 0x10); + regs.window2_enabled[OAM] = !!(value & 0x08); + regs.window2_invert [OAM] = !!(value & 0x04); + regs.window1_enabled[OAM] = !!(value & 0x02); + regs.window1_invert [OAM] = !!(value & 0x01); +} + +//WH0 +void bPPU::mmio_w2126(uint8 value) { + regs.window1_left = value; +} + +//WH1 +void bPPU::mmio_w2127(uint8 value) { + regs.window1_right = value; +} + +//WH2 +void bPPU::mmio_w2128(uint8 value) { + regs.window2_left = value; +} + +//WH3 +void bPPU::mmio_w2129(uint8 value) { + regs.window2_right = value; +} + +//WBGLOG +void bPPU::mmio_w212a(uint8 value) { + regs.window_mask[BG4] = (value >> 6) & 3; + regs.window_mask[BG3] = (value >> 4) & 3; + regs.window_mask[BG2] = (value >> 2) & 3; + regs.window_mask[BG1] = (value ) & 3; +} + +//WOBJLOG +void bPPU::mmio_w212b(uint8 value) { + regs.window_mask[COL] = (value >> 2) & 3; + regs.window_mask[OAM] = (value ) & 3; +} + +//TM +void bPPU::mmio_w212c(uint8 value) { + regs.bg_enabled[OAM] = !!(value & 0x10); + regs.bg_enabled[BG4] = !!(value & 0x08); + regs.bg_enabled[BG3] = !!(value & 0x04); + regs.bg_enabled[BG2] = !!(value & 0x02); + regs.bg_enabled[BG1] = !!(value & 0x01); +} + +//TS +void bPPU::mmio_w212d(uint8 value) { + regs.bgsub_enabled[OAM] = !!(value & 0x10); + regs.bgsub_enabled[BG4] = !!(value & 0x08); + regs.bgsub_enabled[BG3] = !!(value & 0x04); + regs.bgsub_enabled[BG2] = !!(value & 0x02); + regs.bgsub_enabled[BG1] = !!(value & 0x01); +} + +//TMW +void bPPU::mmio_w212e(uint8 value) { + regs.window_enabled[OAM] = !!(value & 0x10); + regs.window_enabled[BG4] = !!(value & 0x08); + regs.window_enabled[BG3] = !!(value & 0x04); + regs.window_enabled[BG2] = !!(value & 0x02); + regs.window_enabled[BG1] = !!(value & 0x01); +} + +//TSW +void bPPU::mmio_w212f(uint8 value) { + regs.sub_window_enabled[OAM] = !!(value & 0x10); + regs.sub_window_enabled[BG4] = !!(value & 0x08); + regs.sub_window_enabled[BG3] = !!(value & 0x04); + regs.sub_window_enabled[BG2] = !!(value & 0x02); + regs.sub_window_enabled[BG1] = !!(value & 0x01); +} + +//CGWSEL +void bPPU::mmio_w2130(uint8 value) { + regs.color_mask = (value >> 6) & 3; + regs.colorsub_mask = (value >> 4) & 3; + regs.addsub_mode = !!(value & 0x02); + regs.direct_color = !!(value & 0x01); +} + +//CGADDSUB +void bPPU::mmio_w2131(uint8 value) { + regs.color_mode = !!(value & 0x80); + regs.color_halve = !!(value & 0x40); + regs.color_enabled[BACK] = !!(value & 0x20); + regs.color_enabled[OAM] = !!(value & 0x10); + regs.color_enabled[BG4] = !!(value & 0x08); + regs.color_enabled[BG3] = !!(value & 0x04); + regs.color_enabled[BG2] = !!(value & 0x02); + regs.color_enabled[BG1] = !!(value & 0x01); +} + +//COLDATA +void bPPU::mmio_w2132(uint8 value) { + if(value & 0x80) regs.color_b = value & 0x1f; + if(value & 0x40) regs.color_g = value & 0x1f; + if(value & 0x20) regs.color_r = value & 0x1f; + + regs.color_rgb = (regs.color_r) + | (regs.color_g << 5) + | (regs.color_b << 10); +} + +//SETINI +void bPPU::mmio_w2133(uint8 value) { + regs.mode7_extbg = !!(value & 0x40); + regs.pseudo_hires = !!(value & 0x08); + regs.overscan = !!(value & 0x04); + regs.oam_interlace = !!(value & 0x02); + regs.interlace = !!(value & 0x01); + + display.overscan = regs.overscan; +} + +//MPYL +uint8 bPPU::mmio_r2134() { +uint32 r; + r = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = r; + return regs.ppu1_mdr; +} + +//MPYM +uint8 bPPU::mmio_r2135() { +uint32 r; + r = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = r >> 8; + return regs.ppu1_mdr; +} + +//MPYH +uint8 bPPU::mmio_r2136() { +uint32 r; + r = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = r >> 16; + return regs.ppu1_mdr; +} + +//SLHV +uint8 bPPU::mmio_r2137() { + if(cpu.pio() & 0x80) { + latch_counters(); + } + return cpu.regs.mdr; +} + +//OAMDATAREAD +uint8 bPPU::mmio_r2138() { + regs.ppu1_mdr = oam_mmio_read(regs.oam_addr); + + regs.oam_addr++; + regs.oam_addr &= 0x03ff; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; + + return regs.ppu1_mdr; +} + +//VMDATALREAD +uint8 bPPU::mmio_r2139() { +uint16 addr = get_vram_address(); + regs.ppu1_mdr = regs.vram_readbuffer; + if(regs.vram_incmode == 0) { + addr &= 0xfffe; + regs.vram_readbuffer = vram_mmio_read(addr + 0); + regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8; + regs.vram_addr += regs.vram_incsize; + } + return regs.ppu1_mdr; +} + +//VMDATAHREAD +uint8 bPPU::mmio_r213a() { +uint16 addr = get_vram_address() + 1; + regs.ppu1_mdr = regs.vram_readbuffer >> 8; + if(regs.vram_incmode == 1) { + addr &= 0xfffe; + regs.vram_readbuffer = vram_mmio_read(addr + 0); + regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8; + regs.vram_addr += regs.vram_incsize; + } + return regs.ppu1_mdr; +} + +//CGDATAREAD +//note: CGRAM palette data is 15-bits (0,bbbbb,ggggg,rrrrr) +//therefore, the high byte read from each color does not +//update bit 7 of the PPU2 MDR. +uint8 bPPU::mmio_r213b() { + if(!(regs.cgram_addr & 1)) { + regs.ppu2_mdr = cgram_mmio_read(regs.cgram_addr) & 0xff; + } else { + regs.ppu2_mdr &= 0x80; + regs.ppu2_mdr |= cgram_mmio_read(regs.cgram_addr) & 0x7f; + } + regs.cgram_addr++; + regs.cgram_addr &= 0x01ff; + return regs.ppu2_mdr; +} + +//OPHCT +uint8 bPPU::mmio_r213c() { + if(!regs.latch_hcounter) { + regs.ppu2_mdr = regs.hcounter & 0xff; + } else { + regs.ppu2_mdr &= 0xfe; + regs.ppu2_mdr |= (regs.hcounter >> 8) & 1; + } + regs.latch_hcounter ^= 1; + return regs.ppu2_mdr; +} + +//OPVCT +uint8 bPPU::mmio_r213d() { + if(!regs.latch_vcounter) { + regs.ppu2_mdr = regs.vcounter & 0xff; + } else { + regs.ppu2_mdr &= 0xfe; + regs.ppu2_mdr |= (regs.vcounter >> 8) & 1; + } + regs.latch_vcounter ^= 1; + return regs.ppu2_mdr; +} + +//STAT77 +uint8 bPPU::mmio_r213e() { +uint8 r = 0x00; + r |= (regs.time_over) ? 0x80 : 0x00; + r |= (regs.range_over) ? 0x40 : 0x00; + r |= (regs.ppu1_mdr & 0x10); + r |= (ppu1_version & 0x0f); + regs.ppu1_mdr = r; + return regs.ppu1_mdr; +} + +//STAT78 +uint8 bPPU::mmio_r213f() { +uint8 r = 0x00; + regs.latch_hcounter = 0; + regs.latch_vcounter = 0; + + r |= field() << 7; + if(!(cpu.pio() & 0x80)) { + r |= 0x40; + } else if(regs.counters_latched == true) { + r |= 0x40; + regs.counters_latched = false; + } + r |= (regs.ppu2_mdr & 0x20); + r |= (region << 4); //0 = NTSC, 1 = PAL + r |= (ppu2_version & 0x0f); + regs.ppu2_mdr = r; + return regs.ppu2_mdr; +} + +uint8 bPPU::mmio_read(unsigned addr) { + scheduler.sync_cpuppu(); + + switch(addr & 0xffff) { + case 0x2104: + case 0x2105: + case 0x2106: + case 0x2108: + case 0x2109: + case 0x210a: + case 0x2114: + case 0x2115: + case 0x2116: + case 0x2118: + case 0x2119: + case 0x211a: + case 0x2124: + case 0x2125: + case 0x2126: + case 0x2128: + case 0x2129: + case 0x212a: return regs.ppu1_mdr; + case 0x2134: return mmio_r2134(); //MPYL + case 0x2135: return mmio_r2135(); //MPYM + case 0x2136: return mmio_r2136(); //MPYH + case 0x2137: return mmio_r2137(); //SLHV + case 0x2138: return mmio_r2138(); //OAMDATAREAD + case 0x2139: return mmio_r2139(); //VMDATALREAD + case 0x213a: return mmio_r213a(); //VMDATAHREAD + case 0x213b: return mmio_r213b(); //CGDATAREAD + case 0x213c: return mmio_r213c(); //OPHCT + case 0x213d: return mmio_r213d(); //OPVCT + case 0x213e: return mmio_r213e(); //STAT77 + case 0x213f: return mmio_r213f(); //STAT78 + } + + //return 0x00; + return cpu.regs.mdr; +} + +void bPPU::mmio_write(unsigned addr, uint8 data) { + scheduler.sync_cpuppu(); + + switch(addr & 0xffff) { + case 0x2100: mmio_w2100(data); return; //INIDISP + case 0x2101: mmio_w2101(data); return; //OBSEL + case 0x2102: mmio_w2102(data); return; //OAMADDL + case 0x2103: mmio_w2103(data); return; //OAMADDH + case 0x2104: mmio_w2104(data); return; //OAMDATA + case 0x2105: mmio_w2105(data); return; //BGMODE + case 0x2106: mmio_w2106(data); return; //MOSAIC + case 0x2107: mmio_w2107(data); return; //BG1SC + case 0x2108: mmio_w2108(data); return; //BG2SC + case 0x2109: mmio_w2109(data); return; //BG3SC + case 0x210a: mmio_w210a(data); return; //BG4SC + case 0x210b: mmio_w210b(data); return; //BG12NBA + case 0x210c: mmio_w210c(data); return; //BG34NBA + case 0x210d: mmio_w210d(data); return; //BG1HOFS + case 0x210e: mmio_w210e(data); return; //BG1VOFS + case 0x210f: mmio_w210f(data); return; //BG2HOFS + case 0x2110: mmio_w2110(data); return; //BG2VOFS + case 0x2111: mmio_w2111(data); return; //BG3HOFS + case 0x2112: mmio_w2112(data); return; //BG3VOFS + case 0x2113: mmio_w2113(data); return; //BG4HOFS + case 0x2114: mmio_w2114(data); return; //BG4VOFS + case 0x2115: mmio_w2115(data); return; //VMAIN + case 0x2116: mmio_w2116(data); return; //VMADDL + case 0x2117: mmio_w2117(data); return; //VMADDH + case 0x2118: mmio_w2118(data); return; //VMDATAL + case 0x2119: mmio_w2119(data); return; //VMDATAH + case 0x211a: mmio_w211a(data); return; //M7SEL + case 0x211b: mmio_w211b(data); return; //M7A + case 0x211c: mmio_w211c(data); return; //M7B + case 0x211d: mmio_w211d(data); return; //M7C + case 0x211e: mmio_w211e(data); return; //M7D + case 0x211f: mmio_w211f(data); return; //M7X + case 0x2120: mmio_w2120(data); return; //M7Y + case 0x2121: mmio_w2121(data); return; //CGADD + case 0x2122: mmio_w2122(data); return; //CGDATA + case 0x2123: mmio_w2123(data); return; //W12SEL + case 0x2124: mmio_w2124(data); return; //W34SEL + case 0x2125: mmio_w2125(data); return; //WOBJSEL + case 0x2126: mmio_w2126(data); return; //WH0 + case 0x2127: mmio_w2127(data); return; //WH1 + case 0x2128: mmio_w2128(data); return; //WH2 + case 0x2129: mmio_w2129(data); return; //WH3 + case 0x212a: mmio_w212a(data); return; //WBGLOG + case 0x212b: mmio_w212b(data); return; //WOBJLOG + case 0x212c: mmio_w212c(data); return; //TM + case 0x212d: mmio_w212d(data); return; //TS + case 0x212e: mmio_w212e(data); return; //TMW + case 0x212f: mmio_w212f(data); return; //TSW + case 0x2130: mmio_w2130(data); return; //CGWSEL + case 0x2131: mmio_w2131(data); return; //CGADDSUB + case 0x2132: mmio_w2132(data); return; //COLDATA + case 0x2133: mmio_w2133(data); return; //SETINI + } +} + +#endif //ifdef BPPU_CPP diff --git a/bsnes/ppu/bppu/bppu_render.cpp b/bsnes/ppu/bppu/bppu_render.cpp new file mode 100755 index 0000000..86729dd --- /dev/null +++ b/bsnes/ppu/bppu/bppu_render.cpp @@ -0,0 +1,150 @@ +#ifdef BPPU_CPP + +#include "bppu_render_cache.cpp" +#include "bppu_render_windows.cpp" +#include "bppu_render_bg.cpp" +#include "bppu_render_oam.cpp" +#include "bppu_render_mode7.cpp" +#include "bppu_render_addsub.cpp" +#include "bppu_render_line.cpp" + +//this function can be used to disable BG / OAM layer rendering (currently unused) +bool bPPU::render_enabled(uint8 bg, uint8 pri) { + return true; +} + +/* +Mode 0: -> + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 + BG4B, BG3B, OAM0, BG4A, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3 +*/ +void bPPU::render_line_mode0() { + render_line_bg(BG1, COLORDEPTH_4, 8, 11); + render_line_bg(BG2, COLORDEPTH_4, 7, 10); + render_line_bg(BG3, COLORDEPTH_4, 2, 5); + render_line_bg(BG4, COLORDEPTH_4, 1, 4); + render_line_oam(3, 6, 9, 12); +} + +/* +Mode 1 (pri=1): -> + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + BG3B, OAM0, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3, BG3A + +Mode 1 (pri=0): -> + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + BG3B, OAM0, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3 +*/ +void bPPU::render_line_mode1() { + if(regs.bg3_priority) { + render_line_bg(BG1, COLORDEPTH_16, 5, 8); + render_line_bg(BG2, COLORDEPTH_16, 4, 7); + render_line_bg(BG3, COLORDEPTH_4, 1, 10); + render_line_oam(2, 3, 6, 9); + } else { + render_line_bg(BG1, COLORDEPTH_16, 6, 9); + render_line_bg(BG2, COLORDEPTH_16, 5, 8); + render_line_bg(BG3, COLORDEPTH_4, 1, 3); + render_line_oam(2, 4, 7, 10); + } +} + +/* +Mode 2: -> + 1, 2, 3, 4, 5, 6, 7, 8 + BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3 +*/ +void bPPU::render_line_mode2() { + render_line_bg(BG1, COLORDEPTH_16, 3, 7); + render_line_bg(BG2, COLORDEPTH_16, 1, 5); + render_line_oam(2, 4, 6, 8); +} + +/* +Mode 3: -> + 1, 2, 3, 4, 5, 6, 7, 8 + BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3 +*/ +void bPPU::render_line_mode3() { + render_line_bg(BG1, COLORDEPTH_256, 3, 7); + render_line_bg(BG2, COLORDEPTH_16, 1, 5); + render_line_oam(2, 4, 6, 8); +} + +/* +Mode 4: -> + 1, 2, 3, 4, 5, 6, 7, 8 + BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3 +*/ +void bPPU::render_line_mode4() { + render_line_bg(BG1, COLORDEPTH_256, 3, 7); + render_line_bg(BG2, COLORDEPTH_4, 1, 5); + render_line_oam(2, 4, 6, 8); +} + +/* +Mode 5: -> + 1, 2, 3, 4, 5, 6, 7, 8 + BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3 +*/ +void bPPU::render_line_mode5() { + render_line_bg(BG1, COLORDEPTH_16, 3, 7); + render_line_bg(BG2, COLORDEPTH_4, 1, 5); + render_line_oam(2, 4, 6, 8); +} + +/* +Mode 6: -> + 1, 2, 3, 4, 5, 6 + OAM0, BG1B, OAM1, OAM2, BG1A, OAM3 +*/ +void bPPU::render_line_mode6() { + render_line_bg(BG1, COLORDEPTH_16, 2, 5); + render_line_oam(1, 3, 4, 6); +} + +/* +Mode7: -> + 1, 2, 3, 4, 5 + OAM0, BG1n, OAM1, OAM2, OAM3 + +Mode 7 EXTBG: -> + 1, 2, 3, 4, 5, 6, 7 + BG2B, OAM0, BG1n, OAM1, BG2A, OAM2, OAM3 +*/ +void bPPU::render_line_mode7() { + if(regs.mode7_extbg == false) { + render_line_mode7(BG1, 2, 2); + render_line_oam(1, 3, 4, 5); + } else { + render_line_mode7(BG1, 3, 3); + render_line_mode7(BG2, 1, 5); + render_line_oam(2, 4, 6, 7); + } +} + +void bPPU::render_line() { + if(regs.display_disabled == true) { + render_line_clear(); + return; + } + + flush_pixel_cache(); + build_window_tables(COL); + update_bg_info(); + + switch(regs.bg_mode) { + case 0: render_line_mode0(); break; + case 1: render_line_mode1(); break; + case 2: render_line_mode2(); break; + case 3: render_line_mode3(); break; + case 4: render_line_mode4(); break; + case 5: render_line_mode5(); break; + case 6: render_line_mode6(); break; + case 7: render_line_mode7(); break; + } + + render_line_output(); +} + +#endif //ifdef BPPU_CPP diff --git a/bsnes/ppu/bppu/bppu_render.hpp b/bsnes/ppu/bppu/bppu_render.hpp new file mode 100755 index 0000000..5cf200b --- /dev/null +++ b/bsnes/ppu/bppu/bppu_render.hpp @@ -0,0 +1,97 @@ +//bppu_render.cpp +inline bool render_enabled(uint8 bg, uint8 pri); +inline void render_line_mode0(); +inline void render_line_mode1(); +inline void render_line_mode2(); +inline void render_line_mode3(); +inline void render_line_mode4(); +inline void render_line_mode5(); +inline void render_line_mode6(); +inline void render_line_mode7(); + +//bppu_render_cache.cpp +enum { COLORDEPTH_4 = 0, COLORDEPTH_16 = 1, COLORDEPTH_256 = 2 }; +enum { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 }; + +struct _pixel { + //bgr555 color data for main/subscreen pixels: 0x0000 = transparent / use palette color # 0 + //needs to be bgr555 instead of palette index for direct color mode ($2130 bit 0) to work + uint16 src_main, src_sub; + //indicates source of palette # for main/subscreen (BG1-4, OAM, or back) + uint8 bg_main, bg_sub; + //color_exemption -- true when bg == OAM && palette index >= 192, disables color add/sub effects + uint8 ce_main, ce_sub; + //priority level of src_n. to set src_n, + //the priority of the pixel must be >pri_n + uint8 pri_main, pri_sub; +} pixel_cache[256]; + +uint8 *bg_tiledata[3]; +uint8 *bg_tiledata_state[3]; //0 = valid, 1 = dirty + +void render_bg_tile(uint8 color_depth, uint16 tile_num); +inline void flush_pixel_cache(); +void alloc_tiledata_cache(); +void flush_tiledata_cache(); +void free_tiledata_cache(); + +//bppu_render_windows.cpp +struct _window { + uint8 main[256], sub[256]; +} window[6]; + +void build_window_table(uint8 bg, bool mainscreen); +void build_window_tables(uint8 bg); + +//bppu_render_bg.cpp +struct { + uint16 tw, th; //tile width, height + uint16 mx, my; //screen mask x, y + uint16 scx, scy; //sc index offsets +} bg_info[4]; + +void update_bg_info(); +uint16 bg_get_tile(uint8 bg, uint16 x, uint16 y); +void render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri1_pos); + +//bppu_render_oam.cpp +struct sprite_item { + uint8 width, height; + uint16 x, y; + uint8 character; + bool use_nameselect; + bool vflip, hflip; + uint8 palette; + uint8 priority; +} sprite_list[128], *spr; + +uint8 oam_itemlist[32]; +struct oam_tileitem { + uint16 x, y, pri, pal, tile; + bool hflip; +} oam_tilelist[34]; + +enum { OAM_PRI_NONE = 4 }; +uint8 oam_line_pal[256], oam_line_pri[256]; + +void build_sprite_list(); +bool is_sprite_on_scanline(); +void load_oam_tiles(); +void render_oam_tile(int tile_num); +void render_line_oam_rto(); +void render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos); +void render_line_oam_lores(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos); + +//bppu_render_mode7.cpp +void render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos); + +//bppu_render_addsub.cpp +inline uint16 addsub(uint32 x, uint32 y, bool halve); + +//bppu_render_line.cpp +inline uint16 get_palette(uint8 index); +inline uint16 get_direct_color(uint8 p, uint8 t); +inline uint16 get_pixel_normal(uint32 x); +inline uint16 get_pixel_swap(uint32 x); +void render_line_output(); +void render_line_clear(); diff --git a/bsnes/ppu/bppu/bppu_render_addsub.cpp b/bsnes/ppu/bppu/bppu_render_addsub.cpp new file mode 100755 index 0000000..447aa4e --- /dev/null +++ b/bsnes/ppu/bppu/bppu_render_addsub.cpp @@ -0,0 +1,25 @@ +#ifdef BPPU_CPP + +//color addition / subtraction +//thanks go to blargg for the optimized algorithms +inline uint16 bPPU::addsub(uint32 x, uint32 y, bool halve) { + if(!regs.color_mode) { + if(!halve) { + unsigned sum = x + y; + unsigned carry = (sum - ((x ^ y) & 0x0421)) & 0x8420; + return (sum - carry) | (carry - (carry >> 5)); + } else { + return (x + y - ((x ^ y) & 0x0421)) >> 1; + } + } else { + unsigned diff = x - y + 0x8420; + unsigned borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420; + if(!halve) { + return (diff - borrow) & (borrow - (borrow >> 5)); + } else { + return (((diff - borrow) & (borrow - (borrow >> 5))) & 0x7bde) >> 1; + } + } +} + +#endif diff --git a/bsnes/ppu/bppu/bppu_render_bg.cpp b/bsnes/ppu/bppu/bppu_render_bg.cpp new file mode 100755 index 0000000..ec1cdb0 --- /dev/null +++ b/bsnes/ppu/bppu/bppu_render_bg.cpp @@ -0,0 +1,211 @@ +#ifdef BPPU_CPP + +//called once at the start of every rendered scanline +void bPPU::update_bg_info() { + const unsigned hires = (regs.bg_mode == 5 || regs.bg_mode == 6); + const unsigned width = (!hires ? 256 : 512); + + for(unsigned bg = 0; bg < 4; bg++) { + bg_info[bg].th = (regs.bg_tilesize[bg] ? 4 : 3); + bg_info[bg].tw = (hires ? 4 : bg_info[bg].th); + + bg_info[bg].mx = (bg_info[bg].th == 4 ? (width << 1) : width); + bg_info[bg].my = bg_info[bg].mx; + if(regs.bg_scsize[bg] & 0x01) bg_info[bg].mx <<= 1; + if(regs.bg_scsize[bg] & 0x02) bg_info[bg].my <<= 1; + bg_info[bg].mx--; + bg_info[bg].my--; + + bg_info[bg].scy = (regs.bg_scsize[bg] & 0x02) ? (32 << 5) : 0; + bg_info[bg].scx = (regs.bg_scsize[bg] & 0x01) ? (32 << 5) : 0; + if(regs.bg_scsize[bg] == 3) bg_info[bg].scy <<= 1; + } +} + +uint16 bPPU::bg_get_tile(uint8 bg, uint16 x, uint16 y) { + x = (x & bg_info[bg].mx) >> bg_info[bg].tw; + y = (y & bg_info[bg].my) >> bg_info[bg].th; + + uint16 pos = ((y & 0x1f) << 5) + (x & 0x1f); + if(y & 0x20) pos += bg_info[bg].scy; + if(x & 0x20) pos += bg_info[bg].scx; + + const uint16 addr = regs.bg_scaddr[bg] + (pos << 1); + return memory::vram[addr] + (memory::vram[addr + 1] << 8); +} + +#define setpixel_main(x) \ + if(pixel_cache[x].pri_main < tile_pri) { \ + pixel_cache[x].pri_main = tile_pri; \ + pixel_cache[x].bg_main = bg; \ + pixel_cache[x].src_main = col; \ + pixel_cache[x].ce_main = false; \ + } + +#define setpixel_sub(x) \ + if(pixel_cache[x].pri_sub < tile_pri) { \ + pixel_cache[x].pri_sub = tile_pri; \ + pixel_cache[x].bg_sub = bg; \ + pixel_cache[x].src_sub = col; \ + pixel_cache[x].ce_sub = false; \ + } + +void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri1_pos) { + if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) { + return; + } + + //are layers disabled by user? + if(render_enabled(bg, 0) == false) pri0_pos = 0; + if(render_enabled(bg, 1) == false) pri1_pos = 0; + //nothing to render? + if(!pri0_pos && !pri1_pos) return; + + const bool bg_enabled = regs.bg_enabled[bg]; + const bool bgsub_enabled = regs.bgsub_enabled[bg]; + + const uint16 opt_valid_bit = (bg == BG1) ? 0x2000 : (bg == BG2) ? 0x4000 : 0x0000; + const uint8 bgpal_index = (regs.bg_mode == 0 ? (bg << 5) : 0); + + const uint8 pal_size = 2 << color_depth; //<<2 (*4), <<4 (*16), <<8 (*256) + const uint16 tile_mask = 0x0fff >> color_depth; //0x0fff, 0x07ff, 0x03ff + //4 + color_depth = >>(4-6) -- / {16, 32, 64 } bytes/tile + //index is a tile number count to add to base tile number + const unsigned tiledata_index = regs.bg_tdaddr[bg] >> (4 + color_depth); + + const uint8 *bg_td = bg_tiledata[color_depth]; + const uint8 *bg_td_state = bg_tiledata_state[color_depth]; + + const uint8 tile_width = bg_info[bg].tw; + const uint8 tile_height = bg_info[bg].th; + const uint16 mask_x = bg_info[bg].mx; //screen width mask + const uint16 mask_y = bg_info[bg].my; //screen height mask + + uint16 y = regs.bg_y[bg]; + uint16 hscroll = regs.bg_hofs[bg]; + uint16 vscroll = regs.bg_vofs[bg]; + + const unsigned hires = (regs.bg_mode == 5 || regs.bg_mode == 6); + const unsigned width = (!hires ? 256 : 512); + + if(hires) { + hscroll <<= 1; + if(regs.interlace) y = (y << 1) + ifield(); + } + + uint16 hval, vval; + uint16 tile_pri, tile_num; + uint8 pal_index, pal_num; + uint16 hoffset, voffset, opt_x, col; + bool mirror_x, mirror_y; + + const uint8 *tile_ptr; + const uint16 *mtable = mosaic_table[regs.mosaic_enabled[bg] ? regs.mosaic_size : 0]; + const bool is_opt_mode = (regs.bg_mode == 2 || regs.bg_mode == 4 || regs.bg_mode == 6); + const bool is_direct_color_mode = (regs.direct_color == true && bg == BG1 && (regs.bg_mode == 3 || regs.bg_mode == 4)); + + build_window_tables(bg); + const uint8 *wt_main = window[bg].main; + const uint8 *wt_sub = window[bg].sub; + + uint16 prev_x = 0xffff, prev_y = 0xffff, prev_optx = 0xffff; + for(uint16 x = 0; x < width; x++) { + hoffset = mtable[x] + hscroll; + voffset = y + vscroll; + + if(is_opt_mode) { + opt_x = (x + (hscroll & 7)); + + //tile 0 is unaffected by OPT mode... + if(opt_x >= 8) { + //cache tile data in hval, vval if possible + if((opt_x >> 3) != (prev_optx >> 3)) { + prev_optx = opt_x; + + hval = bg_get_tile(BG3, (opt_x - 8) + (regs.bg_hofs[BG3] & ~7), regs.bg_vofs[BG3]); + if(regs.bg_mode != 4) { + vval = bg_get_tile(BG3, (opt_x - 8) + (regs.bg_hofs[BG3] & ~7), regs.bg_vofs[BG3] + 8); + } + } + + if(regs.bg_mode == 4) { + if(hval & opt_valid_bit) { + if(!(hval & 0x8000)) { + hoffset = opt_x + (hval & ~7); + } else { + voffset = y + hval; + } + } + } else { + if(hval & opt_valid_bit) { + hoffset = opt_x + (hval & ~7); + } + if(vval & opt_valid_bit) { + voffset = y + vval; + } + } + } + } + + hoffset &= mask_x; + voffset &= mask_y; + + if((hoffset >> 3) != prev_x || (voffset >> 3) != prev_y) { + prev_x = (hoffset >> 3); + prev_y = (voffset >> 3); + + tile_num = bg_get_tile(bg, hoffset, voffset); //format = vhopppcc cccccccc + mirror_y = (tile_num & 0x8000); + mirror_x = (tile_num & 0x4000); + tile_pri = (tile_num & 0x2000) ? pri1_pos : pri0_pos; + pal_num = ((tile_num >> 10) & 7); + pal_index = bgpal_index + (pal_num << pal_size); + + if(tile_width == 4) { //16x16 horizontal tile mirroring + if((bool)(hoffset & 8) != mirror_x) tile_num++; + } + + if(tile_height == 4) { //16x16 vertical tile mirroring + if((bool)(voffset & 8) != mirror_y) tile_num += 16; + } + + tile_num &= 0x03ff; + tile_num += tiledata_index; + tile_num &= tile_mask; + + if(bg_td_state[tile_num] == 1) { + render_bg_tile(color_depth, tile_num); + } + + if(mirror_y) voffset ^= 7; //invert y tile pos + tile_ptr = bg_td + (tile_num * 64) + ((voffset & 7) * 8); + } + + if(mirror_x) hoffset ^= 7; //invert x tile pos + col = *(tile_ptr + (hoffset & 7)); + if(col) { + if(is_direct_color_mode) { + col = get_direct_color(pal_num, col); + } else { + col = get_palette(col + pal_index); + } + + if(!hires) { + if(bg_enabled == true && !wt_main[x]) { setpixel_main(x); } + if(bgsub_enabled == true && !wt_sub[x]) { setpixel_sub(x); } + } else { + int hx = x >> 1; + if(x & 1) { + if(bg_enabled == true && !wt_main[hx]) { setpixel_main(hx); } + } else { + if(bgsub_enabled == true && !wt_sub[hx]) { setpixel_sub(hx); } + } + } + } + } +} + +#undef setpixel_main +#undef setpixel_sub + +#endif //ifdef BPPU_CPP diff --git a/bsnes/ppu/bppu/bppu_render_cache.cpp b/bsnes/ppu/bppu/bppu_render_cache.cpp new file mode 100755 index 0000000..977b4b4 --- /dev/null +++ b/bsnes/ppu/bppu/bppu_render_cache.cpp @@ -0,0 +1,151 @@ +#ifdef BPPU_CPP + +#define render_bg_tile_line_2bpp(mask) \ + col = !!(d0 & mask) << 0; \ + col += !!(d1 & mask) << 1; \ + *dest++ = col + +#define render_bg_tile_line_4bpp(mask) \ + col = !!(d0 & mask) << 0; \ + col += !!(d1 & mask) << 1; \ + col += !!(d2 & mask) << 2; \ + col += !!(d3 & mask) << 3; \ + *dest++ = col + +#define render_bg_tile_line_8bpp(mask) \ + col = !!(d0 & mask) << 0; \ + col += !!(d1 & mask) << 1; \ + col += !!(d2 & mask) << 2; \ + col += !!(d3 & mask) << 3; \ + col += !!(d4 & mask) << 4; \ + col += !!(d5 & mask) << 5; \ + col += !!(d6 & mask) << 6; \ + col += !!(d7 & mask) << 7; \ + *dest++ = col + +void bPPU::render_bg_tile(uint8 color_depth, uint16 tile_num) { + uint8 mask, d0, d1, d2, d3, d4, d5, d6, d7, col; + int x, y; + uint32 pos; + uint8 *dest; + + switch(color_depth) { + case COLORDEPTH_4: { + dest = (uint8*)bg_tiledata[TILE_2BIT] + tile_num * 64; + pos = tile_num * 16; + y = 8; + while(y--) { + d0 = memory::vram[pos ]; + d1 = memory::vram[pos + 1]; + render_bg_tile_line_2bpp(0x80); + render_bg_tile_line_2bpp(0x40); + render_bg_tile_line_2bpp(0x20); + render_bg_tile_line_2bpp(0x10); + render_bg_tile_line_2bpp(0x08); + render_bg_tile_line_2bpp(0x04); + render_bg_tile_line_2bpp(0x02); + render_bg_tile_line_2bpp(0x01); + pos += 2; + } + bg_tiledata_state[TILE_2BIT][tile_num] = 0; + } break; + + case COLORDEPTH_16: { + dest = (uint8*)bg_tiledata[TILE_4BIT] + tile_num * 64; + pos = tile_num * 32; + y = 8; + while(y--) { + d0 = memory::vram[pos ]; + d1 = memory::vram[pos + 1]; + d2 = memory::vram[pos + 16]; + d3 = memory::vram[pos + 17]; + render_bg_tile_line_4bpp(0x80); + render_bg_tile_line_4bpp(0x40); + render_bg_tile_line_4bpp(0x20); + render_bg_tile_line_4bpp(0x10); + render_bg_tile_line_4bpp(0x08); + render_bg_tile_line_4bpp(0x04); + render_bg_tile_line_4bpp(0x02); + render_bg_tile_line_4bpp(0x01); + pos += 2; + } + bg_tiledata_state[TILE_4BIT][tile_num] = 0; + } break; + + case COLORDEPTH_256: { + dest = (uint8*)bg_tiledata[TILE_8BIT] + tile_num * 64; + pos = tile_num * 64; + y = 8; + while(y--) { + d0 = memory::vram[pos ]; + d1 = memory::vram[pos + 1]; + d2 = memory::vram[pos + 16]; + d3 = memory::vram[pos + 17]; + d4 = memory::vram[pos + 32]; + d5 = memory::vram[pos + 33]; + d6 = memory::vram[pos + 48]; + d7 = memory::vram[pos + 49]; + render_bg_tile_line_8bpp(0x80); + render_bg_tile_line_8bpp(0x40); + render_bg_tile_line_8bpp(0x20); + render_bg_tile_line_8bpp(0x10); + render_bg_tile_line_8bpp(0x08); + render_bg_tile_line_8bpp(0x04); + render_bg_tile_line_8bpp(0x02); + render_bg_tile_line_8bpp(0x01); + pos += 2; + } + bg_tiledata_state[TILE_8BIT][tile_num] = 0; + } break; + } +} + +#undef render_bg_tile_line_2bpp +#undef render_bg_tile_line_4bpp +#undef render_bg_tile_line_8bpp + +void bPPU::flush_pixel_cache() { + uint16 main = get_palette(0); + uint16 sub = (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6) + ? main + : regs.color_rgb; + + unsigned i = 255; + do { + pixel_cache[i].src_main = main; + pixel_cache[i].src_sub = sub; + pixel_cache[i].bg_main = BACK; + pixel_cache[i].bg_sub = BACK; + pixel_cache[i].ce_main = false; + pixel_cache[i].ce_sub = false; + pixel_cache[i].pri_main = 0; + pixel_cache[i].pri_sub = 0; + } while(i--); +} + +void bPPU::alloc_tiledata_cache() { + bg_tiledata[TILE_2BIT] = new(zeromemory) uint8_t[262144]; + bg_tiledata[TILE_4BIT] = new(zeromemory) uint8_t[131072]; + bg_tiledata[TILE_8BIT] = new(zeromemory) uint8_t[ 65536]; + bg_tiledata_state[TILE_2BIT] = new(zeromemory) uint8_t[ 4096]; + bg_tiledata_state[TILE_4BIT] = new(zeromemory) uint8_t[ 2048]; + bg_tiledata_state[TILE_8BIT] = new(zeromemory) uint8_t[ 1024]; +} + +//marks all tiledata cache entries as dirty +void bPPU::flush_tiledata_cache() { + for(unsigned i = 0; i < 4096; i++) bg_tiledata_state[TILE_2BIT][i] = 1; + for(unsigned i = 0; i < 2048; i++) bg_tiledata_state[TILE_4BIT][i] = 1; + for(unsigned i = 0; i < 1024; i++) bg_tiledata_state[TILE_8BIT][i] = 1; +} + +void bPPU::free_tiledata_cache() { + delete[] bg_tiledata[TILE_2BIT]; + delete[] bg_tiledata[TILE_4BIT]; + delete[] bg_tiledata[TILE_8BIT]; + delete[] bg_tiledata_state[TILE_2BIT]; + delete[] bg_tiledata_state[TILE_4BIT]; + delete[] bg_tiledata_state[TILE_8BIT]; +} + +#endif //ifdef BPPU_CPP diff --git a/bsnes/ppu/bppu/bppu_render_line.cpp b/bsnes/ppu/bppu/bppu_render_line.cpp new file mode 100755 index 0000000..47f734b --- /dev/null +++ b/bsnes/ppu/bppu/bppu_render_line.cpp @@ -0,0 +1,140 @@ +#ifdef BPPU_CPP + +inline uint16 bPPU::get_palette(uint8 index) { + unsigned addr = index << 1; + return memory::cgram[addr] + (memory::cgram[addr + 1] << 8); +} + +inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) { + //p = 00000bgr + //t = BBGGGRRR + //r = 0BBb00GGGg0RRRr0 + return ((t & 7) << 2) | ((p & 1) << 1) | + (((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) | + ((t >> 6) << 13) | ((p >> 2) << 12); +} + +inline uint16 bPPU::get_pixel_normal(uint32 x) { + _pixel *p = &pixel_cache[x]; + uint16 src_main, src_sub; + uint8 bg_sub; + src_main = p->src_main; + + if(!regs.addsub_mode) { + bg_sub = BACK; + src_sub = regs.color_rgb; + } else { + bg_sub = p->bg_sub; + src_sub = p->src_sub; + } + + if(!window[COL].main[x]) { + if(!window[COL].sub[x]) { + return 0x0000; + } + src_main = 0x0000; + } + + if(!p->ce_main && regs.color_enabled[p->bg_main] && window[COL].sub[x]) { + bool halve = false; + if(regs.color_halve && window[COL].main[x]) { + if(regs.addsub_mode && bg_sub == BACK); + else { + halve = true; + } + } + return addsub(src_main, src_sub, halve); + } + + return src_main; +} + +inline uint16 bPPU::get_pixel_swap(uint32 x) { + _pixel *p = &pixel_cache[x]; + uint16 src_main, src_sub; + uint8 bg_sub; + src_main = p->src_sub; + + if(!regs.addsub_mode) { + bg_sub = BACK; + src_sub = regs.color_rgb; + } else { + bg_sub = p->bg_main; + src_sub = p->src_main; + } + + if(!window[COL].main[x]) { + if(!window[COL].sub[x]) { + return 0x0000; + } + src_main = 0x0000; + } + + if(!p->ce_sub && regs.color_enabled[p->bg_sub] && window[COL].sub[x]) { + bool halve = false; + if(regs.color_halve && window[COL].main[x]) { + if(regs.addsub_mode && bg_sub == BACK); + else { + halve = true; + } + } + return addsub(src_main, src_sub, halve); + } + + return src_main; +} + +inline void bPPU::render_line_output() { + uint16 *ptr = (uint16*)output + (line * 1024) + + ((interlace() && ifield()) ? 512 : 0); + uint16 *luma_b = light_table_b [regs.display_brightness]; + uint16 *luma_gr = light_table_gr[regs.display_brightness]; + uint16 curr, prev; + + if(!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) { + if(regs.display_brightness == 15) { + for(int x = 0; x < 256; x++) { + *ptr++ = get_pixel_normal(x); + } + } else { + for(int x = 0; x < 256; x++) { + curr = get_pixel_normal(x); + *ptr++ = luma_b[curr >> 10] + luma_gr[curr & 0x3ff]; + } + } + } else { + if(regs.display_brightness == 15) { + for(int x = 0, prev = 0; x < 256; x++) { + curr = get_pixel_swap(x); + *ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1; + prev = curr; + + curr = get_pixel_normal(x); + *ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1; + prev = curr; + + } + } else { + for(int x = 0, prev = 0; x < 256; x++) { + curr = get_pixel_swap(x); + curr = luma_b[curr >> 10] + luma_gr[curr & 0x3ff]; + *ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1; + prev = curr; + + curr = get_pixel_normal(x); + curr = luma_b[curr >> 10] + luma_gr[curr & 0x3ff]; + *ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1; + prev = curr; + } + } + } +} + +inline void bPPU::render_line_clear() { + uint16 *ptr = (uint16*)output + (line * 1024) + + ((interlace() && ifield()) ? 512 : 0); + uint16 width = (!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) ? 256 : 512; + memset(ptr, 0, width * 2 * sizeof(uint16)); +} + +#endif //ifdef BPPU_CPP diff --git a/bsnes/ppu/bppu/bppu_render_mode7.cpp b/bsnes/ppu/bppu/bppu_render_mode7.cpp new file mode 100755 index 0000000..4145f64 --- /dev/null +++ b/bsnes/ppu/bppu/bppu_render_mode7.cpp @@ -0,0 +1,148 @@ +#ifdef BPPU_CPP + +/***** + * bsnes mode7 renderer + * + * base algorithm written by anomie + * bsnes implementation written by byuu + * + * supports mode 7 + extbg + rotate + zoom + + * direct color + scrolling + m7sel + windowing + mosaic + * interlace and pseudo-hires support are automatic via main rendering routine + *****/ + +//13-bit sign extend +//--s---vvvvvvvvvv -> ssssssvvvvvvvvvv +#define CLIP(x) ( ((x) & 0x2000) ? ( (x) | ~0x03ff) : ((x) & 0x03ff) ) + +void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) { + if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) return; + + //are layers disabled by user? + if(render_enabled(bg, 0) == false) pri0_pos = 0; + if(render_enabled(bg, 1) == false) pri1_pos = 0; + + //nothing to render? + if(!pri0_pos && !pri1_pos) return; + + int32 px, py; + int32 tx, ty, tile, palette; + + int32 a = sclip<16>(regs.m7a); + int32 b = sclip<16>(regs.m7b); + int32 c = sclip<16>(regs.m7c); + int32 d = sclip<16>(regs.m7d); + + int32 cx = sclip<13>(regs.m7x); + int32 cy = sclip<13>(regs.m7y); + int32 hofs = sclip<13>(regs.m7_hofs); + int32 vofs = sclip<13>(regs.m7_vofs); + + int _pri, _x; + bool _bg_enabled = regs.bg_enabled[bg]; + bool _bgsub_enabled = regs.bgsub_enabled[bg]; + + build_window_tables(bg); + uint8 *wt_main = window[bg].main; + uint8 *wt_sub = window[bg].sub; + + int32 y = (regs.mode7_vflip == false ? line : 255 - line); + + uint16 *mtable_x, *mtable_y; + if(bg == BG1) { + mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0]; + mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0]; + } else { //bg == BG2 + //Mode7 EXTBG BG2 uses BG1 mosaic enable to control vertical mosaic, + //and BG2 mosaic enable to control horizontal mosaic... + mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG2] == true) ? regs.mosaic_size : 0]; + mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0]; + } + + int32 psx = ((a * CLIP(hofs - cx)) & ~63) + ((b * CLIP(vofs - cy)) & ~63) + ((b * mtable_y[y]) & ~63) + (cx << 8); + int32 psy = ((c * CLIP(hofs - cx)) & ~63) + ((d * CLIP(vofs - cy)) & ~63) + ((d * mtable_y[y]) & ~63) + (cy << 8); + for(int32 x = 0; x < 256; x++) { + px = psx + (a * mtable_x[x]); + py = psy + (c * mtable_x[x]); + + //mask floating-point bits (low 8 bits) + px >>= 8; + py >>= 8; + + switch(regs.mode7_repeat) { + case 0: //screen repetition outside of screen area + case 1: { //same as case 0 + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = memory::vram[(ty * 128 + tx) << 1]; + palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } break; + case 2: { //palette color 0 outside of screen area + if(px < 0 || px > 1023 || py < 0 || py > 1023) { + palette = 0; + } else { + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = memory::vram[(ty * 128 + tx) << 1]; + palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } + } break; + case 3: { //character 0 repetition outside of screen area + if(px < 0 || px > 1023 || py < 0 || py > 1023) { + tile = 0; + } else { + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = memory::vram[(ty * 128 + tx) << 1]; + } + palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } break; + } + + if(bg == BG1) { + _pri = pri0_pos; + } else { + _pri = (palette >> 7) ? pri1_pos : pri0_pos; + palette &= 0x7f; + } + + if(!palette) continue; + + _x = (regs.mode7_hflip == false) ? (x) : (255 - x); + + uint32 col; + if(regs.direct_color == true && bg == BG1) { + //direct color mode does not apply to bg2, as it is only 128 colors... + col = get_direct_color(0, palette); + } else { + col = get_palette(palette); + } + + if(regs.bg_enabled[bg] == true && !wt_main[_x]) { + if(pixel_cache[_x].pri_main < _pri) { + pixel_cache[_x].pri_main = _pri; + pixel_cache[_x].bg_main = bg; + pixel_cache[_x].src_main = col; + pixel_cache[_x].ce_main = false; + } + } + if(regs.bgsub_enabled[bg] == true && !wt_sub[_x]) { + if(pixel_cache[_x].pri_sub < _pri) { + pixel_cache[_x].pri_sub = _pri; + pixel_cache[_x].bg_sub = bg; + pixel_cache[_x].src_sub = col; + pixel_cache[_x].ce_sub = false; + } + } + } +} + +#undef CLIP + +#endif //ifdef BPPU_CPP diff --git a/bsnes/ppu/bppu/bppu_render_oam.cpp b/bsnes/ppu/bppu/bppu_render_oam.cpp new file mode 100755 index 0000000..f0da95c --- /dev/null +++ b/bsnes/ppu/bppu/bppu_render_oam.cpp @@ -0,0 +1,224 @@ +#ifdef BPPU_CPP + +void bPPU::build_sprite_list() { + uint8 *tableA = memory::oam.handle(); + uint8 *tableB = memory::oam.handle() + 512; + + for(unsigned i = 0; i < 128; i++) { + unsigned x = !!(*tableB & (1 << ((i & 3) << 1))); //0x01, 0x04, 0x10, 0x40 + bool size = !!(*tableB & (2 << ((i & 3) << 1))); //0x02, 0x08, 0x20, 0x80 + + switch(cache.oam_basesize) { + case 0: sprite_list[i].width = (!size) ? 8 : 16; + sprite_list[i].height = (!size) ? 8 : 16; + break; + case 1: sprite_list[i].width = (!size) ? 8 : 32; + sprite_list[i].height = (!size) ? 8 : 32; + break; + case 2: sprite_list[i].width = (!size) ? 8 : 64; + sprite_list[i].height = (!size) ? 8 : 64; + break; + case 3: sprite_list[i].width = (!size) ? 16 : 32; + sprite_list[i].height = (!size) ? 16 : 32; + break; + case 4: sprite_list[i].width = (!size) ? 16 : 64; + sprite_list[i].height = (!size) ? 16 : 64; + break; + case 5: sprite_list[i].width = (!size) ? 32 : 64; + sprite_list[i].height = (!size) ? 32 : 64; + break; + case 6: sprite_list[i].width = (!size) ? 16 : 32; + sprite_list[i].height = (!size) ? 32 : 64; + if(regs.oam_interlace && !size) sprite_list[i].height = 16; + //32x64 height is not affected by oam_interlace setting + break; + case 7: sprite_list[i].width = (!size) ? 16 : 32; + sprite_list[i].height = (!size) ? 32 : 32; + if(regs.oam_interlace && !size) sprite_list[i].height = 16; + break; + } + + sprite_list[i].x = (x << 8) + tableA[0]; + sprite_list[i].y = (tableA[1] + 1) & 0xff; + sprite_list[i].character = tableA[2]; + sprite_list[i].vflip = !!(tableA[3] & 0x80); + sprite_list[i].hflip = !!(tableA[3] & 0x40); + sprite_list[i].priority = (tableA[3] >> 4) & 3; + sprite_list[i].palette = (tableA[3] >> 1) & 7; + sprite_list[i].use_nameselect = tableA[3] & 1; + + tableA += 4; + if((i & 3) == 3) tableB++; + } +} + +bool bPPU::is_sprite_on_scanline() { + //if sprite is entirely offscreen and doesn't wrap around to the left side of the screen, + //then it is not counted. this *should* be 256, and not 255, even though dot 256 is offscreen. + if(spr->x > 256 && (spr->x + spr->width - 1) < 512) return false; + + int spr_height = (regs.oam_interlace == false) ? (spr->height) : (spr->height >> 1); + if(line >= spr->y && line < (spr->y + spr_height)) return true; + if((spr->y + spr_height) >= 256 && line < ((spr->y + spr_height) & 255)) return true; + return false; +} + +void bPPU::load_oam_tiles() { + uint16 tile_width = spr->width >> 3; + int x = spr->x; + int y = (line - spr->y) & 0xff; + if(regs.oam_interlace == true) { + y <<= 1; + } + + if(spr->vflip == true) { + if(spr->width == spr->height) { + y = (spr->height - 1) - y; + } else { + y = (y < spr->width) ? ((spr->width - 1) - y) : (spr->width + ((spr->width - 1) - (y - spr->width))); + } + } + + if(regs.oam_interlace == true) { + y = (spr->vflip == false) ? (y + ifield()) : (y - ifield()); + } + + x &= 511; + y &= 255; + + uint16 tdaddr = cache.oam_tdaddr; + uint16 chrx = (spr->character ) & 15; + uint16 chry = (spr->character >> 4) & 15; + if(spr->use_nameselect == true) { + tdaddr += (256 * 32) + (cache.oam_nameselect << 13); + } + chry += (y >> 3); + chry &= 15; + chry <<= 4; + + for(unsigned tx = 0; tx < tile_width; tx++) { + unsigned sx = (x + (tx << 3)) & 511; + //ignore sprites that are offscreen, x==256 is a special case that loads all tiles in OBJ + if(x != 256 && sx >= 256 && (sx + 7) < 512) continue; + + if(regs.oam_tilecount++ > 34) break; + unsigned n = regs.oam_tilecount - 1; + oam_tilelist[n].x = sx; + oam_tilelist[n].y = y; + oam_tilelist[n].pri = spr->priority; + oam_tilelist[n].pal = 128 + (spr->palette << 4); + oam_tilelist[n].hflip = spr->hflip; + + unsigned mx = (spr->hflip == false) ? tx : ((tile_width - 1) - tx); + unsigned pos = tdaddr + ((chry + ((chrx + mx) & 15)) << 5); + oam_tilelist[n].tile = (pos >> 5) & 0x07ff; + } +} + +void bPPU::render_oam_tile(int tile_num) { + oam_tileitem *t = &oam_tilelist[tile_num]; + uint8 *oam_td = (uint8*)bg_tiledata[COLORDEPTH_16]; + uint8 *oam_td_state = (uint8*)bg_tiledata_state[COLORDEPTH_16]; + + if(oam_td_state[t->tile] == 1) { + render_bg_tile(COLORDEPTH_16, t->tile); + } + + unsigned sx = t->x; + uint8 *tile_ptr = (uint8*)oam_td + (t->tile << 6) + ((t->y & 7) << 3); + for(unsigned x = 0; x < 8; x++) { + sx &= 511; + if(sx < 256) { + unsigned col = *(tile_ptr + ((t->hflip == false) ? x : (7 - x))); + if(col) { + col += t->pal; + oam_line_pal[sx] = col; + oam_line_pri[sx] = t->pri; + } + } + sx++; + } +} + +void bPPU::render_line_oam_rto() { + build_sprite_list(); + + regs.oam_itemcount = 0; + regs.oam_tilecount = 0; + memset(oam_line_pri, OAM_PRI_NONE, 256); + memset(oam_itemlist, 0xff, 32); + for(int s = 0; s < 34; s++) oam_tilelist[s].tile = 0xffff; + + for(int s = 0; s < 128; s++) { + spr = &sprite_list[(s + regs.oam_firstsprite) & 127]; + if(is_sprite_on_scanline() == false) continue; + if(regs.oam_itemcount++ > 32) break; + oam_itemlist[regs.oam_itemcount - 1] = (s + regs.oam_firstsprite) & 127; + } + + for(int s = 31; s >= 0; s--) { + if(oam_itemlist[s] == 0xff) continue; + spr = &sprite_list[oam_itemlist[s]]; + load_oam_tiles(); + } + + regs.time_over |= (regs.oam_tilecount > 34); + regs.range_over |= (regs.oam_itemcount > 32); +} + +void bPPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) { + if(regs.bg_enabled[OAM] == false && regs.bgsub_enabled[OAM] == false) return; + + //are layers disabled by user? + if(render_enabled(OAM, 0) == false) pri0_pos = 0; + if(render_enabled(OAM, 1) == false) pri1_pos = 0; + if(render_enabled(OAM, 2) == false) pri2_pos = 0; + if(render_enabled(OAM, 3) == false) pri3_pos = 0; + //nothing to render? + if(!pri0_pos && !pri1_pos && !pri2_pos && !pri3_pos) return; + + for(int s = 0; s < 34; s++) { + if(oam_tilelist[s].tile == 0xffff) continue; + render_oam_tile(s); + } + + render_line_oam_lores(pri0_pos, pri1_pos, pri2_pos, pri3_pos); +} + +#define setpixel_main(x) \ + if(pixel_cache[x].pri_main < pri) { \ + pixel_cache[x].pri_main = pri; \ + pixel_cache[x].bg_main = OAM; \ + pixel_cache[x].src_main = get_palette(oam_line_pal[x]); \ + pixel_cache[x].ce_main = (oam_line_pal[x] < 192); \ + } +#define setpixel_sub(x) \ + if(pixel_cache[x].pri_sub < pri) { \ + pixel_cache[x].pri_sub = pri; \ + pixel_cache[x].bg_sub = OAM; \ + pixel_cache[x].src_sub = get_palette(oam_line_pal[x]); \ + pixel_cache[x].ce_sub = (oam_line_pal[x] < 192); \ + } + +void bPPU::render_line_oam_lores(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) { + bool bg_enabled = regs.bg_enabled[OAM]; + bool bgsub_enabled = regs.bgsub_enabled[OAM]; + + build_window_tables(OAM); + uint8 *wt_main = window[OAM].main; + uint8 *wt_sub = window[OAM].sub; + + int pri_tbl[4] = { pri0_pos, pri1_pos, pri2_pos, pri3_pos }; + for(int x = 0; x < 256; x++) { + if(oam_line_pri[x] == OAM_PRI_NONE) continue; + + int pri = pri_tbl[oam_line_pri[x]]; + if(bg_enabled == true && !wt_main[x]) { setpixel_main(x); } + if(bgsub_enabled == true && !wt_sub[x]) { setpixel_sub(x); } + } +} + +#undef setpixel_main +#undef setpixel_sub + +#endif //ifdef BPPU_CPP diff --git a/bsnes/ppu/bppu/bppu_render_windows.cpp b/bsnes/ppu/bppu/bppu_render_windows.cpp new file mode 100755 index 0000000..648603b --- /dev/null +++ b/bsnes/ppu/bppu/bppu_render_windows.cpp @@ -0,0 +1,99 @@ +#ifdef BPPU_CPP + +void bPPU::build_window_table(uint8 bg, bool mainscreen) { + uint8 set = 1, clr = 0; + uint8 *wtbl = (mainscreen == true) ? window[bg].main : window[bg].sub; + + if(bg != COL) { + if(mainscreen == true && regs.window_enabled[bg] == false) { + memset(wtbl, 0, 256); + return; + } + if(mainscreen == false && regs.sub_window_enabled[bg] == false) { + memset(wtbl, 0, 256); + return; + } + } else { + switch((mainscreen == true) ? regs.color_mask : regs.colorsub_mask) { + case 0: { //always + memset(wtbl, 1, 256); + } return; + + case 3: { //never + memset(wtbl, 0, 256); + } return; + + case 1: { //inside window only + set = 1; + clr = 0; + } break; + + case 2: { //outside window only + set = 0; + clr = 1; + } break; + } + } + + uint16 window1_left = regs.window1_left; + uint16 window1_right = regs.window1_right; + uint16 window2_left = regs.window2_left; + uint16 window2_right = regs.window2_right; + + if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == false) { + memset(wtbl, clr, 256); + return; + } + + if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == false) { + if(regs.window1_invert[bg] == true)swap(set, clr); + for(int x = 0; x < 256; x++) { + wtbl[x] = (x >= window1_left && x <= window1_right) ? set : clr; + } + return; + } + + if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == true) { + if(regs.window2_invert[bg] == true)swap(set, clr); + for(int x = 0; x < 256; x++) { + wtbl[x] = (x >= window2_left && x <= window2_right) ? set : clr; + } + return; + } + + //(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == true) + int w1_mask, w2_mask; //1 = masked, 0 = not masked + + for(int x = 0; x < 256; x++) { + w1_mask = (x >= window1_left && x <= window1_right); + if(regs.window1_invert[bg] == true)w1_mask = !w1_mask; + + w2_mask = (x >= window2_left && x <= window2_right); + if(regs.window2_invert[bg] == true)w2_mask = !w2_mask; + + switch(regs.window_mask[bg]) { + case 0: { //WINDOWMASK_OR: + wtbl[x] = ((w1_mask | w2_mask) == 1) ? set : clr; + } break; + + case 1: { //WINDOWMASK_AND: + wtbl[x] = ((w1_mask & w2_mask) == 1) ? set : clr; + } break; + + case 2: { //WINDOWMASK_XOR: + wtbl[x] = ((w1_mask ^ w2_mask) == 1) ? set : clr; + } break; + + case 3: { //WINDOWMASK_XNOR: + wtbl[x] = ((w1_mask ^ w2_mask) == 0) ? set : clr; + } break; + } + } +} + +void bPPU::build_window_tables(uint8 bg) { + build_window_table(bg, true); + build_window_table(bg, false); +} + +#endif //ifdef BPPU_CPP diff --git a/bsnes/ppu/counter.cpp b/bsnes/ppu/counter.cpp new file mode 100755 index 0000000..17bdce7 --- /dev/null +++ b/bsnes/ppu/counter.cpp @@ -0,0 +1,50 @@ +#ifdef PPU_CPP + +//wrappers to allow PPUcounter::tick()/tock() to be inlined +bool PPUcounter::region() const { return snes.region() == SNES::NTSC ? 0 : 1; } +bool PPUcounter::interlace() const { return ppu.interlace(); } +void PPUcounter::scanline() { cpu.scanline(); } + +//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(region() == 0 && interlace() == false && status.vcounter == 240 && status.field == 1) { + return (status.hcounter >> 2); + } else { + return (status.hcounter - ((status.hcounter > 1292) << 1) - ((status.hcounter > 1310) << 1)) >> 2; + } +} + +uint16 PPUcounter::lineclocks() const { + if(region() == 0 && interlace() == false && vcounter() == 240 && status.field == 1) return 1360; + return 1364; +} + +uint16 PPUcounter::ilineclocks() const { + if(region() == 0 && interlace() == false && ivcounter() == 240 && status.field == 1) return 1360; + return 1364; +} + +void PPUcounter::reset() { + status.field = 0; + status.vcounter = 0; + status.hcounter = 0; + + history.index = 0; + history.ppudiff = 0; + + for(unsigned i = 0; i < 2048; i++) { + history.field [i] = 0; + history.vcounter[i] = 0; + history.hcounter[i] = 0; + } +} + +#endif diff --git a/bsnes/ppu/counter.hpp b/bsnes/ppu/counter.hpp new file mode 100755 index 0000000..515f64e --- /dev/null +++ b/bsnes/ppu/counter.hpp @@ -0,0 +1,73 @@ +class PPUcounter { +public: + alwaysinline void tick() { + history.ppudiff += 2; + status.hcounter += 2; + + if(status.hcounter >= 1360 && status.hcounter == lineclocks()) { + status.hcounter = 0; + status.vcounter++; + if((region() == 0 && interlace() == false && status.vcounter == 262) + || (region() == 0 && interlace() == true && status.vcounter == 263) + || (region() == 0 && interlace() == true && status.vcounter == 262 && status.field == 1) + || (region() == 1 && interlace() == false && status.vcounter == 312) + || (region() == 1 && interlace() == true && status.vcounter == 313) + || (region() == 1 && interlace() == true && status.vcounter == 312 && status.field == 1) + ) { + status.vcounter = 0; + status.field = !status.field; + } + + scanline(); + } + + history.index = (history.index + 1) & 2047; + history.field [history.index] = status.field; + history.vcounter[history.index] = status.vcounter; + history.hcounter[history.index] = status.hcounter; + } + + alwaysinline void tock(unsigned clocks) { + history.ppudiff -= clocks; + } + + //timing information relative to S-CPU + alwaysinline bool field () const { return status.field; } + alwaysinline uint16 vcounter() const { return status.vcounter; } + alwaysinline uint16 hcounter() const { return status.hcounter; } + uint16 hdot() const; + uint16 lineclocks() const; + + //timing history information relative to S-CPU + alwaysinline bool field (unsigned offset) const { return history.field [(history.index - (offset >> 1)) & 2047]; } + alwaysinline uint16 vcounter(unsigned offset) const { return history.vcounter[(history.index - (offset >> 1)) & 2047]; } + alwaysinline uint16 hcounter(unsigned offset) const { return history.hcounter[(history.index - (offset >> 1)) & 2047]; } + + //timing information relative to S-PPU + alwaysinline bool ifield() const { return history.field [(history.index - (history.ppudiff >> 1)) & 2047]; } + alwaysinline uint16 ivcounter() const { return history.vcounter[(history.index - (history.ppudiff >> 1)) & 2047]; } + alwaysinline uint16 ihcounter() const { return history.hcounter[(history.index - (history.ppudiff >> 1)) & 2047]; } + uint16 ilineclocks() const; + + void reset(); + +private: + bool region() const; + bool interlace() const; + void scanline(); + + struct { + bool field; + uint16 vcounter; + uint16 hcounter; + } status; + + struct { + bool field[2048]; + uint16 vcounter[2048]; + uint16 hcounter[2048]; + + unsigned index; + signed ppudiff; + } history; +}; diff --git a/bsnes/ppu/ppu.cpp b/bsnes/ppu/ppu.cpp new file mode 100755 index 0000000..d933b67 --- /dev/null +++ b/bsnes/ppu/ppu.cpp @@ -0,0 +1,47 @@ +#include <../base.hpp> +#define PPU_CPP + +#include "counter.cpp" + +void PPU::enable_renderer(bool r) { status.render_output = r; } +bool PPU::renderer_enabled() { return status.render_output; } + +void PPU::frame() { + status.frame_executed = true; + + static int32 fr = 0, fe = 0; + static time_t prev, curr; + fe++; + if(status.render_output)fr++; + + time(&curr); + if(curr != prev) { + status.frames_updated = true; + status.frames_rendered = fr; + status.frames_executed = fe; + fr = fe = 0; + } + prev = curr; +} + +void PPU::power() { + ppu1_version = snes.config.ppu1.version; + ppu2_version = snes.config.ppu2.version; +} + +void PPU::reset() { + memset(output, 0, 512 * 480 * sizeof(uint16)); +} + +PPU::PPU() { + output = new(zeromemory) uint16[512 * 480]; + + status.render_output = true; + status.frames_updated = false; + status.frames_rendered = 0; + status.frames_executed = 0; +} + +PPU::~PPU() { + delete[] output; +} diff --git a/bsnes/ppu/ppu.hpp b/bsnes/ppu/ppu.hpp new file mode 100755 index 0000000..a6064ad --- /dev/null +++ b/bsnes/ppu/ppu.hpp @@ -0,0 +1,42 @@ +#include "counter.hpp" + +class PPU : public PPUcounter, public MMIO { +public: + virtual void enter() = 0; + + uint16 *output; + + struct { + bool render_output; + + bool frame_executed; + bool frames_updated; + unsigned frames_rendered; + unsigned frames_executed; + } status; + + //PPU1 version number + //* 1 is known + //* reported by $213e + uint8 ppu1_version; + + //PPU2 version number + //* 1 and 3 are known + //* reported by $213f + uint8 ppu2_version; + + virtual bool interlace() const = 0; + virtual bool overscan() const = 0; + virtual bool hires() const = 0; + + virtual void latch_counters() = 0; + + virtual void frame(); + virtual void power(); + virtual void reset(); + virtual void enable_renderer(bool r); + virtual bool renderer_enabled(); + + PPU(); + virtual ~PPU(); +}; diff --git a/bsnes/reader/filereader.cpp b/bsnes/reader/filereader.cpp new file mode 100755 index 0000000..4c97063 --- /dev/null +++ b/bsnes/reader/filereader.cpp @@ -0,0 +1,50 @@ +#ifdef READER_CPP + +#include "filereader.hpp" + +unsigned FileReader::size() { + return fp.size(); +} + +//This function will allocate memory even if open() fails. +//This is needed so that when SRAM files do not exist, the +//memory for the SRAM data will be allocated still. +//The memory is flushed to 0x00 when no file is opened. +uint8_t* FileReader::read(unsigned length) { + uint8_t *data = 0; + + if(length == 0) { + //read the entire file into RAM + data = new(zeromemory) uint8_t[fp.size()]; + if(fp.open()) fp.read(data, fp.size()); + } else if(length > fp.size()) { + //read the entire file into RAM, pad the rest with 0x00s + data = new(zeromemory) uint8_t[length]; + if(fp.open()) fp.read(data, fp.size()); + } else { //filesize >= length + //read only what was requested + data = new(zeromemory) uint8_t[length]; + if(fp.open()) fp.read(data, length); + } + + return data; +} + +bool FileReader::ready() { + return fp.open(); +} + +FileReader::FileReader(const char *fn) { + if(!fp.open(fn, file::mode_read)) return; + + if(fp.size() == 0) { + //empty file + fp.close(); + } +} + +FileReader::~FileReader() { + if(fp.open()) fp.close(); +} + +#endif //ifdef READER_CPP diff --git a/bsnes/reader/filereader.hpp b/bsnes/reader/filereader.hpp new file mode 100755 index 0000000..c48819c --- /dev/null +++ b/bsnes/reader/filereader.hpp @@ -0,0 +1,12 @@ +class FileReader : public Reader { +public: + unsigned size(); + uint8_t* read(unsigned length = 0); + bool ready(); + + FileReader(const char *fn); + ~FileReader(); + +private: + file fp; +}; diff --git a/bsnes/reader/gzreader.cpp b/bsnes/reader/gzreader.cpp new file mode 100755 index 0000000..3bed61d --- /dev/null +++ b/bsnes/reader/gzreader.cpp @@ -0,0 +1,85 @@ +#ifdef READER_CPP + +#include "gzreader.hpp" + +unsigned GZReader::size() { + return filesize; +} + +//This function will allocate memory even if open() fails. +//This is needed so that when SRAM files do not exist, the +//memory for the SRAM data will be allocated still. +//The memory is flushed to 0x00 when no file is opened. +uint8_t* GZReader::read(unsigned length) { + uint8_t *data = 0; + + if(length == 0) { + //read the entire file into RAM + data = new(zeromemory) uint8_t[filesize]; + if(gp) gzread(gp, data, filesize); + } else if(length > filesize) { + //read the entire file into RAM, pad the rest with 0x00s + data = new(zeromemory) uint8_t[length]; + if(gp) gzread(gp, data, filesize); + } else { //filesize >= length + //read only what was requested + data = new(zeromemory) uint8_t[length]; + if(gp) gzread(gp, data, length); + } + + return data; +} + +bool GZReader::ready() { + return (gp != 0); +} + +GZReader::GZReader(const char *fn) : gp(0) { + #if !defined(_WIN32) + fp = fopen(fn, "rb"); + #else + fp = _wfopen(utf16_t(fn), L"rb"); + #endif + if(!fp) return; + + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + + if(filesize < 4) { + //too small to be a valid GZ archive + fclose(fp); + fp = 0; + return; + } + + fseek(fp, -4, SEEK_END); + unsigned gzsize; + gzsize = fgetc(fp); + gzsize |= fgetc(fp) << 8; + gzsize |= fgetc(fp) << 16; + gzsize |= fgetc(fp) << 24; + fseek(fp, 0, SEEK_SET); + + //zlib does not support UTF-8 filenames on Windows, + //thus _wfopen() wrapper above + fileno() wrapper here. + gp = gzdopen(fileno(fp), "rb"); + if(!gp) return; + + if(gzdirect(gp) == false) filesize = gzsize; + + if(filesize == 0) { + //archive is empty + gzclose(gp); + gp = 0; + return; + } +} + +GZReader::~GZReader() { + if(gp) { + gzclose(gp); + gp = 0; + } +} + +#endif //ifdef READER_CPP diff --git a/bsnes/reader/gzreader.hpp b/bsnes/reader/gzreader.hpp new file mode 100755 index 0000000..f009fe1 --- /dev/null +++ b/bsnes/reader/gzreader.hpp @@ -0,0 +1,16 @@ +#include "zlib/zlib.h" + +class GZReader : public Reader { +private: + FILE *fp; + gzFile gp; + unsigned filesize; + +public: + unsigned size(); + uint8_t* read(unsigned length = 0); + bool ready(); + + GZReader(const char *fn); + ~GZReader(); +}; diff --git a/bsnes/reader/jma/7z.h b/bsnes/reader/jma/7z.h new file mode 100755 index 0000000..50e1f24 --- /dev/null +++ b/bsnes/reader/jma/7z.h @@ -0,0 +1,28 @@ +/* +Copyright (C) 2005-2007 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __7Z_H +#define __7Z_H + +#include "iiostrm.h" + +bool decompress_lzma_7z(ISequentialInStream& in, unsigned in_size, ISequentialOutStream& out, unsigned out_size) throw (); +bool decompress_lzma_7z(const unsigned char* in_data, unsigned in_size, unsigned char* out_data, unsigned out_size) throw (); + +#endif + diff --git a/bsnes/reader/jma/7zlzma.cpp b/bsnes/reader/jma/7zlzma.cpp new file mode 100755 index 0000000..b849d8d --- /dev/null +++ b/bsnes/reader/jma/7zlzma.cpp @@ -0,0 +1,50 @@ +/* +Copyright (C) 2005-2007 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "7z.h" + +#include "lzmadec.h" + +bool decompress_lzma_7z(ISequentialInStream& in, unsigned in_size, ISequentialOutStream& out, unsigned out_size) throw () +{ + try + { + NCompress::NLZMA::CDecoder cc; + + UINT64 in_size_l = in_size; + UINT64 out_size_l = out_size; + + if (cc.ReadCoderProperties(&in) != S_OK) { return(false); } + if (cc.Code(&in, &out, &in_size_l, &out_size_l) != S_OK) { return(false); } + if (out.size_get() != out_size || out.overflow_get()) { return(false); } + + return(true); + } + catch (...) + { + return(false); + } +} + +bool decompress_lzma_7z(const unsigned char* in_data, unsigned int in_size, unsigned char* out_data, unsigned int out_size) throw () +{ + ISequentialInStream_Array in(reinterpret_cast(in_data), in_size); + ISequentialOutStream_Array out(reinterpret_cast(out_data), out_size); + + return(decompress_lzma_7z(in, in_size, out, out_size)); +} diff --git a/bsnes/reader/jma/aribitcd.h b/bsnes/reader/jma/aribitcd.h new file mode 100755 index 0000000..1fb421b --- /dev/null +++ b/bsnes/reader/jma/aribitcd.h @@ -0,0 +1,73 @@ +#ifndef __COMPRESSION_BITCODER_H +#define __COMPRESSION_BITCODER_H + +#include "rngcoder.h" + +namespace NCompression { +namespace NArithmetic { + +const int kNumBitModelTotalBits = 11; +const UINT32 kBitModelTotal = (1 << kNumBitModelTotalBits); + +const int kNumMoveReducingBits = 2; + +///////////////////////////// +// CBitModel + +template +class CBitModel +{ +public: + UINT32 m_Probability; + void UpdateModel(UINT32 aSymbol) + { + /* + m_Probability -= (m_Probability + ((aSymbol - 1) & ((1 << aNumMoveBits) - 1))) >> aNumMoveBits; + m_Probability += (1 - aSymbol) << (kNumBitModelTotalBits - aNumMoveBits); + */ + if (aSymbol == 0) + m_Probability += (kBitModelTotal - m_Probability) >> aNumMoveBits; + else + m_Probability -= (m_Probability) >> aNumMoveBits; + } +public: + void Init() { m_Probability = kBitModelTotal / 2; } +}; + +template +class CBitDecoder: public CBitModel +{ +public: + UINT32 Decode(CRangeDecoder *aRangeDecoder) + { + UINT32 aNewBound = (aRangeDecoder->m_Range >> kNumBitModelTotalBits) * CBitModel::m_Probability; + if (aRangeDecoder->m_Code < aNewBound) + { + aRangeDecoder->m_Range = aNewBound; + CBitModel::m_Probability += (kBitModelTotal - CBitModel::m_Probability) >> aNumMoveBits; + if (aRangeDecoder->m_Range < kTopValue) + { + aRangeDecoder->m_Code = (aRangeDecoder->m_Code << 8) | aRangeDecoder->m_Stream.ReadByte(); + aRangeDecoder->m_Range <<= 8; + } + return 0; + } + else + { + aRangeDecoder->m_Range -= aNewBound; + aRangeDecoder->m_Code -= aNewBound; + CBitModel::m_Probability -= (CBitModel::m_Probability) >> aNumMoveBits; + if (aRangeDecoder->m_Range < kTopValue) + { + aRangeDecoder->m_Code = (aRangeDecoder->m_Code << 8) | aRangeDecoder->m_Stream.ReadByte(); + aRangeDecoder->m_Range <<= 8; + } + return 1; + } + } +}; + +}} + + +#endif diff --git a/bsnes/reader/jma/ariconst.h b/bsnes/reader/jma/ariconst.h new file mode 100755 index 0000000..751b2b7 --- /dev/null +++ b/bsnes/reader/jma/ariconst.h @@ -0,0 +1,29 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __ARICONST_H +#define __ARICONST_H + +#include "aribitcd.h" + + +typedef NCompression::NArithmetic::CRangeDecoder CMyRangeDecoder; +template class CMyBitDecoder: + public NCompression::NArithmetic::CBitDecoder {}; + +#endif diff --git a/bsnes/reader/jma/ariprice.h b/bsnes/reader/jma/ariprice.h new file mode 100755 index 0000000..ccc398e --- /dev/null +++ b/bsnes/reader/jma/ariprice.h @@ -0,0 +1,12 @@ +#ifndef __COMPRESSION_ARIPRICE_H +#define __COMPRESSION_ARIPRICE_H + +namespace NCompression { +namespace NArithmetic { + +const UINT32 kNumBitPriceShiftBits = 6; +const UINT32 kBitPrice = 1 << kNumBitPriceShiftBits; + +}} + +#endif diff --git a/bsnes/reader/jma/btreecd.h b/bsnes/reader/jma/btreecd.h new file mode 100755 index 0000000..acce366 --- /dev/null +++ b/bsnes/reader/jma/btreecd.h @@ -0,0 +1,126 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __BITTREECODER_H +#define __BITTREECODER_H + +#include "aribitcd.h" +#include "rcdefs.h" + + +////////////////////////// +// CBitTreeDecoder + +template +class CBitTreeDecoder +{ + CMyBitDecoder m_Models[1 << m_NumBitLevels]; +public: + void Init() + { + for(UINT32 i = 1; i < (1 << m_NumBitLevels); i++) + m_Models[i].Init(); + } + UINT32 Decode(CMyRangeDecoder *aRangeDecoder) + { + UINT32 aModelIndex = 1; + RC_INIT_VAR + for(UINT32 aBitIndex = m_NumBitLevels; aBitIndex > 0; aBitIndex--) + { + // aModelIndex = (aModelIndex << 1) + m_Models[aModelIndex].Decode(aRangeDecoder); + RC_GETBIT(aNumMoveBits, m_Models[aModelIndex].m_Probability, aModelIndex) + } + RC_FLUSH_VAR + return aModelIndex - (1 << m_NumBitLevels); + }; +}; + +//////////////////////////////// +// CReverseBitTreeDecoder + +template +class CReverseBitTreeDecoder2 +{ + CMyBitDecoder *m_Models; + UINT32 m_NumBitLevels; +public: + CReverseBitTreeDecoder2(): m_Models(0) { } + ~CReverseBitTreeDecoder2() { delete []m_Models; } + bool Create(UINT32 aNumBitLevels) + { + m_NumBitLevels = aNumBitLevels; + m_Models = new CMyBitDecoder[1 << aNumBitLevels]; + return (m_Models != 0); + } + void Init() + { + UINT32 aNumModels = 1 << m_NumBitLevels; + for(UINT32 i = 1; i < aNumModels; i++) + m_Models[i].Init(); + } + UINT32 Decode(CMyRangeDecoder *aRangeDecoder) + { + UINT32 aModelIndex = 1; + UINT32 aSymbol = 0; + RC_INIT_VAR + for(UINT32 aBitIndex = 0; aBitIndex < m_NumBitLevels; aBitIndex++) + { + // UINT32 aBit = m_Models[aModelIndex].Decode(aRangeDecoder); + // aModelIndex <<= 1; + // aModelIndex += aBit; + // aSymbol |= (aBit << aBitIndex); + RC_GETBIT2(aNumMoveBits, m_Models[aModelIndex].m_Probability, aModelIndex, ; , aSymbol |= (1 << aBitIndex)) + } + RC_FLUSH_VAR + return aSymbol; + }; +}; +//////////////////////////// +// CReverseBitTreeDecoder2 + +template +class CReverseBitTreeDecoder +{ + CMyBitDecoder m_Models[1 << m_NumBitLevels]; +public: + void Init() + { + for(UINT32 i = 1; i < (1 << m_NumBitLevels); i++) + m_Models[i].Init(); + } + UINT32 Decode(CMyRangeDecoder *aRangeDecoder) + { + UINT32 aModelIndex = 1; + UINT32 aSymbol = 0; + RC_INIT_VAR + for(UINT32 aBitIndex = 0; aBitIndex < m_NumBitLevels; aBitIndex++) + { + // UINT32 aBit = m_Models[aModelIndex].Decode(aRangeDecoder); + // aModelIndex <<= 1; + // aModelIndex += aBit; + // aSymbol |= (aBit << aBitIndex); + RC_GETBIT2(aNumMoveBits, m_Models[aModelIndex].m_Probability, aModelIndex, ; , aSymbol |= (1 << aBitIndex)) + } + RC_FLUSH_VAR + return aSymbol; + } +}; + + + +#endif diff --git a/bsnes/reader/jma/crc32.h b/bsnes/reader/jma/crc32.h new file mode 100755 index 0000000..876a7d3 --- /dev/null +++ b/bsnes/reader/jma/crc32.h @@ -0,0 +1,26 @@ +/* +Copyright (C) 2004-2007 NSRT Team ( http://nsrt.edgeemu.com ) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +version 2 as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef CRC32_H +#define CRC32_H + +namespace CRC32lib +{ + unsigned int CRC32(const unsigned char *, size_t, register unsigned int crc32 = 0xFFFFFFFF); +} + +#endif diff --git a/bsnes/reader/jma/iiostrm.cpp b/bsnes/reader/jma/iiostrm.cpp new file mode 100755 index 0000000..f271996 --- /dev/null +++ b/bsnes/reader/jma/iiostrm.cpp @@ -0,0 +1,132 @@ +/* +Copyright (C) 2005-2007 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "portable.h" +#include "iiostrm.h" +#include "crc32.h" + +HRESULT ISequentialInStream_Array::Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + if (aSize > size) + { + aSize = size; + } + + *aProcessedSize = aSize; + memcpy(aData, data, aSize); + size -= aSize; + data += aSize; + return(S_OK); +} + +HRESULT ISequentialOutStream_Array::Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + if (aSize > size) + { + overflow = true; + aSize = size; + } + + *aProcessedSize = aSize; + memcpy(data, aData, aSize); + size -= aSize; + data += aSize; + total += aSize; + return(S_OK); +} + +HRESULT ISequentialInStream_String::Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + if (aSize > data.size()) + { + aSize = data.size(); + } + + *aProcessedSize = aSize; + memcpy(aData, data.c_str(), aSize); + data.erase(0, aSize); + return(S_OK); +} + +HRESULT ISequentialOutStream_String::Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + *aProcessedSize = aSize; + data.append((const char *)aData, aSize); + total += aSize; + return(S_OK); +} + +HRESULT ISequentialInStream_Istream::Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + data.read((char *)aData, aSize); + *aProcessedSize = data.gcount(); + return(S_OK); +} + +HRESULT ISequentialOutStream_Ostream::Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + *aProcessedSize = aSize; + data.write((char *)aData, aSize); + total += aSize; + return(S_OK); +} + + + +HRESULT ISequentialInStreamCRC32_Array::Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + ISequentialInStream_Array::Read(aData, aSize, aProcessedSize); + crc32 = CRC32lib::CRC32((const unsigned char *)aData, *aProcessedSize, ~crc32); + return(S_OK); +} + +HRESULT ISequentialOutStreamCRC32_Array::Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + ISequentialOutStream_Array::Write(aData, aSize, aProcessedSize); + crc32 = CRC32lib::CRC32((const unsigned char *)aData, *aProcessedSize, ~crc32); + return(S_OK); +} + +HRESULT ISequentialInStreamCRC32_String::Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + ISequentialInStream_String::Read(aData, aSize, aProcessedSize); + crc32 = CRC32lib::CRC32((const unsigned char *)aData, *aProcessedSize, ~crc32); + return(S_OK); +} + +HRESULT ISequentialOutStreamCRC32_String::Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + ISequentialOutStream_String::Write(aData, aSize, aProcessedSize); + crc32 = CRC32lib::CRC32((const unsigned char *)aData, *aProcessedSize, ~crc32); + return(S_OK); +} + +HRESULT ISequentialInStreamCRC32_Istream::Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + ISequentialInStream_Istream::Read(aData, aSize, aProcessedSize); + crc32 = CRC32lib::CRC32((const unsigned char *)aData, *aProcessedSize, ~crc32); + return(S_OK); +} + +HRESULT ISequentialOutStreamCRC32_Ostream::Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize) +{ + ISequentialOutStream_Ostream::Write(aData, aSize, aProcessedSize); + crc32 = CRC32lib::CRC32((const unsigned char *)aData, *aProcessedSize, ~crc32); + return(S_OK); +} diff --git a/bsnes/reader/jma/iiostrm.h b/bsnes/reader/jma/iiostrm.h new file mode 100755 index 0000000..a5b2ab2 --- /dev/null +++ b/bsnes/reader/jma/iiostrm.h @@ -0,0 +1,210 @@ +/* +Copyright (C) 2005-2007 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __IINOUTSTREAMS_H +#define __IINOUTSTREAMS_H + +#include +#include + +#include "portable.h" + + +class ISequentialInStream +{ +public: + virtual HRESULT Read(void *, UINT32, UINT32 *) = 0; + + virtual ~ISequentialInStream() {} +}; + + +class ISequentialInStream_Array : public ISequentialInStream +{ + const char *data; + unsigned int size; +public: + ISequentialInStream_Array(const char *Adata, unsigned Asize) : data(Adata), size(Asize) { } + + HRESULT Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialInStream_Array() {} +}; + +class ISequentialInStream_String : public ISequentialInStream +{ + std::string& data; +public: + ISequentialInStream_String(std::string& Adata) : data(Adata) { } + + HRESULT Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialInStream_String() {} +}; + +class ISequentialInStream_Istream : public ISequentialInStream +{ + std::istream& data; +public: + ISequentialInStream_Istream(std::istream& Adata) : data(Adata) { } + + HRESULT Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialInStream_Istream() {} +}; + + + +class ISequentialOutStream +{ +public: + virtual bool overflow_get() const = 0; + virtual unsigned int size_get() const = 0; + + virtual HRESULT Write(const void *, UINT32, UINT32 *) = 0; + + virtual ~ISequentialOutStream() {} +}; + + +class ISequentialOutStream_Array : public ISequentialOutStream +{ + char *data; + unsigned int size; + bool overflow; + unsigned int total; +public: + ISequentialOutStream_Array(char *Adata, unsigned Asize) : data(Adata), size(Asize), overflow(false), total(0) { } + + bool overflow_get() const { return(overflow); } + unsigned int size_get() const { return(total); } + + HRESULT Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialOutStream_Array() {} +}; + +class ISequentialOutStream_String : public ISequentialOutStream +{ + std::string& data; + unsigned int total; +public: + ISequentialOutStream_String(std::string& Adata) : data(Adata), total(0) { } + + bool overflow_get() const { return(false); } + unsigned int size_get() const { return(total); } + + HRESULT Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialOutStream_String() {} +}; + + +class ISequentialOutStream_Ostream : public ISequentialOutStream +{ + std::ostream& data; + unsigned int total; +public: + ISequentialOutStream_Ostream(std::ostream& Adata) : data(Adata), total(0) { } + + bool overflow_get() const { return(false); } + unsigned int size_get() const { return(total); } + + HRESULT Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialOutStream_Ostream() {} +}; + + + +class ISequentialStreamCRC32 +{ +protected: + unsigned int crc32; +public: + ISequentialStreamCRC32() : crc32(0) {} + unsigned int crc32_get() const { return(crc32); } + + virtual ~ISequentialStreamCRC32() {} +}; + + +class ISequentialInStreamCRC32_Array : public ISequentialInStream_Array, public ISequentialStreamCRC32 +{ +public: + ISequentialInStreamCRC32_Array(const char *Adata, unsigned Asize) : ISequentialInStream_Array(Adata, Asize) { } + + HRESULT Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialInStreamCRC32_Array() {} +}; + +class ISequentialInStreamCRC32_String : public ISequentialInStream_String, public ISequentialStreamCRC32 +{ +public: + ISequentialInStreamCRC32_String(std::string& Adata) : ISequentialInStream_String(Adata) { } + + HRESULT Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialInStreamCRC32_String() {} +}; + +class ISequentialInStreamCRC32_Istream : public ISequentialInStream_Istream, public ISequentialStreamCRC32 +{ +public: + ISequentialInStreamCRC32_Istream(std::istream& Adata) : ISequentialInStream_Istream(Adata) { } + + HRESULT Read(void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialInStreamCRC32_Istream() {} +}; + + +class ISequentialOutStreamCRC32_Array : public ISequentialOutStream_Array, public ISequentialStreamCRC32 +{ +public: + ISequentialOutStreamCRC32_Array(char *Adata, unsigned Asize) : ISequentialOutStream_Array(Adata, Asize) { } + + HRESULT Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialOutStreamCRC32_Array() {} +}; + +class ISequentialOutStreamCRC32_String : public ISequentialOutStream_String, public ISequentialStreamCRC32 +{ +public: + ISequentialOutStreamCRC32_String(std::string& Adata) : ISequentialOutStream_String(Adata) { } + + HRESULT Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialOutStreamCRC32_String() {} +}; + + +class ISequentialOutStreamCRC32_Ostream : public ISequentialOutStream_Ostream, public ISequentialStreamCRC32 +{ +public: + ISequentialOutStreamCRC32_Ostream(std::ostream& Adata) : ISequentialOutStream_Ostream(Adata) { } + + HRESULT Write(const void *aData, UINT32 aSize, UINT32 *aProcessedSize); + + virtual ~ISequentialOutStreamCRC32_Ostream() {} +}; + +#endif diff --git a/bsnes/reader/jma/inbyte.cpp b/bsnes/reader/jma/inbyte.cpp new file mode 100755 index 0000000..c727a4b --- /dev/null +++ b/bsnes/reader/jma/inbyte.cpp @@ -0,0 +1,60 @@ +/* +Copyright (C) 2005-2007 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "inbyte.h" + +namespace NStream{ + +CInByte::CInByte(UINT32 aBufferSize): + m_BufferBase(0), + m_BufferSize(aBufferSize) +{ + m_BufferBase = new BYTE[m_BufferSize]; +} + +CInByte::~CInByte() +{ + delete []m_BufferBase; +} + +void CInByte::Init(ISequentialInStream *aStream) +{ + m_Stream = aStream; + m_ProcessedSize = 0; + m_Buffer = m_BufferBase; + m_BufferLimit = m_Buffer; + m_StreamWasExhausted = false; +} + +bool CInByte::ReadBlock() +{ + if (m_StreamWasExhausted) + return false; + m_ProcessedSize += (m_Buffer - m_BufferBase); + UINT32 aNumProcessedBytes; + HRESULT aResult = m_Stream->Read(m_BufferBase, m_BufferSize, &aNumProcessedBytes); + if (aResult != S_OK) + throw aResult; + m_Buffer = m_BufferBase; + m_BufferLimit = m_Buffer + aNumProcessedBytes; + m_StreamWasExhausted = (aNumProcessedBytes == 0); + return (!m_StreamWasExhausted); +} + +} diff --git a/bsnes/reader/jma/inbyte.h b/bsnes/reader/jma/inbyte.h new file mode 100755 index 0000000..53afa17 --- /dev/null +++ b/bsnes/reader/jma/inbyte.h @@ -0,0 +1,76 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __STREAM_INBYTE_H +#define __STREAM_INBYTE_H + +#include "iiostrm.h" + +namespace NStream { + +class CInByte +{ + UINT64 m_ProcessedSize; + BYTE *m_BufferBase; + UINT32 m_BufferSize; + BYTE *m_Buffer; + BYTE *m_BufferLimit; + ISequentialInStream* m_Stream; + bool m_StreamWasExhausted; + + bool ReadBlock(); + +public: + CInByte(UINT32 aBufferSize = 0x100000); + ~CInByte(); + + void Init(ISequentialInStream *aStream); + + bool ReadByte(BYTE &aByte) + { + if(m_Buffer >= m_BufferLimit) + if(!ReadBlock()) + return false; + aByte = *m_Buffer++; + return true; + } + BYTE ReadByte() + { + if(m_Buffer >= m_BufferLimit) + if(!ReadBlock()) + return 0x0; + return *m_Buffer++; + } + void ReadBytes(void *aData, UINT32 aSize, UINT32 &aProcessedSize) + { + for(aProcessedSize = 0; aProcessedSize < aSize; aProcessedSize++) + if (!ReadByte(((BYTE *)aData)[aProcessedSize])) + return; + } + bool ReadBytes(void *aData, UINT32 aSize) + { + UINT32 aProcessedSize; + ReadBytes(aData, aSize, aProcessedSize); + return (aProcessedSize == aSize); + } + UINT64 GetProcessedSize() const { return m_ProcessedSize + (m_Buffer - m_BufferBase); } +}; + +} + +#endif diff --git a/bsnes/reader/jma/jcrc32.cpp b/bsnes/reader/jma/jcrc32.cpp new file mode 100755 index 0000000..e3377d5 --- /dev/null +++ b/bsnes/reader/jma/jcrc32.cpp @@ -0,0 +1,80 @@ +/* +Copyright (C) 2004-2007 NSRT Team ( http://nsrt.edgeemu.com ) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +version 2 as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +namespace CRC32lib +{ + //Don't ask questions, this is the PKZip CRC32 table + const unsigned int crc32Table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; + + + //CRC32 for char arrays + unsigned int CRC32(const unsigned char *array, size_t size, register unsigned int crc32) + { + const unsigned char *end_p = array+size; + for (register const unsigned char *p = array; p < end_p; p++) + { + crc32 = ((crc32 >> 8) & 0x00FFFFFF) ^ crc32Table[(crc32 ^ *p) & 0xFF]; + } + + return(~crc32); + } +} diff --git a/bsnes/reader/jma/jma.cpp b/bsnes/reader/jma/jma.cpp new file mode 100755 index 0000000..87e0322 --- /dev/null +++ b/bsnes/reader/jma/jma.cpp @@ -0,0 +1,550 @@ +/* +Copyright (C) 2005-2007 NSRT Team ( http://nsrt.edgeemu.com ) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +version 2 as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include "jma.h" +using namespace std; + +#include "portable.h" +#include "7z.h" +#include "crc32.h" + +namespace JMA +{ + const char jma_magic[] = { 'J', 'M', 'A', 0, 'N' }; + const unsigned int jma_header_length = 5; + const unsigned char jma_version = 1; + const unsigned int jma_version_length = 1; + const unsigned int jma_total_header_length = jma_header_length + jma_version_length + UINT_SIZE; + + //Convert DOS/zip/JMA integer time to to time_t + time_t uint_to_time(unsigned short date, unsigned short time) + { + tm formatted_time; + + formatted_time.tm_mday = date & 0x1F; + formatted_time.tm_mon = ((date >> 5) & 0xF) - 1; + formatted_time.tm_year = ((date >> 9) & 0x7f) + 80; + formatted_time.tm_sec = (time & 0x1F) * 2; + formatted_time.tm_min = (time >> 5) & 0x3F; + formatted_time.tm_hour = (time >> 11) & 0x1F; + + return(mktime(&formatted_time)); + } + + + //Retreive the file block, what else? + void jma_open::retrieve_file_block() throw(jma_errors) + { + unsigned char uint_buffer[UINT_SIZE]; + unsigned char ushort_buffer[USHORT_SIZE]; + + //File block size is the last UINT in the file + stream.seekg(-UINT_SIZE,ios::end); + stream.read((char *)uint_buffer, UINT_SIZE); + size_t file_block_size = charp_to_uint(uint_buffer); + + //Currently at the end of the file, so that's the file size + size_t jma_file_size = stream.tellg(); + + //The file block can't be larger than the JMA file without it's header. + //This if can probably be improved + if (file_block_size >= jma_file_size-jma_total_header_length) + { + throw(JMA_BAD_FILE); + } + + //Seek to before file block so we can read the file block + stream.seekg(-((int)file_block_size+UINT_SIZE),ios::end); + + //This is needed if the file block is compressed + stringstream decompressed_file_block; + //Pointer to where to read file block from (file or decompressed buffer) + istream *file_block_stream; + + //Setup file info buffer and byte to read with + jma_file_info file_info; + char byte; + + stream.get(byte); + if (!byte) //If file block is compressed + { + //Compressed size isn't counting the byte we just read or the UINT for compressed size + size_t compressed_size = file_block_size - (1+UINT_SIZE); + + //Read decompressed size / true file block size + stream.read((char *)uint_buffer, UINT_SIZE); + file_block_size = charp_to_uint(uint_buffer); + + //Setup access methods for decompression + ISequentialInStream_Istream compressed_data(stream); + ISequentialOutStream_Ostream decompressed_data(decompressed_file_block); + + //Decompress the data + if (!decompress_lzma_7z(compressed_data, compressed_size, decompressed_data, file_block_size)) + { + throw(JMA_DECOMPRESS_FAILED); + } + + //Go to beginning, setup pointer to buffer + decompressed_file_block.seekg(0, ios::beg); + file_block_stream = &decompressed_file_block; + } + else + { + stream.putback(byte); //Putback byte, byte is part of filename, not compressed indicator + file_block_stream = &stream; + } + + + //Minimum file name length is 2 bytes, a char and a null + //Minimum comment length is 1 byte, a null + //There are currently 2 UINTs and 2 USHORTs per file + while (file_block_size >= 2+1+UINT_SIZE*2+USHORT_SIZE*2) //This does allow for a gap, but that's okay + { + //First stored in the file block is the file name null terminated + file_info.name = ""; + + file_block_stream->get(byte); + while (byte) + { + file_info.name += byte; + file_block_stream->get(byte); + } + + //There must be a file name or the file is bad + if (!file_info.name.length()) + { + throw(JMA_BAD_FILE); + } + + //Same trick as above for the comment + file_info.comment = ""; + + file_block_stream->get(byte); + while (byte) + { + file_info.comment += byte; + file_block_stream->get(byte); + } + + //Next is a UINT representing the file's size + file_block_stream->read((char *)uint_buffer, UINT_SIZE); + file_info.size = charp_to_uint(uint_buffer); + + //Followed by CRC32 + file_block_stream->read((char *)uint_buffer, UINT_SIZE); + file_info.crc32 = charp_to_uint(uint_buffer); + + //Special USHORT representation of file's date + file_block_stream->read((char *)ushort_buffer, USHORT_SIZE); + file_info.date = charp_to_ushort(ushort_buffer); + + //Special USHORT representation of file's time + file_block_stream->read((char *)ushort_buffer, USHORT_SIZE); + file_info.time = charp_to_ushort(ushort_buffer); + + file_info.buffer = 0; //Pointing to null till we decompress files + + files.push_back(file_info); //Put file info into our structure + + //Subtract size of the file info we just read + file_block_size -= file_info.name.length()+file_info.comment.length()+2+UINT_SIZE*2+USHORT_SIZE*2; + } + } + + //Constructor for opening JMA files for reading + jma_open::jma_open(const char *compressed_file_name) throw (jma_errors) + { + decompressed_buffer = 0; + compressed_buffer = 0; + + stream.open(compressed_file_name, ios::in | ios::binary); + if (!stream.is_open()) + { + throw(JMA_NO_OPEN); + } + + //Header is "JMA\0N" + unsigned char header[jma_header_length]; + stream.read((char *)header, jma_header_length); + if (memcmp(jma_magic, header, jma_header_length)) + { + throw(JMA_BAD_FILE); + } + + //Not the cleanest code but logical + stream.read((char *)header, 5); + if (*header <= jma_version) + { + chunk_size = charp_to_uint(header+1); //Chunk size is a UINT that follows version # + retrieve_file_block(); + } + else + { + throw(JMA_UNSUPPORTED_VERSION); + } + } + + //Destructor only has to close the stream if neccesary + jma_open::~jma_open() + { + if (stream.is_open()) + { + stream.close(); + } + } + + //Return a vector containing useful info about the files in the JMA + vector jma_open::get_files_info() + { + vector file_info_vector; + jma_public_file_info file_info; + + for (vector::iterator i = files.begin(); i != files.end(); i++) + { + file_info.name = i->name; + file_info.comment = i->comment; + file_info.size = i->size; + file_info.datetime = uint_to_time(i->date, i->time); + file_info.crc32 = i->crc32; + file_info_vector.push_back(file_info); + } + + return(file_info_vector); + } + + //Skip forward a given number of chunks + void jma_open::chunk_seek(unsigned int chunk_num) throw(jma_errors) + { + //Check the stream is open + if (!stream.is_open()) + { + throw(JMA_NO_OPEN); + } + + //Clear possible errors so the seek will work + stream.clear(); + + //Move forward over header + stream.seekg(jma_total_header_length, ios::beg); + + unsigned char int4_buffer[UINT_SIZE]; + + while (chunk_num--) + { + //Read in size of chunk + stream.read((char *)int4_buffer, UINT_SIZE); + + //Skip chunk plus it's CRC32 + stream.seekg(charp_to_uint(int4_buffer)+UINT_SIZE, ios::cur); + } + } + + //Return a vector of pointers to each file in the JMA, the buffer to hold all the files + //must be initilized outside. + vector jma_open::get_all_files(unsigned char *buffer) throw(jma_errors) + { + //If there's no stream we can't read from it, so exit + if (!stream.is_open()) + { + throw(JMA_NO_OPEN); + } + + //Seek to the first chunk + chunk_seek(0); + + //Set the buffer that decompressed data goes to + decompressed_buffer = buffer; + + //If the JMA is not solid + if (chunk_size) + { + unsigned char int4_buffer[UINT_SIZE]; + size_t size = get_total_size(files); + + //For each chunk in the file... + for (size_t remaining_size = size; remaining_size; remaining_size -= chunk_size) + { + //Read the compressed size + stream.read((char *)int4_buffer, UINT_SIZE); + size_t compressed_size = charp_to_uint(int4_buffer); + + //Allocate memory of the correct size to hold the compressed data in the JMA + //Throw error on failure as that is unrecoverable from + try + { + compressed_buffer = new unsigned char[compressed_size]; + } + catch (bad_alloc xa) + { + throw(JMA_NO_MEM_ALLOC); + } + + //Read all the compressed data in + stream.read((char *)compressed_buffer, compressed_size); + + //Read the expected CRC of compressed data from the file + stream.read((char *)int4_buffer, UINT_SIZE); + + //If it doesn't match, throw error and cleanup memory + if (CRC32lib::CRC32(compressed_buffer, compressed_size) != charp_to_uint(int4_buffer)) + { + delete[] compressed_buffer; + throw(JMA_BAD_FILE); + } + + //Decompress the data, cleanup memory on failure + if (!decompress_lzma_7z(compressed_buffer, compressed_size, + decompressed_buffer+size-remaining_size, + (remaining_size > chunk_size) ? chunk_size : remaining_size)) + { + delete[] compressed_buffer; + throw(JMA_DECOMPRESS_FAILED); + } + delete[] compressed_buffer; + + if (remaining_size <= chunk_size) //If we just decompressed the remainder + { + break; + } + } + } + else //Solidly compressed JMA + { + unsigned char int4_buffer[UINT_SIZE]; + + //Read the size of the compressed data + stream.read((char *)int4_buffer, UINT_SIZE); + size_t compressed_size = charp_to_uint(int4_buffer); + + //Get decompressed size + size_t size = get_total_size(files); + + //Setup access methods for decompression + ISequentialInStream_Istream compressed_data(stream); + ISequentialOutStream_Array decompressed_data(reinterpret_cast(decompressed_buffer), size); + + //Decompress the data + if (!decompress_lzma_7z(compressed_data, compressed_size, decompressed_data, size)) + { + throw(JMA_DECOMPRESS_FAILED); + } + + /* + //Allocate memory of the right size to hold the compressed data in the JMA + try + { + compressed_buffer = new unsigned char[compressed_size]; + } + catch (bad_alloc xa) + { + throw(JMA_NO_MEM_ALLOC); + } + + //Copy the compressed data into memory + stream.read((char *)compressed_buffer, compressed_size); + size_t size = get_total_size(files); + + //Read the CRC of the compressed data + stream.read((char *)int4_buffer, UINT_SIZE); + + //If it doesn't match, complain + if (CRC32lib::CRC32(compressed_buffer, compressed_size) != charp_to_uint(int4_buffer)) + { + delete[] compressed_buffer; + throw(JMA_BAD_FILE); + } + + //Decompress the data + if (!decompress_lzma_7z(compressed_buffer, compressed_size, decompressed_buffer, size)) + { + delete[] compressed_buffer; + throw(JMA_DECOMPRESS_FAILED); + } + delete[] compressed_buffer; + */ + } + + vector file_pointers; + size_t size = 0; + + //For each file, add it's pointer to the vector, size is pointer offset in the buffer + for (vector::iterator i = files.begin(); i != files.end(); i++) + { + i->buffer = decompressed_buffer+size; + file_pointers.push_back(decompressed_buffer+size); + size += i->size; + } + + //Return the vector of pointers + return(file_pointers); + } + + //Extracts the file with a given name found in the archive to the given buffer + void jma_open::extract_file(string& name, unsigned char *buffer) throw(jma_errors) + { + if (!stream.is_open()) + { + throw(JMA_NO_OPEN); + } + + size_t size_to_skip = 0; + size_t our_file_size = 0; + + //Search through the vector of file information + for (vector::iterator i = files.begin(); i != files.end(); i++) + { + if (i->name == name) + { + //Set the variable so we can tell we found it + our_file_size = i->size; + break; + } + + //Keep a running total of size + size_to_skip += i->size; + } + + if (!our_file_size) //File with the specified name was not found in the archive + { + throw(JMA_FILE_NOT_FOUND); + } + + //If the JMA only contains one file, we can skip a lot of overhead + if (files.size() == 1) + { + get_all_files(buffer); + return; + } + + if (chunk_size) //we are using non-solid archive.. + { + unsigned int chunks_to_skip = size_to_skip / chunk_size; + + //skip over requisite number of chunks + chunk_seek(chunks_to_skip); + + //Allocate memory for compressed and decompressed data + unsigned char *comp_buffer = 0, *decomp_buffer = 0; + try + { + //Compressed data size is <= non compressed size + unsigned char *combined_buffer = new unsigned char[chunk_size*2]; + comp_buffer = combined_buffer; + decomp_buffer = combined_buffer+chunk_size; + } + catch (bad_alloc xa) + { + throw(JMA_NO_MEM_ALLOC); + } + + size_t first_chunk_offset = size_to_skip % chunk_size; + unsigned char int4_buffer[UINT_SIZE]; + for (size_t i = 0; i < our_file_size;) + { + //Get size + stream.read((char *)int4_buffer, UINT_SIZE); + size_t compressed_size = charp_to_uint(int4_buffer); + + //Read all the compressed data in + stream.read((char *)comp_buffer, compressed_size); + + //Read the CRC of the compressed data + stream.read((char *)int4_buffer, UINT_SIZE); + + //If it doesn't match, complain + if (CRC32lib::CRC32(comp_buffer, compressed_size) != charp_to_uint(int4_buffer)) + { + delete[] comp_buffer; + throw(JMA_BAD_FILE); + } + + //Decompress chunk + if (!decompress_lzma_7z(comp_buffer, compressed_size, decomp_buffer, chunk_size)) + { + delete[] comp_buffer; + throw(JMA_DECOMPRESS_FAILED); + } + + size_t copy_amount = our_file_size-i > chunk_size-first_chunk_offset ? chunk_size-first_chunk_offset : our_file_size-i; + + memcpy(buffer+i, decomp_buffer+first_chunk_offset, copy_amount); + first_chunk_offset = 0; //Set to zero since this is only for the first iteration + i += copy_amount; + } + delete[] comp_buffer; + } + else //Solid JMA + { + unsigned char *decomp_buffer = 0; + try + { + decomp_buffer = new unsigned char[get_total_size(files)]; + } + catch (bad_alloc xa) + { + throw(JMA_NO_MEM_ALLOC); + } + + get_all_files(decomp_buffer); + + memcpy(buffer, decomp_buffer+size_to_skip, our_file_size); + + delete[] decomp_buffer; + } + } + + bool jma_open::is_solid() + { + return(chunk_size ? false : true); + } + + const char *jma_error_text(jma_errors error) + { + switch (error) + { + case JMA_NO_CREATE: + return("JMA could not be created"); + + case JMA_NO_MEM_ALLOC: + return("Memory for JMA could be allocated"); + + case JMA_NO_OPEN: + return("JMA could not be opened"); + + case JMA_BAD_FILE: + return("Invalid/Corrupt JMA"); + + case JMA_UNSUPPORTED_VERSION: + return("JMA version not supported"); + + case JMA_COMPRESS_FAILED: + return("JMA compression failed"); + + case JMA_DECOMPRESS_FAILED: + return("JMA decompression failed"); + + case JMA_FILE_NOT_FOUND: + return("File not found in JMA"); + } + return("Unknown error"); + } + +} + + diff --git a/bsnes/reader/jma/jma.h b/bsnes/reader/jma/jma.h new file mode 100755 index 0000000..2aaa5ca --- /dev/null +++ b/bsnes/reader/jma/jma.h @@ -0,0 +1,88 @@ +/* +Copyright (C) 2005-2007 NSRT Team ( http://nsrt.edgeemu.com ) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +version 2 as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef JMA_H +#define JMA_H + +#include +#include +#include +#include + +namespace JMA +{ + enum jma_errors { JMA_NO_CREATE, JMA_NO_MEM_ALLOC, JMA_NO_OPEN, JMA_BAD_FILE, + JMA_UNSUPPORTED_VERSION, JMA_COMPRESS_FAILED, JMA_DECOMPRESS_FAILED, + JMA_FILE_NOT_FOUND }; + + struct jma_file_info_base + { + std::string name; + std::string comment; + size_t size; + unsigned int crc32; + }; + + struct jma_public_file_info : jma_file_info_base + { + time_t datetime; + }; + + struct jma_file_info : jma_file_info_base + { + unsigned short date; + unsigned short time; + const unsigned char *buffer; + }; + + template + inline size_t get_total_size(std::vector& files) + { + size_t size = 0; + for (typename std::vector::iterator i = files.begin(); i != files.end(); i++) + { + size += i->size; //We do have a problem if this wraps around + } + + return(size); + } + + class jma_open + { + public: + jma_open(const char *) throw(jma_errors); + ~jma_open(); + + std::vector get_files_info(); + std::vector get_all_files(unsigned char *) throw(jma_errors); + void extract_file(std::string& name, unsigned char *) throw(jma_errors); + bool is_solid(); + + private: + std::ifstream stream; + std::vector files; + size_t chunk_size; + unsigned char *decompressed_buffer; + unsigned char *compressed_buffer; + + void chunk_seek(unsigned int) throw(jma_errors); + void retrieve_file_block() throw(jma_errors); + }; + + const char *jma_error_text(jma_errors); +} +#endif diff --git a/bsnes/reader/jma/lencoder.h b/bsnes/reader/jma/lencoder.h new file mode 100755 index 0000000..6f30e47 --- /dev/null +++ b/bsnes/reader/jma/lencoder.h @@ -0,0 +1,93 @@ +/* +Copyright (C) 2005-2007 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LENCODER_H +#define __LENCODER_H + +#include "btreecd.h" + +namespace NLength { + +const UINT32 kNumPosStatesBitsMax = 4; +const int kNumPosStatesMax = (1 << kNumPosStatesBitsMax); + + +const int kNumPosStatesBitsEncodingMax = 4; +const int kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax); + + +const int kNumMoveBits = 5; + +const int kNumLenBits = 3; +const int kNumLowSymbols = 1 << kNumLenBits; +const int kNumMidBits = 3; +const int kNumMidSymbols = 1 << kNumMidBits; + +const int kNumHighBits = 8; + +const int kNumSymbolsTotal = kNumLowSymbols + kNumMidSymbols + (1 << kNumHighBits); + +const int kNumSpecSymbols = kNumLowSymbols + kNumMidSymbols; + +class CDecoder +{ + CMyBitDecoder m_Choice; + CBitTreeDecoder m_LowCoder[kNumPosStatesMax]; + CMyBitDecoder m_Choice2; + CBitTreeDecoder m_MidCoder[kNumPosStatesMax]; + CBitTreeDecoder m_HighCoder; + UINT32 m_NumPosStates; +public: + void Create(UINT32 aNumPosStates) + { m_NumPosStates = aNumPosStates; } + void Init() + { + m_Choice.Init(); + for (UINT32 aPosState = 0; aPosState < m_NumPosStates; aPosState++) + { + m_LowCoder[aPosState].Init(); + m_MidCoder[aPosState].Init(); + } + m_Choice2.Init(); + m_HighCoder.Init(); + } + UINT32 Decode(CMyRangeDecoder *aRangeDecoder, UINT32 aPosState) + { + if(m_Choice.Decode(aRangeDecoder) == 0) + return m_LowCoder[aPosState].Decode(aRangeDecoder); + else + { + UINT32 aSymbol = kNumLowSymbols; + if(m_Choice2.Decode(aRangeDecoder) == 0) + aSymbol += m_MidCoder[aPosState].Decode(aRangeDecoder); + else + { + aSymbol += kNumMidSymbols; + aSymbol += m_HighCoder.Decode(aRangeDecoder); + } + return aSymbol; + } + } + +}; + +} + + +#endif diff --git a/bsnes/reader/jma/litcoder.h b/bsnes/reader/jma/litcoder.h new file mode 100755 index 0000000..639d6c5 --- /dev/null +++ b/bsnes/reader/jma/litcoder.h @@ -0,0 +1,122 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LITERALCODER_H +#define __LITERALCODER_H + +#include "aribitcd.h" +#include "rcdefs.h" + +namespace NLiteral { + +const int kNumMoveBits = 5; + +class CDecoder2 +{ + CMyBitDecoder m_Decoders[3][1 << 8]; +public: + void Init() + { + for (int i = 0; i < 3; i++) + for (int j = 1; j < (1 << 8); j++) + m_Decoders[i][j].Init(); + } + + BYTE DecodeNormal(CMyRangeDecoder *aRangeDecoder) + { + UINT32 aSymbol = 1; + RC_INIT_VAR + do + { + // aSymbol = (aSymbol << 1) | m_Decoders[0][aSymbol].Decode(aRangeDecoder); + RC_GETBIT(kNumMoveBits, m_Decoders[0][aSymbol].m_Probability, aSymbol) + } + while (aSymbol < 0x100); + RC_FLUSH_VAR + return aSymbol; + } + + BYTE DecodeWithMatchByte(CMyRangeDecoder *aRangeDecoder, BYTE aMatchByte) + { + UINT32 aSymbol = 1; + RC_INIT_VAR + do + { + UINT32 aMatchBit = (aMatchByte >> 7) & 1; + aMatchByte <<= 1; + // UINT32 aBit = m_Decoders[1 + aMatchBit][aSymbol].Decode(aRangeDecoder); + // aSymbol = (aSymbol << 1) | aBit; + UINT32 aBit; + RC_GETBIT2(kNumMoveBits, m_Decoders[1 + aMatchBit][aSymbol].m_Probability, aSymbol, + aBit = 0, aBit = 1) + if (aMatchBit != aBit) + { + while (aSymbol < 0x100) + { + // aSymbol = (aSymbol << 1) | m_Decoders[0][aSymbol].Decode(aRangeDecoder); + RC_GETBIT(kNumMoveBits, m_Decoders[0][aSymbol].m_Probability, aSymbol) + } + break; + } + } + while (aSymbol < 0x100); + RC_FLUSH_VAR + return aSymbol; + } +}; + +class CDecoder +{ + CDecoder2 *m_Coders; + UINT32 m_NumPrevBits; + UINT32 m_NumPosBits; + UINT32 m_PosMask; +public: + CDecoder(): m_Coders(0) {} + ~CDecoder() { Free(); } + void Free() + { + delete []m_Coders; + m_Coders = 0; + } + void Create(UINT32 aNumPosBits, UINT32 aNumPrevBits) + { + Free(); + m_NumPosBits = aNumPosBits; + m_PosMask = (1 << aNumPosBits) - 1; + m_NumPrevBits = aNumPrevBits; + UINT32 aNumStates = 1 << (m_NumPrevBits + m_NumPosBits); + m_Coders = new CDecoder2[aNumStates]; + } + void Init() + { + UINT32 aNumStates = 1 << (m_NumPrevBits + m_NumPosBits); + for (UINT32 i = 0; i < aNumStates; i++) + m_Coders[i].Init(); + } + UINT32 GetState(UINT32 aPos, BYTE aPrevByte) const + { return ((aPos & m_PosMask) << m_NumPrevBits) + (aPrevByte >> (8 - m_NumPrevBits)); } + BYTE DecodeNormal(CMyRangeDecoder *aRangeDecoder, UINT32 aPos, BYTE aPrevByte) + { return m_Coders[GetState(aPos, aPrevByte)].DecodeNormal(aRangeDecoder); } + BYTE DecodeWithMatchByte(CMyRangeDecoder *aRangeDecoder, UINT32 aPos, BYTE aPrevByte, BYTE aMatchByte) + { return m_Coders[GetState(aPos, aPrevByte)].DecodeWithMatchByte(aRangeDecoder, aMatchByte); } +}; + +} + +#endif diff --git a/bsnes/reader/jma/lzma.cpp b/bsnes/reader/jma/lzma.cpp new file mode 100755 index 0000000..d020ed2 --- /dev/null +++ b/bsnes/reader/jma/lzma.cpp @@ -0,0 +1,41 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "lzma.h" + +namespace NCompress { +namespace NLZMA { + +UINT32 kDistStart[kDistTableSizeMax]; + +static class CConstInit +{ +public: + CConstInit() + { + UINT32 aStartValue = 0; + int i; + for (i = 0; i < kDistTableSizeMax; i++) + { + kDistStart[i] = aStartValue; + aStartValue += (1 << kDistDirectBits[i]); + } + } +} g_ConstInit; + +}} diff --git a/bsnes/reader/jma/lzma.h b/bsnes/reader/jma/lzma.h new file mode 100755 index 0000000..949b70b --- /dev/null +++ b/bsnes/reader/jma/lzma.h @@ -0,0 +1,124 @@ +/* +Copyright (C) 2005-2007 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "lencoder.h" + +#ifndef __LZMA_H +#define __LZMA_H + +namespace NCompress { +namespace NLZMA { + +const UINT32 kNumRepDistances = 4; + +const BYTE kNumStates = 12; + +const BYTE kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; +const BYTE kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; +const BYTE kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; +const BYTE kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + +class CState +{ +public: + BYTE m_Index; + void Init() + { m_Index = 0; } + void UpdateChar() + { m_Index = kLiteralNextStates[m_Index]; } + void UpdateMatch() + { m_Index = kMatchNextStates[m_Index]; } + void UpdateRep() + { m_Index = kRepNextStates[m_Index]; } + void UpdateShortRep() + { m_Index = kShortRepNextStates[m_Index]; } +}; + +class CBaseCoder +{ +protected: + CState m_State; + BYTE m_PreviousByte; + bool m_PeviousIsMatch; + UINT32 m_RepDistances[kNumRepDistances]; + void Init() + { + m_State.Init(); + m_PreviousByte = 0; + m_PeviousIsMatch = false; + for(UINT32 i = 0 ; i < kNumRepDistances; i++) + m_RepDistances[i] = 0; + } +}; + +const int kNumPosSlotBits = 6; +const int kDicLogSizeMax = 28; +const int kDistTableSizeMax = kDicLogSizeMax * 2; + +extern UINT32 kDistStart[kDistTableSizeMax]; +const BYTE kDistDirectBits[kDistTableSizeMax] = +{ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, + 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, + 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26 +}; + +const UINT32 kNumLenToPosStates = 4; +inline UINT32 GetLenToPosState(UINT32 aLen) +{ + aLen -= 2; + if (aLen < kNumLenToPosStates) + return aLen; + return kNumLenToPosStates - 1; +} + +const int kMatchMinLen = 2; + +const int kMatchMaxLen = kMatchMinLen + NLength::kNumSymbolsTotal - 1; + +const int kNumAlignBits = 4; +const int kAlignTableSize = 1 << kNumAlignBits; +const UINT32 kAlignMask = (kAlignTableSize - 1); + +const int kStartPosModelIndex = 4; +const int kEndPosModelIndex = 14; +const int kNumPosModels = kEndPosModelIndex - kStartPosModelIndex; + +const int kNumFullDistances = 1 << (kEndPosModelIndex / 2); + + +const int kMainChoiceLiteralIndex = 0; +const int kMainChoiceMatchIndex = 1; + +const int kMatchChoiceDistanceIndex= 0; +const int kMatchChoiceRepetitionIndex = 1; + +const int kNumMoveBitsForMainChoice = 5; +const int kNumMoveBitsForPosCoders = 5; + +const int kNumMoveBitsForAlignCoders = 5; + +const int kNumMoveBitsForPosSlotCoder = 5; + +const int kNumLitPosStatesBitsEncodingMax = 4; +const int kNumLitContextBitsMax = 8; + +}} + +#endif diff --git a/bsnes/reader/jma/lzmadec.cpp b/bsnes/reader/jma/lzmadec.cpp new file mode 100755 index 0000000..ad6b570 --- /dev/null +++ b/bsnes/reader/jma/lzmadec.cpp @@ -0,0 +1,298 @@ +/* +Copyright (C) 2005-2007 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "portable.h" +#include "lzmadec.h" + +#define RETURN_E_OUTOFMEMORY_IF_FALSE(x) { if (!(x)) return E_OUTOFMEMORY; } + +namespace NCompress { +namespace NLZMA { + +HRESULT CDecoder::SetDictionarySize(UINT32 aDictionarySize) +{ + if (aDictionarySize > (1 << kDicLogSizeMax)) + return E_INVALIDARG; + + UINT32 aWindowReservSize = MyMax(aDictionarySize, UINT32(1 << 21)); + + if (m_DictionarySize != aDictionarySize) + { + m_OutWindowStream.Create(aDictionarySize, kMatchMaxLen, aWindowReservSize); + m_DictionarySize = aDictionarySize; + } + return S_OK; +} + +HRESULT CDecoder::SetLiteralProperties( + UINT32 aLiteralPosStateBits, UINT32 aLiteralContextBits) +{ + if (aLiteralPosStateBits > 8) + return E_INVALIDARG; + if (aLiteralContextBits > 8) + return E_INVALIDARG; + m_LiteralDecoder.Create(aLiteralPosStateBits, aLiteralContextBits); + return S_OK; +} + +HRESULT CDecoder::SetPosBitsProperties(UINT32 aNumPosStateBits) +{ + if (aNumPosStateBits > NLength::kNumPosStatesBitsMax) + return E_INVALIDARG; + UINT32 aNumPosStates = 1 << aNumPosStateBits; + m_LenDecoder.Create(aNumPosStates); + m_RepMatchLenDecoder.Create(aNumPosStates); + m_PosStateMask = aNumPosStates - 1; + return S_OK; +} + +CDecoder::CDecoder(): + m_DictionarySize((UINT32)-1) +{ + Create(); +} + +HRESULT CDecoder::Create() +{ + for(int i = 0; i < kNumPosModels; i++) + { + RETURN_E_OUTOFMEMORY_IF_FALSE( + m_PosDecoders[i].Create(kDistDirectBits[kStartPosModelIndex + i])); + } + return S_OK; +} + + +HRESULT CDecoder::Init(ISequentialInStream *anInStream, + ISequentialOutStream *anOutStream) +{ + m_RangeDecoder.Init(anInStream); + + m_OutWindowStream.Init(anOutStream); + + int i; + for(i = 0; i < kNumStates; i++) + { + for (UINT32 j = 0; j <= m_PosStateMask; j++) + { + m_MainChoiceDecoders[i][j].Init(); + m_MatchRepShortChoiceDecoders[i][j].Init(); + } + m_MatchChoiceDecoders[i].Init(); + m_MatchRepChoiceDecoders[i].Init(); + m_MatchRep1ChoiceDecoders[i].Init(); + m_MatchRep2ChoiceDecoders[i].Init(); + } + + m_LiteralDecoder.Init(); + + // m_RepMatchLenDecoder.Init(); + + for (i = 0; (UINT32) i < kNumLenToPosStates; i++) + m_PosSlotDecoder[i].Init(); + + for(i = 0; i < kNumPosModels; i++) + m_PosDecoders[i].Init(); + + m_LenDecoder.Init(); + m_RepMatchLenDecoder.Init(); + + m_PosAlignDecoder.Init(); + return S_OK; + +} + +HRESULT CDecoder::CodeReal(ISequentialInStream *anInStream, + ISequentialOutStream *anOutStream, + const UINT64 *anInSize, const UINT64 *anOutSize) +{ + if (anOutSize == NULL) + return E_INVALIDARG; + + Init(anInStream, anOutStream); + + CState aState; + aState.Init(); + bool aPeviousIsMatch = false; + BYTE aPreviousByte = 0; + UINT32 aRepDistances[kNumRepDistances]; + for(UINT32 i = 0 ; i < kNumRepDistances; i++) + aRepDistances[i] = 0; + + UINT64 aNowPos64 = 0; + UINT64 aSize = *anOutSize; + while(aNowPos64 < aSize) + { + UINT64 aNext = MyMin(aNowPos64 + (1 << 18), aSize); + while(aNowPos64 < aNext) + { + UINT32 aPosState = UINT32(aNowPos64) & m_PosStateMask; + if (m_MainChoiceDecoders[aState.m_Index][aPosState].Decode(&m_RangeDecoder) == (UINT32) kMainChoiceLiteralIndex) + { + // aCounts[0]++; + aState.UpdateChar(); + if(aPeviousIsMatch) + { + BYTE aMatchByte = m_OutWindowStream.GetOneByte(0 - aRepDistances[0] - 1); + aPreviousByte = m_LiteralDecoder.DecodeWithMatchByte(&m_RangeDecoder, + UINT32(aNowPos64), aPreviousByte, aMatchByte); + aPeviousIsMatch = false; + } + else + aPreviousByte = m_LiteralDecoder.DecodeNormal(&m_RangeDecoder, + UINT32(aNowPos64), aPreviousByte); + m_OutWindowStream.PutOneByte(aPreviousByte); + aNowPos64++; + } + else + { + aPeviousIsMatch = true; + UINT32 aDistance, aLen; + if(m_MatchChoiceDecoders[aState.m_Index].Decode(&m_RangeDecoder) == + (UINT32) kMatchChoiceRepetitionIndex) + { + if(m_MatchRepChoiceDecoders[aState.m_Index].Decode(&m_RangeDecoder) == 0) + { + if(m_MatchRepShortChoiceDecoders[aState.m_Index][aPosState].Decode(&m_RangeDecoder) == 0) + { + aState.UpdateShortRep(); + aPreviousByte = m_OutWindowStream.GetOneByte(0 - aRepDistances[0] - 1); + m_OutWindowStream.PutOneByte(aPreviousByte); + aNowPos64++; + // aCounts[3 + 4]++; + continue; + } + // aCounts[3 + 0]++; + aDistance = aRepDistances[0]; + } + else + { + if(m_MatchRep1ChoiceDecoders[aState.m_Index].Decode(&m_RangeDecoder) == 0) + { + aDistance = aRepDistances[1]; + aRepDistances[1] = aRepDistances[0]; + // aCounts[3 + 1]++; + } + else + { + if (m_MatchRep2ChoiceDecoders[aState.m_Index].Decode(&m_RangeDecoder) == 0) + { + // aCounts[3 + 2]++; + aDistance = aRepDistances[2]; + } + else + { + // aCounts[3 + 3]++; + aDistance = aRepDistances[3]; + aRepDistances[3] = aRepDistances[2]; + } + aRepDistances[2] = aRepDistances[1]; + aRepDistances[1] = aRepDistances[0]; + } + aRepDistances[0] = aDistance; + } + aLen = m_RepMatchLenDecoder.Decode(&m_RangeDecoder, aPosState) + kMatchMinLen; + // aCounts[aLen]++; + aState.UpdateRep(); + } + else + { + aLen = kMatchMinLen + m_LenDecoder.Decode(&m_RangeDecoder, aPosState); + aState.UpdateMatch(); + UINT32 aPosSlot = m_PosSlotDecoder[GetLenToPosState(aLen)].Decode(&m_RangeDecoder); + // aCounts[aPosSlot]++; + if (aPosSlot >= (UINT32) kStartPosModelIndex) + { + aDistance = kDistStart[aPosSlot]; + if (aPosSlot < (UINT32) kEndPosModelIndex) + aDistance += m_PosDecoders[aPosSlot - kStartPosModelIndex].Decode(&m_RangeDecoder); + else + { + aDistance += (m_RangeDecoder.DecodeDirectBits(kDistDirectBits[aPosSlot] - + kNumAlignBits) << kNumAlignBits); + aDistance += m_PosAlignDecoder.Decode(&m_RangeDecoder); + } + } + else + aDistance = aPosSlot; + + + aRepDistances[3] = aRepDistances[2]; + aRepDistances[2] = aRepDistances[1]; + aRepDistances[1] = aRepDistances[0]; + + aRepDistances[0] = aDistance; + // UpdateStat(aLen, aPosSlot); + } + if (aDistance >= aNowPos64) + throw E_INVALIDDATA; + m_OutWindowStream.CopyBackBlock(aDistance, aLen); + aNowPos64 += aLen; + aPreviousByte = m_OutWindowStream.GetOneByte(0 - 1); + } + } + } + return Flush(); +} + +HRESULT CDecoder::Code(ISequentialInStream *anInStream, ISequentialOutStream *anOutStream, const UINT64 *anInSize, const UINT64 *anOutSize) +{ + try { + return CodeReal(anInStream, anOutStream, anInSize, anOutSize); + } catch (HRESULT& e) { + return e; + } catch (...) { + return E_FAIL; + } +} + +HRESULT CDecoder::ReadCoderProperties(ISequentialInStream *anInStream) +{ + UINT32 aNumPosStateBits; + UINT32 aLiteralPosStateBits; + UINT32 aLiteralContextBits; + UINT32 aDictionarySize; + + UINT32 aProcessesedSize; + + BYTE aByte; + RETURN_IF_NOT_S_OK(anInStream->Read(&aByte, sizeof(aByte), &aProcessesedSize)); + if (aProcessesedSize != sizeof(aByte)) + return E_INVALIDARG; + + aLiteralContextBits = aByte % 9; + BYTE aRemainder = aByte / 9; + aLiteralPosStateBits = aRemainder % 5; + aNumPosStateBits = aRemainder / 5; + + UINT8 uint_buffer[UINT_SIZE]; + RETURN_IF_NOT_S_OK(anInStream->Read(uint_buffer, sizeof(aDictionarySize), &aProcessesedSize)); + aDictionarySize = charp_to_uint(uint_buffer); + + if (aProcessesedSize != sizeof(aDictionarySize)) + return E_INVALIDARG; + + RETURN_IF_NOT_S_OK(SetDictionarySize(aDictionarySize)); + RETURN_IF_NOT_S_OK(SetLiteralProperties(aLiteralPosStateBits, aLiteralContextBits)); + RETURN_IF_NOT_S_OK(SetPosBitsProperties(aNumPosStateBits)); + + return S_OK; +} + +}} diff --git a/bsnes/reader/jma/lzmadec.h b/bsnes/reader/jma/lzmadec.h new file mode 100755 index 0000000..bb91912 --- /dev/null +++ b/bsnes/reader/jma/lzmadec.h @@ -0,0 +1,82 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LZARITHMETIC_DECODER_H +#define __LZARITHMETIC_DECODER_H + +#include "winout.h" +#include "lzma.h" +#include "lencoder.h" +#include "litcoder.h" + +namespace NCompress { +namespace NLZMA { + +typedef CMyBitDecoder CMyBitDecoder2; + +class CDecoder +{ + NStream::NWindow::COut m_OutWindowStream; + CMyRangeDecoder m_RangeDecoder; + + CMyBitDecoder2 m_MainChoiceDecoders[kNumStates][NLength::kNumPosStatesMax]; + CMyBitDecoder2 m_MatchChoiceDecoders[kNumStates]; + CMyBitDecoder2 m_MatchRepChoiceDecoders[kNumStates]; + CMyBitDecoder2 m_MatchRep1ChoiceDecoders[kNumStates]; + CMyBitDecoder2 m_MatchRep2ChoiceDecoders[kNumStates]; + CMyBitDecoder2 m_MatchRepShortChoiceDecoders[kNumStates][NLength::kNumPosStatesMax]; + + CBitTreeDecoder m_PosSlotDecoder[kNumLenToPosStates]; + + CReverseBitTreeDecoder2 m_PosDecoders[kNumPosModels]; + CReverseBitTreeDecoder m_PosAlignDecoder; + // CBitTreeDecoder2 m_PosDecoders[kNumPosModels]; + // CBitTreeDecoder m_PosAlignDecoder; + + NLength::CDecoder m_LenDecoder; + NLength::CDecoder m_RepMatchLenDecoder; + + NLiteral::CDecoder m_LiteralDecoder; + + UINT32 m_DictionarySize; + + UINT32 m_PosStateMask; + + HRESULT Create(); + + HRESULT Init(ISequentialInStream *anInStream, ISequentialOutStream *anOutStream); + + HRESULT Flush() { return m_OutWindowStream.Flush(); } + + HRESULT CodeReal(ISequentialInStream *anInStream, ISequentialOutStream *anOutStream, const UINT64 *anInSize, const UINT64 *anOutSize); + +public: + + CDecoder(); + + HRESULT Code(ISequentialInStream *anInStream, ISequentialOutStream *anOutStream, const UINT64 *anInSize, const UINT64 *anOutSize); + HRESULT ReadCoderProperties(ISequentialInStream *anInStream); + + HRESULT SetDictionarySize(UINT32 aDictionarySize); + HRESULT SetLiteralProperties(UINT32 aLiteralPosStateBits, UINT32 aLiteralContextBits); + HRESULT SetPosBitsProperties(UINT32 aNumPosStateBits); +}; + +}} + +#endif diff --git a/bsnes/reader/jma/portable.h b/bsnes/reader/jma/portable.h new file mode 100755 index 0000000..12416c7 --- /dev/null +++ b/bsnes/reader/jma/portable.h @@ -0,0 +1,83 @@ +/* +Copyright (C) 2004-2007 NSRT Team ( http://nsrt.edgeemu.com ) +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +version 2 as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __PORTABLE_H +#define __PORTABLE_H + +#include + +typedef signed char INT8; +typedef unsigned char UINT8; +typedef short INT16; +typedef unsigned short UINT16; +typedef long INT32; +typedef unsigned long UINT32; +typedef long long INT64; +typedef unsigned long long UINT64; + +typedef UINT8 BYTE; +typedef UINT16 WORD; +typedef UINT32 DWORD; + +typedef unsigned UINT_PTR; + +typedef int BOOL; +#define FALSE 0 +#define TRUE 1 + +#define HRESULT int +#define S_OK 0 +#define E_INVALIDARG -1 +#define E_OUTOFMEMORY -2 +#define E_FAIL -3 +#define E_INTERNAL_ERROR -4 +#define E_INVALIDDATA -5 + +template inline T MyMin(T a, T b) { + return a < b ? a : b; +} + +template inline T MyMax(T a, T b) { + return a > b ? a : b; +} + +#define RETURN_IF_NOT_S_OK(x) { HRESULT __aResult_ = (x); if(__aResult_ != S_OK) return __aResult_; } + + +#define UINT_SIZE (4) +#define USHORT_SIZE (2) + +//Convert an array of 4 bytes back into an integer +inline unsigned int charp_to_uint(const unsigned char buffer[UINT_SIZE]) +{ + unsigned int num = (unsigned int)buffer[3]; + num |= ((unsigned int)buffer[2]) << 8; + num |= ((unsigned int)buffer[1]) << 16; + num |= ((unsigned int)buffer[0]) << 24; + return(num); +} + +//Convert an array of 2 bytes back into a short integer +inline unsigned short charp_to_ushort(const unsigned char buffer[USHORT_SIZE]) +{ + unsigned short num = (unsigned short)buffer[1]; + num |= ((unsigned short)buffer[0]) << 8; + return(num); +} + +#endif diff --git a/bsnes/reader/jma/rcdefs.h b/bsnes/reader/jma/rcdefs.h new file mode 100755 index 0000000..6106b57 --- /dev/null +++ b/bsnes/reader/jma/rcdefs.h @@ -0,0 +1,60 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __RCDEFS_H +#define __RCDEFS_H + +#include "aribitcd.h" +#include "ariconst.h" + +#define RC_INIT_VAR \ + UINT32 aRange = aRangeDecoder->m_Range; \ + UINT32 aCode = aRangeDecoder->m_Code; + +#define RC_FLUSH_VAR \ + aRangeDecoder->m_Range = aRange; \ + aRangeDecoder->m_Code = aCode; + +#define RC_NORMALIZE \ + if (aRange < NCompression::NArithmetic::kTopValue) \ + { \ + aCode = (aCode << 8) | aRangeDecoder->m_Stream.ReadByte(); \ + aRange <<= 8; } + +#define RC_GETBIT2(aNumMoveBits, aProb, aModelIndex, Action0, Action1) \ + {UINT32 aNewBound = (aRange >> NCompression::NArithmetic::kNumBitModelTotalBits) * aProb; \ + if (aCode < aNewBound) \ + { \ + Action0; \ + aRange = aNewBound; \ + aProb += (NCompression::NArithmetic::kBitModelTotal - aProb) >> aNumMoveBits; \ + aModelIndex <<= 1; \ + } \ + else \ + { \ + Action1; \ + aRange -= aNewBound; \ + aCode -= aNewBound; \ + aProb -= (aProb) >> aNumMoveBits; \ + aModelIndex = (aModelIndex << 1) + 1; \ + }} \ + RC_NORMALIZE + +#define RC_GETBIT(aNumMoveBits, aProb, aModelIndex) RC_GETBIT2(aNumMoveBits, aProb, aModelIndex, ; , ;) + +#endif diff --git a/bsnes/reader/jma/rngcoder.h b/bsnes/reader/jma/rngcoder.h new file mode 100755 index 0000000..711c2de --- /dev/null +++ b/bsnes/reader/jma/rngcoder.h @@ -0,0 +1,143 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __COMPRESSION_RANGECODER_H +#define __COMPRESSION_RANGECODER_H + +#include "inbyte.h" + +namespace NCompression { +namespace NArithmetic { + +const UINT32 kNumTopBits = 24; +const UINT32 kTopValue = (1 << kNumTopBits); + +class CRangeDecoder +{ +public: + NStream::CInByte m_Stream; + UINT32 m_Range; + UINT32 m_Code; + UINT32 m_Word; + void Normalize() + { + while (m_Range < kTopValue) + { + m_Code = (m_Code << 8) | m_Stream.ReadByte(); + m_Range <<= 8; + } + } + + void Init(ISequentialInStream *aStream) + { + m_Stream.Init(aStream); + m_Code = 0; + m_Range = UINT32(-1); + for(int i = 0; i < 5; i++) + m_Code = (m_Code << 8) | m_Stream.ReadByte(); + } + + UINT32 GetThreshold(UINT32 aTotal) + { + return (m_Code) / ( m_Range /= aTotal); + } + + void Decode(UINT32 aStart, UINT32 aSize, UINT32 aTotal) + { + m_Code -= aStart * m_Range; + m_Range *= aSize; + Normalize(); + } + + /* + UINT32 DecodeDirectBitsDiv(UINT32 aNumTotalBits) + { + m_Range >>= aNumTotalBits; + UINT32 aThreshold = m_Code / m_Range; + m_Code -= aThreshold * m_Range; + + Normalize(); + return aThreshold; + } + + UINT32 DecodeDirectBitsDiv2(UINT32 aNumTotalBits) + { + if (aNumTotalBits <= kNumBottomBits) + return DecodeDirectBitsDiv(aNumTotalBits); + UINT32 aResult = DecodeDirectBitsDiv(aNumTotalBits - kNumBottomBits) << kNumBottomBits; + return (aResult | DecodeDirectBitsDiv(kNumBottomBits)); + } + */ + + UINT32 DecodeDirectBits(UINT32 aNumTotalBits) + { + UINT32 aRange = m_Range; + UINT32 aCode = m_Code; + UINT32 aResult = 0; + for (UINT32 i = aNumTotalBits; i > 0; i--) + { + aRange >>= 1; + /* + aResult <<= 1; + if (aCode >= aRange) + { + aCode -= aRange; + aResult |= 1; + } + */ + UINT32 t = (aCode - aRange) >> 31; + aCode -= aRange & (t - 1); + // aRange = aRangeTmp + ((aRange & 1) & (1 - t)); + aResult = (aResult << 1) | (1 - t); + + if (aRange < kTopValue) + { + aCode = (aCode << 8) | m_Stream.ReadByte(); + aRange <<= 8; + } + } + m_Range = aRange; + m_Code = aCode; + return aResult; + } + + UINT32 DecodeBit(UINT32 aSize0, UINT32 aNumTotalBits) + { + UINT32 aNewBound = (m_Range >> aNumTotalBits) * aSize0; + UINT32 aSymbol; + if (m_Code < aNewBound) + { + aSymbol = 0; + m_Range = aNewBound; + } + else + { + aSymbol = 1; + m_Code -= aNewBound; + m_Range -= aNewBound; + } + Normalize(); + return aSymbol; + } + + UINT64 GetProcessedSize() {return m_Stream.GetProcessedSize(); } +}; + +}} + +#endif diff --git a/bsnes/reader/jma/winout.cpp b/bsnes/reader/jma/winout.cpp new file mode 100755 index 0000000..1f33885 --- /dev/null +++ b/bsnes/reader/jma/winout.cpp @@ -0,0 +1,89 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "winout.h" + +namespace NStream { +namespace NWindow { + +void COut::Create(UINT32 aKeepSizeBefore, UINT32 aKeepSizeAfter, UINT32 aKeepSizeReserv) +{ + m_Pos = 0; + m_PosLimit = aKeepSizeReserv + aKeepSizeBefore; + m_KeepSizeBefore = aKeepSizeBefore; + m_KeepSizeAfter = aKeepSizeAfter; + m_KeepSizeReserv = aKeepSizeReserv; + m_StreamPos = 0; + m_MoveFrom = m_KeepSizeReserv; + m_WindowSize = aKeepSizeBefore; + UINT32 aBlockSize = m_KeepSizeBefore + m_KeepSizeAfter + m_KeepSizeReserv; + delete []m_Buffer; + m_Buffer = new BYTE[aBlockSize]; +} + +COut::~COut() +{ + delete []m_Buffer; +} + +void COut::SetWindowSize(UINT32 aWindowSize) +{ + m_WindowSize = aWindowSize; + m_MoveFrom = m_KeepSizeReserv + m_KeepSizeBefore - aWindowSize; +} + +void COut::Init(ISequentialOutStream *aStream, bool aSolid) +{ + m_Stream = aStream; + + if(aSolid) + m_StreamPos = m_Pos; + else + { + m_Pos = 0; + m_PosLimit = m_KeepSizeReserv + m_KeepSizeBefore; + m_StreamPos = 0; + } +} + +HRESULT COut::Flush() +{ + UINT32 aSize = m_Pos - m_StreamPos; + if(aSize == 0) + return S_OK; + UINT32 aProcessedSize; + HRESULT aResult = m_Stream->Write(m_Buffer + m_StreamPos, aSize, &aProcessedSize); + if (aResult != S_OK) + return aResult; + if (aSize != aProcessedSize) + return E_FAIL; + m_StreamPos = m_Pos; + return S_OK; +} + +void COut::MoveBlockBackward() +{ + HRESULT aResult = Flush(); + if (aResult != S_OK) + throw aResult; + memmove(m_Buffer, m_Buffer + m_MoveFrom, m_WindowSize + m_KeepSizeAfter); + m_Pos -= m_MoveFrom; + m_StreamPos -= m_MoveFrom; +} + +}} diff --git a/bsnes/reader/jma/winout.h b/bsnes/reader/jma/winout.h new file mode 100755 index 0000000..38e06bd --- /dev/null +++ b/bsnes/reader/jma/winout.h @@ -0,0 +1,89 @@ +/* +Copyright (C) 2002 Andrea Mazzoleni ( http://advancemame.sf.net ) +Copyright (C) 2001-4 Igor Pavlov ( http://www.7-zip.org ) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __STREAM_WINDOWOUT_H +#define __STREAM_WINDOWOUT_H + +#include "iiostrm.h" + +namespace NStream { +namespace NWindow { + +// m_KeepSizeBefore: how mach BYTEs must be in buffer before m_Pos; +// m_KeepSizeAfter: how mach BYTEs must be in buffer after m_Pos; +// m_KeepSizeReserv: how mach BYTEs must be in buffer for Moving Reserv; +// must be >= aKeepSizeAfter; // test it + +class COut +{ + BYTE *m_Buffer; + UINT32 m_Pos; + UINT32 m_PosLimit; + UINT32 m_KeepSizeBefore; + UINT32 m_KeepSizeAfter; + UINT32 m_KeepSizeReserv; + UINT32 m_StreamPos; + + UINT32 m_WindowSize; + UINT32 m_MoveFrom; + + ISequentialOutStream *m_Stream; + + virtual void MoveBlockBackward(); +public: + COut(): m_Buffer(0), m_Stream(0) {} + virtual ~COut(); + void Create(UINT32 aKeepSizeBefore, + UINT32 aKeepSizeAfter, UINT32 aKeepSizeReserv = (1<<17)); + void SetWindowSize(UINT32 aWindowSize); + + void Init(ISequentialOutStream *aStream, bool aSolid = false); + HRESULT Flush(); + + UINT32 GetCurPos() const { return m_Pos; } + const BYTE *GetPointerToCurrentPos() const { return m_Buffer + m_Pos;}; + + void CopyBackBlock(UINT32 aDistance, UINT32 aLen) + { + if (m_Pos >= m_PosLimit) + MoveBlockBackward(); + BYTE *p = m_Buffer + m_Pos; + aDistance++; + for(UINT32 i = 0; i < aLen; i++) + p[i] = p[i - aDistance]; + m_Pos += aLen; + } + + void PutOneByte(BYTE aByte) + { + if (m_Pos >= m_PosLimit) + MoveBlockBackward(); + m_Buffer[m_Pos++] = aByte; + } + + BYTE GetOneByte(UINT32 anIndex) const + { + return m_Buffer[m_Pos + anIndex]; + } + + BYTE *GetBuffer() const { return m_Buffer; } +}; + +}} + +#endif diff --git a/bsnes/reader/jmareader.cpp b/bsnes/reader/jmareader.cpp new file mode 100755 index 0000000..2e879a0 --- /dev/null +++ b/bsnes/reader/jmareader.cpp @@ -0,0 +1,38 @@ +#ifdef READER_CPP + +#include "jmareader.hpp" +#include "jma/jma.h" + +unsigned JMAReader::size() { + return filesize; +} + +uint8_t* JMAReader::read(unsigned length) { + uint8_t *data = 0; + if(!filesize) return 0; + + if(length <= filesize) { + //read the entire file into RAM + data = new(zeromemory) uint8_t[filesize]; + JMAFile.extract_file(cname, data); + } else if(length > filesize) { + //read the entire file into RAM, pad the rest with 0x00s + data = new(zeromemory) uint8_t[length]; + JMAFile.extract_file(cname, data); + } + + return data; +} + +JMAReader::JMAReader(const char *fn) : JMAFile(fn), filesize(0) { + std::vector file_info = JMAFile.get_files_info(); + for(std::vector::iterator i = file_info.begin(); i != file_info.end(); i++) { + //Check for valid ROM based on size + if((i->size <= 0x1000000 + 512) && (i->size > filesize)) { + cname = i->name; + filesize = i->size; + } + } +} + +#endif //ifdef READER_CPP diff --git a/bsnes/reader/jmareader.hpp b/bsnes/reader/jmareader.hpp new file mode 100755 index 0000000..c3d1275 --- /dev/null +++ b/bsnes/reader/jmareader.hpp @@ -0,0 +1,14 @@ +#include "jma/jma.h" + +class JMAReader : public Reader { +public: + unsigned size(); + uint8_t* read(unsigned length = 0); + + JMAReader(const char *fn); + +private: + JMA::jma_open JMAFile; + unsigned filesize; + std::string cname; +}; diff --git a/bsnes/reader/reader.cpp b/bsnes/reader/reader.cpp new file mode 100755 index 0000000..035c712 --- /dev/null +++ b/bsnes/reader/reader.cpp @@ -0,0 +1,37 @@ +#include <../base.hpp> +#define READER_CPP + +#include "reader.hpp" +#include "filereader.cpp" + +#if defined(GZIP_SUPPORT) + #include "gzreader.cpp" + #include "zipreader.cpp" +#endif + +#if defined(JMA_SUPPORT) + #include "jmareader.cpp" +#endif + +Reader::Type Reader::detect(const char *fn, bool inspectheader) { + file fp; + if(!fp.open(fn, file::mode_read)) return Unknown; + + uint8_t p[8]; + memset(p, 0, sizeof p); + fp.read(p, 8); + fp.close(); + + if(inspectheader == true) { + //inspect file header to determine type + if(p[0] == 0x1f && p[1] == 0x8b && p[2] == 0x08 && p[3] <= 0x1f) return GZIP; + if(p[0] == 0x50 && p[1] == 0x4b && p[2] == 0x03 && p[3] == 0x04) return ZIP; + if(p[0] == 0x4a && p[1] == 0x4d && p[2] == 0x41 && p[3] == 0x00 && p[4] == 0x4e) return JMA; + } else { + //check file extension to determine type + if(striend(fn, ".gz")) return GZIP; + if(striend(fn, ".zip") || striend(fn, ".z")) return ZIP; + if(striend(fn, ".jma")) return JMA; + } + return Normal; +} diff --git a/bsnes/reader/reader.hpp b/bsnes/reader/reader.hpp new file mode 100755 index 0000000..1dd1944 --- /dev/null +++ b/bsnes/reader/reader.hpp @@ -0,0 +1,15 @@ +class Reader { +public: + enum Type { + Unknown, + Normal, + GZIP, + ZIP, + JMA, + }; + + static Type detect(const char *fn, bool inspectheader); + virtual unsigned size() = 0; + virtual uint8_t* read(unsigned length = 0) = 0; + virtual bool ready() { return true; } //can only call read() when ready() returns true +}; diff --git a/bsnes/reader/zipreader.cpp b/bsnes/reader/zipreader.cpp new file mode 100755 index 0000000..ea35dbd --- /dev/null +++ b/bsnes/reader/zipreader.cpp @@ -0,0 +1,61 @@ +#ifdef READER_CPP + +#include "zipreader.hpp" + +unsigned ZipReader::size() { + return filesize; +} + +uint8_t* ZipReader::read(unsigned length) { + if(!filesize) return 0; + + uint8_t *data = 0; + if(length <= filesize) { + //read the entire file into RAM + data = new(zeromemory) uint8_t[filesize]; + unzReadCurrentFile(zipfile, data, filesize); + } else { /* length > filesize */ + //read the entire file into RAM, pad the rest with 0x00s + data = new(zeromemory) uint8_t[length]; + unzReadCurrentFile(zipfile, data, filesize); + } + + return data; +} + +bool ZipReader::ready() { + return zipready; +} + +ZipReader::ZipReader(const char *fn) : filesize(0), zipready(false) { + unz_file_info cFileInfo; //Create variable to hold info for a compressed file + char cFileName[sizeof(cname)]; + + if(zipfile = unzOpen(fn)) { //Open zip file + for(int cFile = unzGoToFirstFile(zipfile); cFile == UNZ_OK; cFile = unzGoToNextFile(zipfile)) { + //Gets info on current file, and places it in cFileInfo + unzGetCurrentFileInfo(zipfile, &cFileInfo, cFileName, sizeof(cname), 0, 0, 0, 0); + + //verify uncompressed file is not bigger than max ROM size + if((cFileInfo.uncompressed_size <= 0x1000000 + 512) && (cFileInfo.uncompressed_size > filesize)) { + strcpy(cname, cFileName); + filesize = cFileInfo.uncompressed_size; + } + } + + if(filesize) { + unzLocateFile(zipfile, cname, 1); + unzOpenCurrentFile(zipfile); + zipready = true; + } + } +} + +ZipReader::~ZipReader() { + if(zipfile) { + unzCloseCurrentFile(zipfile); + unzClose(zipfile); + } +} + +#endif //ifdef READER_CPP diff --git a/bsnes/reader/zipreader.hpp b/bsnes/reader/zipreader.hpp new file mode 100755 index 0000000..94639ab --- /dev/null +++ b/bsnes/reader/zipreader.hpp @@ -0,0 +1,19 @@ +#include "zlib/unzip.h" + +#define ZIP_MAX_FILE_NAME PATH_MAX + +class ZipReader : public Reader { +public: + unsigned size(); + uint8_t* read(unsigned length = 0); + bool ready(); + + ZipReader(const char *fn); + ~ZipReader(); + +private: + unzFile zipfile; + unsigned filesize; + bool zipready; + char cname[PATH_MAX]; +}; diff --git a/bsnes/reader/zlib/adler32.c b/bsnes/reader/zlib/adler32.c new file mode 100755 index 0000000..58cdf06 --- /dev/null +++ b/bsnes/reader/zlib/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: adler32.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/bsnes/reader/zlib/compress.c b/bsnes/reader/zlib/compress.c new file mode 100755 index 0000000..1b7f07a --- /dev/null +++ b/bsnes/reader/zlib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: compress.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/bsnes/reader/zlib/crc32.c b/bsnes/reader/zlib/crc32.c new file mode 100755 index 0000000..fe16f0c --- /dev/null +++ b/bsnes/reader/zlib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id: crc32.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/bsnes/reader/zlib/crc32.h b/bsnes/reader/zlib/crc32.h new file mode 100755 index 0000000..8053b61 --- /dev/null +++ b/bsnes/reader/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/bsnes/reader/zlib/crypt.h b/bsnes/reader/zlib/crypt.h new file mode 100755 index 0000000..622f4bc --- /dev/null +++ b/bsnes/reader/zlib/crypt.h @@ -0,0 +1,132 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) + const char *passwd; /* password string */ + unsigned char *buf; /* where to write header */ + int bufSize; + unsigned long* pkeys; + const unsigned long* pcrc_32_tab; + unsigned long crcForCrypting; +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/bsnes/reader/zlib/deflate.c b/bsnes/reader/zlib/deflate.c new file mode 100755 index 0000000..d08ce26 --- /dev/null +++ b/bsnes/reader/zlib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id: deflate.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/bsnes/reader/zlib/deflate.h b/bsnes/reader/zlib/deflate.h new file mode 100755 index 0000000..be7b168 --- /dev/null +++ b/bsnes/reader/zlib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: deflate.h,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/bsnes/reader/zlib/gzio.c b/bsnes/reader/zlib/gzio.c new file mode 100755 index 0000000..6538d6c --- /dev/null +++ b/bsnes/reader/zlib/gzio.c @@ -0,0 +1,1026 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id: gzio.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#include + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int ZEXPORT gzdirect (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff --git a/bsnes/reader/zlib/inffast.c b/bsnes/reader/zlib/inffast.c new file mode 100755 index 0000000..de92735 --- /dev/null +++ b/bsnes/reader/zlib/inffast.c @@ -0,0 +1,724 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef __i386__ + +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + struct inffast_ar { + void *esp; /* esp save */ + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ + unsigned wsize; /* window size or zero if not using window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned status; /* this is set when state changes */ + } ar; + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + ar.in = strm->next_in; + ar.last = ar.in + (strm->avail_in - 5); + ar.out = strm->next_out; + ar.beg = ar.out - (start - strm->avail_out); + ar.end = ar.out + (strm->avail_out - 257); + ar.wsize = state->wsize; + ar.write = state->write; + ar.window = state->window; + ar.hold = state->hold; + ar.bits = state->bits; + ar.lcode = state->lencode; + ar.dcode = state->distcode; + ar.lmask = (1U << state->lenbits) - 1; + ar.dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + + /* align in on 2 byte boundary */ + if (((unsigned long)(void *)ar.in & 0x1) != 0) { + ar.hold += (unsigned long)*ar.in++ << ar.bits; + ar.bits += 8; + } + + __asm__ __volatile__ ( +" leal %0, %%eax\n" +" pushf\n" +" pushl %%ebp\n" +" movl %%esp, (%%eax)\n" +" movl %%eax, %%esp\n" +" movl 4(%%esp), %%esi\n" /* esi = in */ +" movl 12(%%esp), %%edi\n" /* edi = out */ +" movl 36(%%esp), %%edx\n" /* edx = hold */ +" movl 40(%%esp), %%ebx\n" /* ebx = bits */ +" movl 44(%%esp), %%ebp\n" /* ebp = lcode */ + +" cld\n" +" jmp .L_do_loop\n" + +".L_while_test:\n" +" cmpl %%edi, 20(%%esp)\n" +" jbe .L_break_loop\n" +" cmpl %%esi, 8(%%esp)\n" +" jbe .L_break_loop\n" + +".L_do_loop:\n" +" cmpb $15, %%bl\n" +" ja .L_get_length_code\n" /* if (15 < bits) */ + +" xorl %%eax, %%eax\n" +" lodsw\n" /* al = *(ushort *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $16, %%bl\n" /* bits += 16 */ +" shll %%cl, %%eax\n" +" orl %%eax, %%edx\n" /* hold |= *((ushort *)in)++ << bits */ + +".L_get_length_code:\n" +" movl 52(%%esp), %%eax\n" /* eax = lmask */ +" andl %%edx, %%eax\n" /* eax &= hold */ +" movl (%%ebp,%%eax,4), %%eax\n" /* eax = lcode[hold & lmask] */ + +".L_dolen:\n" +" movb %%ah, %%cl\n" /* cl = this.bits */ +" subb %%ah, %%bl\n" /* bits -= this.bits */ +" shrl %%cl, %%edx\n" /* hold >>= this.bits */ + +" testb %%al, %%al\n" +" jnz .L_test_for_length_base\n" /* if (op != 0) 45.7% */ + +" shrl $16, %%eax\n" /* output this.val char */ +" stosb\n" +" jmp .L_while_test\n" + +".L_test_for_length_base:\n" +" movl %%eax, %%ecx\n" /* len = this */ +" shrl $16, %%ecx\n" /* len = this.val */ +" movl %%ecx, 60(%%esp)\n" /* len = this */ +" movb %%al, %%cl\n" + +" testb $16, %%al\n" +" jz .L_test_for_second_level_length\n" /* if ((op & 16) == 0) 8% */ +" andb $15, %%cl\n" /* op &= 15 */ +" jz .L_decode_distance\n" /* if (!op) */ +" cmpb %%cl, %%bl\n" +" jae .L_add_bits_to_len\n" /* if (op <= bits) */ + +" movb %%cl, %%ch\n" /* stash op in ch, freeing cl */ +" xorl %%eax, %%eax\n" +" lodsw\n" /* al = *(ushort *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $16, %%bl\n" /* bits += 16 */ +" shll %%cl, %%eax\n" +" orl %%eax, %%edx\n" /* hold |= *((ushort *)in)++ << bits */ +" movb %%ch, %%cl\n" /* move op back to ecx */ + +".L_add_bits_to_len:\n" +" movl $1, %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" +" subb %%cl, %%bl\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" shrl %%cl, %%edx\n" +" addl %%eax, 60(%%esp)\n" /* len += hold & mask[op] */ + +".L_decode_distance:\n" +" cmpb $15, %%bl\n" +" ja .L_get_distance_code\n" /* if (15 < bits) */ + +" xorl %%eax, %%eax\n" +" lodsw\n" /* al = *(ushort *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $16, %%bl\n" /* bits += 16 */ +" shll %%cl, %%eax\n" +" orl %%eax, %%edx\n" /* hold |= *((ushort *)in)++ << bits */ + +".L_get_distance_code:\n" +" movl 56(%%esp), %%eax\n" /* eax = dmask */ +" movl 48(%%esp), %%ecx\n" /* ecx = dcode */ +" andl %%edx, %%eax\n" /* eax &= hold */ +" movl (%%ecx,%%eax,4), %%eax\n"/* eax = dcode[hold & dmask] */ + +".L_dodist:\n" +" movl %%eax, %%ebp\n" /* dist = this */ +" shrl $16, %%ebp\n" /* dist = this.val */ +" movb %%ah, %%cl\n" +" subb %%ah, %%bl\n" /* bits -= this.bits */ +" shrl %%cl, %%edx\n" /* hold >>= this.bits */ +" movb %%al, %%cl\n" /* cl = this.op */ + +" testb $16, %%al\n" /* if ((op & 16) == 0) */ +" jz .L_test_for_second_level_dist\n" +" andb $15, %%cl\n" /* op &= 15 */ +" jz .L_check_dist_one\n" +" cmpb %%cl, %%bl\n" +" jae .L_add_bits_to_dist\n" /* if (op <= bits) 97.6% */ + +" movb %%cl, %%ch\n" /* stash op in ch, freeing cl */ +" xorl %%eax, %%eax\n" +" lodsw\n" /* al = *(ushort *)in++ */ +" movb %%bl, %%cl\n" /* cl = bits, needs it for shifting */ +" addb $16, %%bl\n" /* bits += 16 */ +" shll %%cl, %%eax\n" +" orl %%eax, %%edx\n" /* hold |= *((ushort *)in)++ << bits */ +" movb %%ch, %%cl\n" /* move op back to ecx */ + +".L_add_bits_to_dist:\n" +" movl $1, %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" /* (1 << op) - 1 */ +" subb %%cl, %%bl\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" shrl %%cl, %%edx\n" +" addl %%eax, %%ebp\n" /* dist += hold & ((1 << op) - 1) */ + +".L_check_window:\n" +" movl %%esi, 4(%%esp)\n" /* save in so from can use it's reg */ +" movl %%edi, %%eax\n" +" subl 16(%%esp), %%eax\n" /* nbytes = out - beg */ + +" cmpl %%ebp, %%eax\n" +" jb .L_clip_window\n" /* if (dist > nbytes) 4.2% */ + +" movl 60(%%esp), %%ecx\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ + +" subl $3, %%ecx\n" /* copy from to out */ +" movb (%%esi), %%al\n" +" movb %%al, (%%edi)\n" +" movb 1(%%esi), %%al\n" +" movb 2(%%esi), %%ah\n" +" addl $3, %%esi\n" +" movb %%al, 1(%%edi)\n" +" movb %%ah, 2(%%edi)\n" +" addl $3, %%edi\n" +" rep movsb\n" + +" movl 4(%%esp), %%esi\n" /* move in back to %esi, toss from */ +" movl 44(%%esp), %%ebp\n" /* ebp = lcode */ +" jmp .L_while_test\n" + +".L_check_dist_one:\n" +" cmpl $1, %%ebp\n" /* if dist 1, is a memset */ +" jne .L_check_window\n" +" cmpl %%edi, 16(%%esp)\n" +" je .L_check_window\n" + +" decl %%edi\n" +" movl 60(%%esp), %%ecx\n" +" movb (%%edi), %%al\n" +" subl $3, %%ecx\n" + +" movb %%al, 1(%%edi)\n" /* memset out with from[-1] */ +" movb %%al, 2(%%edi)\n" +" movb %%al, 3(%%edi)\n" +" addl $4, %%edi\n" +" rep stosb\n" +" movl 44(%%esp), %%ebp\n" /* ebp = lcode */ +" jmp .L_while_test\n" + +".L_test_for_second_level_length:\n" +" testb $64, %%al\n" +" jnz .L_test_for_end_of_block\n" /* if ((op & 64) != 0) */ + +" movl $1, %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" addl 60(%%esp), %%eax\n" /* eax += this.val */ +" movl (%%ebp,%%eax,4), %%eax\n" /* eax = lcode[val+(hold&mask[op])]*/ +" jmp .L_dolen\n" + +".L_test_for_second_level_dist:\n" +" testb $64, %%al\n" +" jnz .L_invalid_distance_code\n" /* if ((op & 64) != 0) */ + +" movl $1, %%eax\n" +" shll %%cl, %%eax\n" +" decl %%eax\n" +" andl %%edx, %%eax\n" /* eax &= hold */ +" addl %%ebp, %%eax\n" /* eax += this.val */ +" movl 48(%%esp), %%ecx\n" /* ecx = dcode */ +" movl (%%ecx,%%eax,4), %%eax\n" /* eax = dcode[val+(hold&mask[op])]*/ +" jmp .L_dodist\n" + +".L_clip_window:\n" +" movl %%eax, %%ecx\n" +" movl 24(%%esp), %%eax\n" /* prepare for dist compare */ +" negl %%ecx\n" /* nbytes = -nbytes */ +" movl 32(%%esp), %%esi\n" /* from = window */ + +" cmpl %%ebp, %%eax\n" +" jb .L_invalid_distance_too_far\n" /* if (dist > wsize) */ + +" addl %%ebp, %%ecx\n" /* nbytes = dist - nbytes */ +" cmpl $0, 28(%%esp)\n" +" jne .L_wrap_around_window\n" /* if (write != 0) */ + +" subl %%ecx, %%eax\n" +" addl %%eax, %%esi\n" /* from += wsize - nbytes */ + +" movl 60(%%esp), %%eax\n" +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy1\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ +" jmp .L_do_copy1\n" + +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy1\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ +" jmp .L_do_copy1\n" + +".L_wrap_around_window:\n" +" movl 28(%%esp), %%eax\n" +" cmpl %%eax, %%ecx\n" +" jbe .L_contiguous_in_window\n" /* if (write >= nbytes) */ + +" addl 24(%%esp), %%esi\n" +" addl %%eax, %%esi\n" +" subl %%ecx, %%esi\n" /* from += wsize + write - nbytes */ +" subl %%eax, %%ecx\n" /* nbytes -= write */ + +" movl 60(%%esp), %%eax\n" +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy1\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl 32(%%esp), %%esi\n" /* from = window */ +" movl 28(%%esp), %%ecx\n" /* nbytes = write */ +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy1\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ +" jmp .L_do_copy1\n" + +".L_contiguous_in_window:\n" +" addl %%eax, %%esi\n" +" subl %%ecx, %%esi\n" /* from += write - nbytes */ + +" movl 60(%%esp), %%eax\n" +" cmpl %%ecx, %%eax\n" +" jbe .L_do_copy1\n" /* if (nbytes >= len) */ + +" subl %%ecx, %%eax\n" /* len -= nbytes */ +" rep movsb\n" +" movl %%edi, %%esi\n" +" subl %%ebp, %%esi\n" /* from = out - dist */ + +".L_do_copy1:\n" +" movl %%eax, %%ecx\n" +" rep movsb\n" + +" movl 4(%%esp), %%esi\n" /* move in back to %esi, toss from */ +" movl 44(%%esp), %%ebp\n" /* ebp = lcode */ +" jmp .L_while_test\n" + +".L_test_for_end_of_block:\n" +" testb $32, %%al\n" +" jz .L_invalid_literal_length_code\n" +" movl $1, 68(%%esp)\n" +" jmp .L_break_loop_with_status\n" + +".L_invalid_literal_length_code:\n" +" movl $2, 68(%%esp)\n" +" jmp .L_break_loop_with_status\n" + +".L_invalid_distance_code:\n" +" movl $3, 68(%%esp)\n" +" jmp .L_break_loop_with_status\n" + +".L_invalid_distance_too_far:\n" +" movl 4(%%esp), %%esi\n" +" movl $4, 68(%%esp)\n" +" jmp .L_break_loop_with_status\n" + +".L_break_loop:\n" +" movl $0, 68(%%esp)\n" + +".L_break_loop_with_status:\n" +/* put in, out, bits, and hold back into ar and pop esp */ +" movl %%esi, 4(%%esp)\n" +" movl %%edi, 12(%%esp)\n" +" movl %%ebx, 40(%%esp)\n" +" movl %%edx, 36(%%esp)\n" +" movl (%%esp), %%esp\n" +" popl %%ebp\n" +" popf\n" + : + : "m" (ar) + : "memory", "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi" + ); + + + if (ar.status > 1) { + if (ar.status == 2) + strm->msg = "invalid literal/length code"; + else if (ar.status == 3) + strm->msg = "invalid distance code"; + else + strm->msg = "invalid distance too far back"; + state->mode = BAD; + } + else if ( ar.status == 1 ) { + state->mode = TYPE; + } + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + ar.len = ar.bits >> 3; + ar.in -= ar.len; + ar.bits -= ar.len << 3; + ar.hold &= (1U << ar.bits) - 1; + + /* update state and return */ + strm->next_in = ar.in; + strm->next_out = ar.out; + strm->avail_in = (unsigned)(ar.in < ar.last ? 5 + (ar.last - ar.in) : + 5 - (ar.in - ar.last)); + strm->avail_out = (unsigned)(ar.out < ar.end ? 257 + (ar.end - ar.out) : + 257 - (ar.out - ar.end)); + state->hold = ar.hold; + state->bits = ar.bits; + return; +} + +#else + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !i386 */ diff --git a/bsnes/reader/zlib/inffast.h b/bsnes/reader/zlib/inffast.h new file mode 100755 index 0000000..1e88d2d --- /dev/null +++ b/bsnes/reader/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/bsnes/reader/zlib/inffixed.h b/bsnes/reader/zlib/inffixed.h new file mode 100755 index 0000000..75ed4b5 --- /dev/null +++ b/bsnes/reader/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/bsnes/reader/zlib/inflate.c b/bsnes/reader/zlib/inflate.c new file mode 100755 index 0000000..792fdee --- /dev/null +++ b/bsnes/reader/zlib/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/bsnes/reader/zlib/inflate.h b/bsnes/reader/zlib/inflate.h new file mode 100755 index 0000000..07bd3e7 --- /dev/null +++ b/bsnes/reader/zlib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/bsnes/reader/zlib/inftrees.c b/bsnes/reader/zlib/inftrees.c new file mode 100755 index 0000000..8a9c13f --- /dev/null +++ b/bsnes/reader/zlib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/bsnes/reader/zlib/inftrees.h b/bsnes/reader/zlib/inftrees.h new file mode 100755 index 0000000..b1104c8 --- /dev/null +++ b/bsnes/reader/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/bsnes/reader/zlib/ioapi.c b/bsnes/reader/zlib/ioapi.c new file mode 100755 index 0000000..d2ddb60 --- /dev/null +++ b/bsnes/reader/zlib/ioapi.c @@ -0,0 +1,193 @@ +/* ioapi.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#include +#include +#include + +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +//needed for MultiByteToWideChar() +#include +#endif + +#include "zlib.h" +#include "ioapi.h" + + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +voidpf ZCALLBACK fopen_file_func OF(( + voidpf opaque, + const char* filename, + int mode)); + +uLong ZCALLBACK fread_file_func OF(( + voidpf opaque, + voidpf stream, + void* buf, + uLong size)); + +uLong ZCALLBACK fwrite_file_func OF(( + voidpf opaque, + voidpf stream, + const void* buf, + uLong size)); + +long ZCALLBACK ftell_file_func OF(( + voidpf opaque, + voidpf stream)); + +long ZCALLBACK fseek_file_func OF(( + voidpf opaque, + voidpf stream, + uLong offset, + int origin)); + +int ZCALLBACK fclose_file_func OF(( + voidpf opaque, + voidpf stream)); + +int ZCALLBACK ferror_file_func OF(( + voidpf opaque, + voidpf stream)); + + +voidpf ZCALLBACK fopen_file_func (opaque, filename, mode) + voidpf opaque; + const char* filename; + int mode; +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) { + //[2008-10-17 byuu] wrap Win32 fopen() to support UTF-8 filenames + #if !defined(_WIN32) + file = fopen(filename, mode_fopen); + #else + wchar_t wfilename[_MAX_PATH + 1]; + wchar_t wmode_fopen[_MAX_PATH + 1]; + MultiByteToWideChar(CP_UTF8, 0, filename, -1, wfilename, _MAX_PATH); + MultiByteToWideChar(CP_UTF8, 0, mode_fopen, -1, wmode_fopen, _MAX_PATH); + file = _wfopen(wfilename, wmode_fopen); + #endif + } + return file; +} + + +uLong ZCALLBACK fread_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + void* buf; + uLong size; +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + + +uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + const void* buf; + uLong size; +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +long ZCALLBACK ftell_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + +long ZCALLBACK fseek_file_func (opaque, stream, offset, origin) + voidpf opaque; + voidpf stream; + uLong offset; + int origin; +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + fseek((FILE *)stream, offset, fseek_origin); + return ret; +} + +int ZCALLBACK fclose_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +int ZCALLBACK ferror_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/bsnes/reader/zlib/ioapi.h b/bsnes/reader/zlib/ioapi.h new file mode 100755 index 0000000..7d457ba --- /dev/null +++ b/bsnes/reader/zlib/ioapi.h @@ -0,0 +1,75 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#ifndef _ZLIBIOAPI_H +#define _ZLIBIOAPI_H + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + +#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +#define ZCALLBACK CALLBACK +#else +#define ZCALLBACK +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + + + +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) +#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) +#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) +#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) +#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/bsnes/reader/zlib/trees.c b/bsnes/reader/zlib/trees.c new file mode 100755 index 0000000..ecd62b5 --- /dev/null +++ b/bsnes/reader/zlib/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id: trees.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/bsnes/reader/zlib/trees.h b/bsnes/reader/zlib/trees.h new file mode 100755 index 0000000..72facf9 --- /dev/null +++ b/bsnes/reader/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/bsnes/reader/zlib/unzip.c b/bsnes/reader/zlib/unzip.c new file mode 100755 index 0000000..1e05038 --- /dev/null +++ b/bsnes/reader/zlib/unzip.c @@ -0,0 +1,1605 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + Read unzip.h for more info +*/ + +/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of +compatibility with older software. The following is from the original crypt.c. Code +woven in by Terry Thorsen 1/2003. +*/ +/* + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html +*/ +/* + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + */ + +/* + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + */ + + +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i = 0; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i = 0; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (fileName1,fileName2) + const char* fileName1; + const char* fileName2; +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) + const char* fileName1; + const char* fileName2; + int iCaseSensitivity; +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def) + const char *path; + zlib_filefunc_def* pzlib_filefunc_def; +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + if (pzlib_filefunc_def==NULL) + fill_fopen_filefunc(&us.z_filefunc); + else + us.z_filefunc = *pzlib_filefunc_def; + + us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + if (ZSEEK(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) + unzFile file; + unz_global_info *pglobal_info; +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) + uLong ulDosDate; + tm_unz* ptm; +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (file, + pfile_info, + pfile_info_internal, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + unz_file_info_internal *pfile_info_internal; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (ZSEEK(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (file, + pfile_info, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (file) + unzFile file; +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (file) + unzFile file; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) + unzFile file; + const char *szFileName; + int iCaseSensitivity; +{ + unz_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info cur_file_infoSaved; + unz_file_info_internal cur_file_info_internalSaved; + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; // offset in file + uLong num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGoToFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, + poffset_local_extrafield, + psize_local_extrafield) + unz_s* s; + uInt* piSizeVar; + uLong *poffset_local_extrafield; + uInt *psize_local_extrafield; +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) + unzFile file; + int* method; + int* level; + int raw; + const char* password; +{ + int err=UNZ_OK; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_DEFLATED) && + (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (file) + unzFile file; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (file, password) + unzFile file; + const char* password; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw) + unzFile file; + int* method; + int* level; + int raw; +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (file, buf, len) + unzFile file; + voidp buf; + unsigned len; +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) + unzFile file; + voidp buf; + unsigned len; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (file) + unzFile file; +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) + unzFile file; + char *szComment; + uLong uSizeBuf; +{ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern uLong ZEXPORT unzGetOffset (file) + unzFile file; +{ + unz_s* s; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern int ZEXPORT unzSetOffset (file, pos) + unzFile file; + uLong pos; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} diff --git a/bsnes/reader/zlib/unzip.h b/bsnes/reader/zlib/unzip.h new file mode 100755 index 0000000..b247937 --- /dev/null +++ b/bsnes/reader/zlib/unzip.h @@ -0,0 +1,354 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz_H */ diff --git a/bsnes/reader/zlib/zconf.h b/bsnes/reader/zlib/zconf.h new file mode 100755 index 0000000..eb5a9ce --- /dev/null +++ b/bsnes/reader/zlib/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zconf.h,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/bsnes/reader/zlib/zip.c b/bsnes/reader/zlib/zip.c new file mode 100755 index 0000000..2dc74df --- /dev/null +++ b/bsnes/reader/zlib/zip.c @@ -0,0 +1,1220 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.01e, February 12th, 2005 + + 27 Dec 2004 Rolf Kalbermatter + Modification to zipOpen2 to support globalComment retrieval. + + Copyright (C) 1998-2005 Gilles Vollant + + Read zip.h for more info +*/ + + +#include +#include +#include +#include +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] = + " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + uLong pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralheader; /* size of the central header for cur file */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile_info; + +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile_info ci; /* info on the file curretly writing */ + + uLong begin_pos; /* position of the beginning of the zipfile */ + uLong add_position_when_writting_offset; + uLong number_entry; +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif +} zip_internal; + + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(ldi) + linkedlist_datablock_internal* ldi; +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(ll) + linkedlist_data* ll; +{ + ll->first_block = ll->last_block = NULL; +} + +/* +local void free_linkedlist(ll) + linkedlist_data* ll; +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} +*/ + +local int add_data_in_datablock(ll,buf,len) + linkedlist_data* ll; + const void* buf; + uLong len; +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;ifilled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 or 4 (byte, short or long) +*/ + +local int ziplocal_putValue OF((const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, uLong x, int nbByte)); +local int ziplocal_putValue (pzlib_filefunc_def, filestream, x, nbByte) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong x; + int nbByte; +{ + unsigned char buf[4]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void ziplocal_putValue_inmemory OF((void* dest, uLong x, int nbByte)); +local void ziplocal_putValue_inmemory (dest, x, nbByte) + void* dest; + uLong x; + int nbByte; +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong ziplocal_TmzDateToDosDate(ptm,dosDate) + const tm_zip* ptm; + uLong dosDate; +{ + uLong year = (uLong)ptm->tm_year; + if (year>1980) + year-=1980; + else if (year>80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int ziplocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int ziplocal_getByte(pzlib_filefunc_def,filestream,pi) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int ziplocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int ziplocal_getShort (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x; + int i = 0; + int err; + + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int ziplocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x = 0; + int i = 0; + int err; + + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong ziplocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong ziplocal_SearchCentralDir(pzlib_filefunc_def,filestream) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + +/************************************************************/ +extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc_def) + const char *pathname; + int append; + zipcharpc* globalcomment; + zlib_filefunc_def* pzlib_filefunc_def; +{ + zip_internal ziinit; + zip_internal* zi; + int err=ZIP_OK; + + + if (pzlib_filefunc_def==NULL) + fill_fopen_filefunc(&ziinit.z_filefunc); + else + ziinit.z_filefunc = *pzlib_filefunc_def; + + ziinit.filestream = (*(ziinit.z_filefunc.zopen_file)) + (ziinit.z_filefunc.opaque, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + ziinit.begin_pos = ZTELL(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + zi = (zip_internal*)ALLOC(sizeof(zip_internal)); + if (zi==NULL) + { + ZCLOSE(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory */ + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry; + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong size_comment; + + central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream); + if (central_pos==0) + err=ZIP_ERRNO; + + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* zipfile global comment length */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((central_pos0) + { + ziinit.globalcomment = ALLOC(size_comment+1); + if (ziinit.globalcomment) + { + size_comment = ZREAD(ziinit.z_filefunc, ziinit.filestream,ziinit.globalcomment,size_comment); + ziinit.globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - + (offset_central_dir+size_central_dir); + ziinit.add_position_when_writting_offset = byte_before_the_zipfile; + + { + uLong size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + offset_central_dir + byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + uLong read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + if (ZREAD(ziinit.z_filefunc, ziinit.filestream,buf_read,read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&ziinit.central_dir,buf_read, + (uLong)read_this); + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + ziinit.begin_pos = byte_before_the_zipfile; + ziinit.number_entry = number_entry_CD; + + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen (pathname, append) + const char *pathname; + int append; +{ + return zipOpen2(pathname,append,NULL,NULL); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting) + zipFile file; + const char* filename; + const zip_fileinfo* zipfi; + const void* extrafield_local; + uInt size_extrafield_local; + const void* extrafield_global; + uInt size_extrafield_global; + const char* comment; + int method; + int level; + int raw; + int windowBits; + int memLevel; + int strategy; + const char* password; + uLong crcForCrypting; +{ + zip_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; + + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate); + } + + zi->ci.flag = 0; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if ((level==2)) + zi->ci.flag |= 4; + if ((level==1)) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL(zi->z_filefunc,zi->filestream) ; + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + + size_extrafield_global + size_comment; + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader); + + ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)VERSIONMADEBY,2); + ziplocal_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + ziplocal_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + ziplocal_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + ziplocal_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + ziplocal_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + ziplocal_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + ziplocal_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + ziplocal_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + ziplocal_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + ziplocal_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + ziplocal_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header- zi->add_position_when_writting_offset,4); + + for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + /* write the local header */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC,4); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield_local,2); + + if ((err==ZIP_OK) && (size_filename>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + + if ((err==ZIP_OK) && (size_extrafield_local>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream,extrafield_local,size_extrafield_local) + !=size_extrafield_local) + err = ZIP_ERRNO; + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, + Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = 1; + } +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip2(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw) + zipFile file; + const char* filename; + const zip_fileinfo* zipfi; + const void* extrafield_local; + uInt size_extrafield_local; + const void* extrafield_global; + uInt size_extrafield_global; + const char* comment; + int method; + int level; + int raw; +{ + return zipOpenNewFileInZip3 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level) + zipFile file; + const char* filename; + const zip_fileinfo* zipfi; + const void* extrafield_local; + uInt size_extrafield_local; + const void* extrafield_global; + uInt size_extrafield_global; + const char* comment; + int method; + int level; +{ + return zipOpenNewFileInZip2 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0); +} + +local int zipFlushWriteBuffer(zi) + zip_internal* zi; +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;ici.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, + zi->ci.buffered_data[i],t); +#endif + } + if (ZWRITE(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) + !=zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + zi->ci.pos_in_buffered_data = 0; + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (file, buf, len) + zipFile file; + const void* buf; + unsigned len; +{ + zip_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.stream.next_in = (void*)buf; + zi->ci.stream.avail_in = len; + zi->ci.crc32 = crc32(zi->ci.crc32,buf,len); + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zipFlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + for (i=0;ici.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (file, uncompressed_size, crc32) + zipFile file; + uLong uncompressed_size; + uLong crc32; +{ + zip_internal* zi; + uLong compressed_size; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zipFlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + if (zipFlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + err=deflateEnd(&zi->ci.stream); + zi->ci.stream_initialised = 0; + } + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = (uLong)zi->ci.stream.total_in; + } + compressed_size = (uLong)zi->ci.stream.total_out; +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + ziplocal_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + ziplocal_putValue_inmemory(zi->ci.central_header+20, + compressed_size,4); /*compr size*/ + if (zi->ci.stream.data_type == Z_ASCII) + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + ziplocal_putValue_inmemory(zi->ci.central_header+24, + uncompressed_size,4); /*uncompr size*/ + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir,zi->ci.central_header, + (uLong)zi->ci.size_centralheader); + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + long cur_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream); + if (ZSEEK(zi->z_filefunc,zi->filestream, + zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if (err==ZIP_OK) /* compressed size, unknown */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + + if (ZSEEK(zi->z_filefunc,zi->filestream, + cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (file) + zipFile file; +{ + return zipCloseFileInZipRaw (file,0,0); +} + +extern int ZEXPORT zipClose (file, global_comment) + zipFile file; + const char* global_comment; +{ + zip_internal* zi; + int err = 0; + uLong size_centraldir = 0; + uLong centraldir_pos_inzip; + uInt size_global_comment; + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + if (global_comment==NULL) + size_global_comment = 0; + else + size_global_comment = (uInt)strlen(global_comment); + + centraldir_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream); + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block ; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream, + ldi->data,ldi->filled_in_this_block) + !=ldi->filled_in_this_block ) + err = ZIP_ERRNO; + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_datablock(zi->central_dir.first_block); + + if (err==ZIP_OK) /* Magic End */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + + if (err==ZIP_OK) /* size of the central directory */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the + starting disk number */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream, + (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + + if (err==ZIP_OK) /* zipfile comment length */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if ((err==ZIP_OK) && (size_global_comment>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream, + global_comment,size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + + if (ZCLOSE(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} diff --git a/bsnes/reader/zlib/zip.h b/bsnes/reader/zlib/zip.h new file mode 100755 index 0000000..acacce8 --- /dev/null +++ b/bsnes/reader/zlib/zip.h @@ -0,0 +1,235 @@ +/* zip.h -- IO for compress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow creates .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + For uncompress .zip file, look at unzip.h + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.html for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef _zip_H +#define _zip_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCtypting)); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCtypting : crc of file to compress (needed for crypting) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); +/* + Close the current file in the zipfile, for fiel opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip_H */ diff --git a/bsnes/reader/zlib/zlib.h b/bsnes/reader/zlib/zlib.h new file mode 100755 index 0000000..0228179 --- /dev/null +++ b/bsnes/reader/zlib/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/bsnes/reader/zlib/zutil.c b/bsnes/reader/zlib/zutil.c new file mode 100755 index 0000000..8b278e3 --- /dev/null +++ b/bsnes/reader/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zutil.c,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/bsnes/reader/zlib/zutil.h b/bsnes/reader/zlib/zutil.h new file mode 100755 index 0000000..c7ad800 --- /dev/null +++ b/bsnes/reader/zlib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: zutil.h,v 1.3 2005/07/27 18:15:11 nach Exp $ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/bsnes/smp/dsmp.cpp b/bsnes/smp/dsmp.cpp new file mode 100755 index 0000000..1cc6c00 --- /dev/null +++ b/bsnes/smp/dsmp.cpp @@ -0,0 +1,312 @@ +#ifdef SMP_CPP + +//virtual function, see src/cpu/dcpu.cpp +//for explanation of this function +bool SMP::in_opcode() { return false; } + +uint16 SMP::__relb(int8 offset, int op_len) { + uint16 pc = regs.pc + op_len; + return pc + offset; +} + +void SMP::disassemble_opcode(char *output) { + char *s, t[512]; + uint8 op, op0, op1; + uint16 opw, opdp0, opdp1; + s = output; + + if(in_opcode() == true) { + strcpy(s, "..???? "); + return; + } + + sprintf(s, "..%.4x ", regs.pc); + + op = ram_read(regs.pc); + op0 = ram_read(regs.pc + 1); + op1 = ram_read(regs.pc + 2); + opw = (op0) | (op1 << 8); + opdp0 = ((regs.p.p)?0x100:0x000) + op0; + opdp1 = ((regs.p.p)?0x100:0x000) + 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, 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 //ifdef SMP_CPP diff --git a/bsnes/smp/iplrom.hpp b/bsnes/smp/iplrom.hpp new file mode 100755 index 0000000..eafbd7b --- /dev/null +++ b/bsnes/smp/iplrom.hpp @@ -0,0 +1,40 @@ +//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_t 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.cpp b/bsnes/smp/smp.cpp new file mode 100755 index 0000000..6392ac2 --- /dev/null +++ b/bsnes/smp/smp.cpp @@ -0,0 +1,5 @@ +#include <../base.hpp> +#define SMP_CPP + +#include "iplrom.hpp" +#include "dsmp.cpp" diff --git a/bsnes/smp/smp.hpp b/bsnes/smp/smp.hpp new file mode 100755 index 0000000..d4e39b9 --- /dev/null +++ b/bsnes/smp/smp.hpp @@ -0,0 +1,25 @@ +class SMP { +public: + virtual void enter() = 0; + + #include "smpregs.hpp" + regs_t regs; + static const uint8_t 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() = 0; + virtual void reset() = 0; + + //debugging functions + virtual bool in_opcode(); + void disassemble_opcode(char *output); + inline uint16 __relb(int8 offset, int op_len); + + SMP() {} + virtual ~SMP() {} +}; diff --git a/bsnes/smp/smpregs.hpp b/bsnes/smp/smpregs.hpp new file mode 100755 index 0000000..d1ac5c3 --- /dev/null +++ b/bsnes/smp/smpregs.hpp @@ -0,0 +1,31 @@ +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; + union { + uint16 ya; + struct { uint8 order_lsb2(a, y); }; + }; + uint8_t x, sp; + flag_t p; + regs_t() : pc(0), ya(0), x(0), sp(0) {} +}; diff --git a/bsnes/smp/ssmp/core/cc.sh b/bsnes/smp/ssmp/core/cc.sh new file mode 100755 index 0000000..6b94ac7 --- /dev/null +++ b/bsnes/smp/ssmp/core/cc.sh @@ -0,0 +1,4 @@ +g++ -c ssmpgen.cpp -I../../../lib +g++ -c ../../../lib/nall/string.cpp -I../../../lib +g++ -o ssmpgen ssmpgen.o string.o +rm *.o diff --git a/bsnes/smp/ssmp/core/clean.sh b/bsnes/smp/ssmp/core/clean.sh new file mode 100755 index 0000000..b62bcbc --- /dev/null +++ b/bsnes/smp/ssmp/core/clean.sh @@ -0,0 +1 @@ +rm ssmpgen diff --git a/bsnes/smp/ssmp/core/core.cpp b/bsnes/smp/ssmp/core/core.cpp new file mode 100755 index 0000000..b3c600c --- /dev/null +++ b/bsnes/smp/ssmp/core/core.cpp @@ -0,0 +1,21 @@ +#ifdef SSMP_CPP + +#include "opfn.cpp" + +void sSMP::enter() { loop: + tracer.trace_smpop(); //traces SMP opcode (only if tracer is enabled) + + status.in_opcode = true; + switch(op_readpc()) { + #include "op_mov.cpp" + #include "op_pc.cpp" + #include "op_read.cpp" + #include "op_rmw.cpp" + #include "op_misc.cpp" + } + status.in_opcode = false; + + goto loop; +} + +#endif //ifdef SSMP_CPP diff --git a/bsnes/smp/ssmp/core/core.hpp b/bsnes/smp/ssmp/core/core.hpp new file mode 100755 index 0000000..cf4fd47 --- /dev/null +++ b/bsnes/smp/ssmp/core/core.hpp @@ -0,0 +1,19 @@ + uint16 dp, sp, rd, wr, bit, ya; + + bool in_opcode() { return status.in_opcode; } + + 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); diff --git a/bsnes/smp/ssmp/core/op_misc.b b/bsnes/smp/ssmp/core/op_misc.b new file mode 100755 index 0000000..a6baba9 --- /dev/null +++ b/bsnes/smp/ssmp/core/op_misc.b @@ -0,0 +1,163 @@ +nop(0x00) { +1:op_io(); +} + +sleep(0xef), +stop(0xff) { +1:op_io(); +2:op_io(); + regs.pc--; +} + +xcn(0x9f) { +1:op_io(); +2:op_io(); +3:op_io(); +4:op_io(); + regs.a = (regs.a >> 4) | (regs.a << 4); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +daa(0xdf) { +1:op_io(); +2:op_io(); + if(regs.p.c || (regs.a) > 0x99) { + regs.a += 0x60; + regs.p.c = 1; + } + if(regs.p.h || (regs.a & 15) > 0x09) { + regs.a += 0x06; + } + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +das(0xbe) { +1:op_io(); +2:op_io(); + if(!regs.p.c || (regs.a) > 0x99) { + regs.a -= 0x60; + regs.p.c = 0; + } + if(!regs.p.h || (regs.a & 15) > 0x09) { + regs.a -= 0x06; + } + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +clrc(0x60, regs.p.c = 0), +clrp(0x20, regs.p.p = 0), +setc(0x80, regs.p.c = 1), +setp(0x40, regs.p.p = 1) { +1:op_io(); + $1; +} + +clrv(0xe0) { +1:op_io(); + regs.p.v = 0; + regs.p.h = 0; +} + +notc(0xed) { +1:op_io(); +2:op_io(); + regs.p.c = !regs.p.c; +} + +ei(0xa0, 1), +di(0xc0, 0) { +1:op_io(); +2:op_io(); + regs.p.i = $1; +} + +set0_dp(0x02, rd |= 0x01), +clr0_dp(0x12, rd &= ~0x01), +set1_dp(0x22, rd |= 0x02), +clr1_dp(0x32, rd &= ~0x02), +set2_dp(0x42, rd |= 0x04), +clr2_dp(0x52, rd &= ~0x04), +set3_dp(0x62, rd |= 0x08), +clr3_dp(0x72, rd &= ~0x08), +set4_dp(0x82, rd |= 0x10), +clr4_dp(0x92, rd &= ~0x10), +set5_dp(0xa2, rd |= 0x20), +clr5_dp(0xb2, rd &= ~0x20), +set6_dp(0xc2, rd |= 0x40), +clr6_dp(0xd2, rd &= ~0x40), +set7_dp(0xe2, rd |= 0x80), +clr7_dp(0xf2, rd &= ~0x80) { +1:dp = op_readpc(); +2:rd = op_readdp(dp); +3:$1; + op_writedp(dp, rd); +} + +push_a(0x2d, a), +push_x(0x4d, x), +push_y(0x6d, y), +push_p(0x0d, p) { +1:op_io(); +2:op_io(); +3:op_writestack(regs.$1); +} + +pop_a(0xae, a), +pop_x(0xce, x), +pop_y(0xee, y), +pop_p(0x8e, p) { +1:op_io(); +2:op_io(); +3:regs.$1 = op_readstack(); +} + +mul_ya(0xcf) { +1:op_io(); +2:op_io(); +3:op_io(); +4:op_io(); +5:op_io(); +6:op_io(); +7:op_io(); +8:op_io(); + ya = regs.y * regs.a; + regs.a = ya; + regs.y = ya >> 8; +//result is set based on y (high-byte) only + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); +} + +div_ya_x(0x9e) { +1:op_io(); +2:op_io(); +3:op_io(); +4:op_io(); +5:op_io(); +6:op_io(); +7:op_io(); +8:op_io(); +9:op_io(); +10:op_io(); +11:op_io(); + ya = regs.ya; +//overflow set if quotient >= 256 + regs.p.v = !!(regs.y >= regs.x); + regs.p.h = !!((regs.y & 15) >= (regs.x & 15)); + if(regs.y < (regs.x << 1)) { + //if quotient is <= 511 (will fit into 9-bit result) + regs.a = ya / regs.x; + regs.y = ya % regs.x; + } else { + //otherwise, the quotient won't fit into regs.p.v + regs.a + //this emulates the odd behavior of the S-SMP in this case + regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); + regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); + } +//result is set based on a (quotient) only + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} diff --git a/bsnes/smp/ssmp/core/op_misc.cpp b/bsnes/smp/ssmp/core/op_misc.cpp new file mode 100755 index 0000000..a778b9e --- /dev/null +++ b/bsnes/smp/ssmp/core/op_misc.cpp @@ -0,0 +1,349 @@ +#ifdef SSMP_CPP + +//nop +case 0x00: { + op_io(); +} break; + +//sleep +case 0xef: { + op_io(); + op_io(); + regs.pc--; +} break; + +//stop +case 0xff: { + op_io(); + op_io(); + regs.pc--; +} break; + +//xcn +case 0x9f: { + op_io(); + op_io(); + op_io(); + op_io(); + regs.a = (regs.a >> 4) | (regs.a << 4); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//daa +case 0xdf: { + op_io(); + op_io(); + if(regs.p.c || (regs.a) > 0x99) { + regs.a += 0x60; + regs.p.c = 1; + } + if(regs.p.h || (regs.a & 15) > 0x09) { + regs.a += 0x06; + } + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//das +case 0xbe: { + op_io(); + op_io(); + if(!regs.p.c || (regs.a) > 0x99) { + regs.a -= 0x60; + regs.p.c = 0; + } + if(!regs.p.h || (regs.a & 15) > 0x09) { + regs.a -= 0x06; + } + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//clrc +case 0x60: { + op_io(); + regs.p.c = 0; +} break; + +//clrp +case 0x20: { + op_io(); + regs.p.p = 0; +} break; + +//setc +case 0x80: { + op_io(); + regs.p.c = 1; +} break; + +//setp +case 0x40: { + op_io(); + regs.p.p = 1; +} break; + +//clrv +case 0xe0: { + op_io(); + regs.p.v = 0; + regs.p.h = 0; +} break; + +//notc +case 0xed: { + op_io(); + op_io(); + regs.p.c = !regs.p.c; +} break; + +//ei +case 0xa0: { + op_io(); + op_io(); + regs.p.i = 1; +} break; + +//di +case 0xc0: { + op_io(); + op_io(); + regs.p.i = 0; +} break; + +//set0_dp +case 0x02: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x01; + op_writedp(dp, rd); +} break; + +//clr0_dp +case 0x12: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x01; + op_writedp(dp, rd); +} break; + +//set1_dp +case 0x22: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x02; + op_writedp(dp, rd); +} break; + +//clr1_dp +case 0x32: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x02; + op_writedp(dp, rd); +} break; + +//set2_dp +case 0x42: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x04; + op_writedp(dp, rd); +} break; + +//clr2_dp +case 0x52: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x04; + op_writedp(dp, rd); +} break; + +//set3_dp +case 0x62: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x08; + op_writedp(dp, rd); +} break; + +//clr3_dp +case 0x72: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x08; + op_writedp(dp, rd); +} break; + +//set4_dp +case 0x82: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x10; + op_writedp(dp, rd); +} break; + +//clr4_dp +case 0x92: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x10; + op_writedp(dp, rd); +} break; + +//set5_dp +case 0xa2: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x20; + op_writedp(dp, rd); +} break; + +//clr5_dp +case 0xb2: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x20; + op_writedp(dp, rd); +} break; + +//set6_dp +case 0xc2: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x40; + op_writedp(dp, rd); +} break; + +//clr6_dp +case 0xd2: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x40; + op_writedp(dp, rd); +} break; + +//set7_dp +case 0xe2: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x80; + op_writedp(dp, rd); +} break; + +//clr7_dp +case 0xf2: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x80; + op_writedp(dp, rd); +} break; + +//push_a +case 0x2d: { + op_io(); + op_io(); + op_writestack(regs.a); +} break; + +//push_x +case 0x4d: { + op_io(); + op_io(); + op_writestack(regs.x); +} break; + +//push_y +case 0x6d: { + op_io(); + op_io(); + op_writestack(regs.y); +} break; + +//push_p +case 0x0d: { + op_io(); + op_io(); + op_writestack(regs.p); +} break; + +//pop_a +case 0xae: { + op_io(); + op_io(); + regs.a = op_readstack(); +} break; + +//pop_x +case 0xce: { + op_io(); + op_io(); + regs.x = op_readstack(); +} break; + +//pop_y +case 0xee: { + op_io(); + op_io(); + regs.y = op_readstack(); +} break; + +//pop_p +case 0x8e: { + op_io(); + op_io(); + regs.p = op_readstack(); +} break; + +//mul_ya +case 0xcf: { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.y * regs.a; + regs.a = ya; + regs.y = ya >> 8; +//result is set based on y (high-byte) only + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); +} break; + +//div_ya_x +case 0x9e: { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.ya; +//overflow set if quotient >= 256 + regs.p.v = !!(regs.y >= regs.x); + regs.p.h = !!((regs.y & 15) >= (regs.x & 15)); + if(regs.y < (regs.x << 1)) { + //if quotient is <= 511 (will fit into 9-bit result) + regs.a = ya / regs.x; + regs.y = ya % regs.x; + } else { + //otherwise, the quotient won't fit into regs.p.v + regs.a + //this emulates the odd behavior of the S-SMP in this case + regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); + regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); + } +//result is set based on a (quotient) only + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +#endif diff --git a/bsnes/smp/ssmp/core/op_mov.b b/bsnes/smp/ssmp/core/op_mov.b new file mode 100755 index 0000000..dee821a --- /dev/null +++ b/bsnes/smp/ssmp/core/op_mov.b @@ -0,0 +1,217 @@ +mov_a_x(0x7d, a, x), +mov_a_y(0xdd, a, y), +mov_x_a(0x5d, x, a), +mov_y_a(0xfd, y, a), +mov_x_sp(0x9d, x, sp) { +1:op_io(); + regs.$1 = regs.$2; + regs.p.n = !!(regs.$1 & 0x80); + regs.p.z = (regs.$1 == 0); +} + +mov_sp_x(0xbd, sp, x) { +1:op_io(); + regs.$1 = regs.$2; +} + +mov_a_const(0xe8, a), +mov_x_const(0xcd, x), +mov_y_const(0x8d, y) { +1:regs.$1 = op_readpc(); + regs.p.n = !!(regs.$1 & 0x80); + regs.p.z = (regs.$1 == 0); +} + +mov_a_ix(0xe6) { +1:op_io(); +2:regs.a = op_readdp(regs.x); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +mov_a_ixinc(0xbf) { +1:op_io(); +2:regs.a = op_readdp(regs.x++); +3:op_io(); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +mov_a_dp(0xe4, a), +mov_x_dp(0xf8, x), +mov_y_dp(0xeb, y) { +1:sp = op_readpc(); +2:regs.$1 = op_readdp(sp); + regs.p.n = !!(regs.$1 & 0x80); + regs.p.z = (regs.$1 == 0); +} + +mov_a_dpx(0xf4, a, x), +mov_x_dpy(0xf9, x, y), +mov_y_dpx(0xfb, y, x) { +1:sp = op_readpc(); +2:op_io(); +3:regs.$1 = op_readdp(sp + regs.$2); + regs.p.n = !!(regs.$1 & 0x80); + regs.p.z = (regs.$1 == 0); +} + +mov_a_addr(0xe5, a), +mov_x_addr(0xe9, x), +mov_y_addr(0xec, y) { +1:sp = op_readpc(); +2:sp |= op_readpc() << 8; +3:regs.$1 = op_readaddr(sp); + regs.p.n = !!(regs.$1 & 0x80); + regs.p.z = (regs.$1 == 0); +} + +mov_a_addrx(0xf5, x), +mov_a_addry(0xf6, y) { +1:sp = op_readpc(); +2:sp |= op_readpc() << 8; +3:op_io(); +4:regs.a = op_readaddr(sp + regs.$1); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +mov_a_idpx(0xe7) { +1:dp = op_readpc() + regs.x; +2:op_io(); +3:sp = op_readdp(dp); +4:sp |= op_readdp(dp + 1) << 8; +5:regs.a = op_readaddr(sp); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +mov_a_idpy(0xf7) { +1:dp = op_readpc(); +2:op_io(); +3:sp = op_readdp(dp); +4:sp |= op_readdp(dp + 1) << 8; +5:regs.a = op_readaddr(sp + regs.y); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +mov_dp_dp(0xfa) { +1:sp = op_readpc(); +2:rd = op_readdp(sp); +3:dp = op_readpc(); +4:op_writedp(dp, rd); +} + +mov_dp_const(0x8f) { +1:rd = op_readpc(); +2:dp = op_readpc(); +3:op_readdp(dp); +4:op_writedp(dp, rd); +} + +mov_ix_a(0xc6) { +1:op_io(); +2:op_readdp(regs.x); +3:op_writedp(regs.x, regs.a); +} + +mov_ixinc_a(0xaf) { +1:op_io(); +2:op_io(); +3:op_writedp(regs.x++, regs.a); +} + +mov_dp_a(0xc4, a), +mov_dp_x(0xd8, x), +mov_dp_y(0xcb, y) { +1:dp = op_readpc(); +2:op_readdp(dp); +3:op_writedp(dp, regs.$1); +} + +mov_dpx_a(0xd4, x, a), +mov_dpy_x(0xd9, y, x), +mov_dpx_y(0xdb, x, y) { +1:dp = op_readpc(); +2:op_io(); + dp += regs.$1; +3:op_readdp(dp); +4:op_writedp(dp, regs.$2); +} + +mov_addr_a(0xc5, a), +mov_addr_x(0xc9, x), +mov_addr_y(0xcc, y) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:op_readaddr(dp); +4:op_writeaddr(dp, regs.$1); +} + +mov_addrx_a(0xd5, x), +mov_addry_a(0xd6, y) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:op_io(); + dp += regs.$1; +4:op_readaddr(dp); +5:op_writeaddr(dp, regs.a); +} + +mov_idpx_a(0xc7) { +1:sp = op_readpc(); +2:op_io(); + sp += regs.x; +3:dp = op_readdp(sp); +4:dp |= op_readdp(sp + 1) << 8; +5:op_readaddr(dp); +6:op_writeaddr(dp, regs.a); +} + +mov_idpy_a(0xd7) { +1:sp = op_readpc(); +2:dp = op_readdp(sp); +3:dp |= op_readdp(sp + 1) << 8; +4:op_io(); + dp += regs.y; +5:op_readaddr(dp); +6:op_writeaddr(dp, regs.a); +} + +movw_ya_dp(0xba) { +1:sp = op_readpc(); +2:regs.a = op_readdp(sp); +3:op_io(); +4:regs.y = op_readdp(sp + 1); + regs.p.n = !!(regs.ya & 0x8000); + regs.p.z = (regs.ya == 0); +} + +movw_dp_ya(0xda) { +1:dp = op_readpc(); +2:op_readdp(dp); +3:op_writedp(dp, regs.a); +4:op_writedp(dp + 1, regs.y); +} + +mov1_c_bit(0xaa) { +1:sp = op_readpc(); +2:sp |= op_readpc() << 8; +3:bit = sp >> 13; + sp &= 0x1fff; + rd = op_readaddr(sp); + regs.p.c = !!(rd & (1 << bit)); +} + +mov1_bit_c(0xca) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + if(regs.p.c)rd |= (1 << bit); + else rd &= ~(1 << bit); +4:op_io(); +5:op_writeaddr(dp, rd); +} diff --git a/bsnes/smp/ssmp/core/op_mov.cpp b/bsnes/smp/ssmp/core/op_mov.cpp new file mode 100755 index 0000000..0389039 --- /dev/null +++ b/bsnes/smp/ssmp/core/op_mov.cpp @@ -0,0 +1,392 @@ +#ifdef SSMP_CPP + +//mov_a_x +case 0x7d: { + op_io(); + regs.a = regs.x; + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//mov_a_y +case 0xdd: { + op_io(); + regs.a = regs.y; + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//mov_x_a +case 0x5d: { + op_io(); + regs.x = regs.a; + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); +} break; + +//mov_y_a +case 0xfd: { + op_io(); + regs.y = regs.a; + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); +} break; + +//mov_x_sp +case 0x9d: { + op_io(); + regs.x = regs.sp; + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); +} break; + +//mov_sp_x +case 0xbd: { + op_io(); + regs.sp = regs.x; +} break; + +//mov_a_const +case 0xe8: { + regs.a = op_readpc(); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//mov_x_const +case 0xcd: { + regs.x = op_readpc(); + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); +} break; + +//mov_y_const +case 0x8d: { + regs.y = op_readpc(); + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); +} break; + +//mov_a_ix +case 0xe6: { + op_io(); + regs.a = op_readdp(regs.x); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//mov_a_ixinc +case 0xbf: { + op_io(); + regs.a = op_readdp(regs.x++); + op_io(); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//mov_a_dp +case 0xe4: { + sp = op_readpc(); + regs.a = op_readdp(sp); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//mov_x_dp +case 0xf8: { + sp = op_readpc(); + regs.x = op_readdp(sp); + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); +} break; + +//mov_y_dp +case 0xeb: { + sp = op_readpc(); + regs.y = op_readdp(sp); + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); +} break; + +//mov_a_dpx +case 0xf4: { + sp = op_readpc(); + op_io(); + regs.a = op_readdp(sp + regs.x); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//mov_x_dpy +case 0xf9: { + sp = op_readpc(); + op_io(); + regs.x = op_readdp(sp + regs.y); + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); +} break; + +//mov_y_dpx +case 0xfb: { + sp = op_readpc(); + op_io(); + regs.y = op_readdp(sp + regs.x); + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); +} break; + +//mov_a_addr +case 0xe5: { + sp = op_readpc(); + sp |= op_readpc() << 8; + regs.a = op_readaddr(sp); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//mov_x_addr +case 0xe9: { + sp = op_readpc(); + sp |= op_readpc() << 8; + regs.x = op_readaddr(sp); + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); +} break; + +//mov_y_addr +case 0xec: { + sp = op_readpc(); + sp |= op_readpc() << 8; + regs.y = op_readaddr(sp); + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); +} break; + +//mov_a_addrx +case 0xf5: { + sp = op_readpc(); + sp |= op_readpc() << 8; + op_io(); + regs.a = op_readaddr(sp + regs.x); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//mov_a_addry +case 0xf6: { + sp = op_readpc(); + sp |= op_readpc() << 8; + op_io(); + regs.a = op_readaddr(sp + regs.y); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//mov_a_idpx +case 0xe7: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + regs.a = op_readaddr(sp); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//mov_a_idpy +case 0xf7: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + regs.a = op_readaddr(sp + regs.y); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} break; + +//mov_dp_dp +case 0xfa: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + op_writedp(dp, rd); +} break; + +//mov_dp_const +case 0x8f: { + rd = op_readpc(); + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, rd); +} break; + +//mov_ix_a +case 0xc6: { + op_io(); + op_readdp(regs.x); + op_writedp(regs.x, regs.a); +} break; + +//mov_ixinc_a +case 0xaf: { + op_io(); + op_io(); + op_writedp(regs.x++, regs.a); +} break; + +//mov_dp_a +case 0xc4: { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.a); +} break; + +//mov_dp_x +case 0xd8: { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.x); +} break; + +//mov_dp_y +case 0xcb: { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.y); +} break; + +//mov_dpx_a +case 0xd4: { + dp = op_readpc(); + op_io(); + dp += regs.x; + op_readdp(dp); + op_writedp(dp, regs.a); +} break; + +//mov_dpy_x +case 0xd9: { + dp = op_readpc(); + op_io(); + dp += regs.y; + op_readdp(dp); + op_writedp(dp, regs.x); +} break; + +//mov_dpx_y +case 0xdb: { + dp = op_readpc(); + op_io(); + dp += regs.x; + op_readdp(dp); + op_writedp(dp, regs.y); +} break; + +//mov_addr_a +case 0xc5: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.a); +} break; + +//mov_addr_x +case 0xc9: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.x); +} break; + +//mov_addr_y +case 0xcc: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.y); +} break; + +//mov_addrx_a +case 0xd5: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + dp += regs.x; + op_readaddr(dp); + op_writeaddr(dp, regs.a); +} break; + +//mov_addry_a +case 0xd6: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + dp += regs.y; + op_readaddr(dp); + op_writeaddr(dp, regs.a); +} break; + +//mov_idpx_a +case 0xc7: { + sp = op_readpc(); + op_io(); + sp += regs.x; + dp = op_readdp(sp); + dp |= op_readdp(sp + 1) << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.a); +} break; + +//mov_idpy_a +case 0xd7: { + sp = op_readpc(); + dp = op_readdp(sp); + dp |= op_readdp(sp + 1) << 8; + op_io(); + dp += regs.y; + op_readaddr(dp); + op_writeaddr(dp, regs.a); +} break; + +//movw_ya_dp +case 0xba: { + sp = op_readpc(); + regs.a = op_readdp(sp); + op_io(); + regs.y = op_readdp(sp + 1); + regs.p.n = !!(regs.ya & 0x8000); + regs.p.z = (regs.ya == 0); +} break; + +//movw_dp_ya +case 0xda: { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.a); + op_writedp(dp + 1, regs.y); +} break; + +//mov1_c_bit +case 0xaa: { + sp = op_readpc(); + sp |= op_readpc() << 8; + bit = sp >> 13; + sp &= 0x1fff; + rd = op_readaddr(sp); + regs.p.c = !!(rd & (1 << bit)); +} break; + +//mov1_bit_c +case 0xca: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + if(regs.p.c)rd |= (1 << bit); + else rd &= ~(1 << bit); + op_io(); + op_writeaddr(dp, rd); +} break; + +#endif diff --git a/bsnes/smp/ssmp/core/op_pc.b b/bsnes/smp/ssmp/core/op_pc.b new file mode 100755 index 0000000..affaf84 --- /dev/null +++ b/bsnes/smp/ssmp/core/op_pc.b @@ -0,0 +1,179 @@ +bra(0x2f, 0), +beq(0xf0, !regs.p.z), +bne(0xd0, regs.p.z), +bcs(0xb0, !regs.p.c), +bcc(0x90, regs.p.c), +bvs(0x70, !regs.p.v), +bvc(0x50, regs.p.v), +bmi(0x30, !regs.p.n), +bpl(0x10, regs.p.n) { +1:rd = op_readpc(); + if($1)end; +2:op_io(); +3:op_io(); + regs.pc += (int8)rd; +} + +bbs0(0x03, 0x01, !=), +bbc0(0x13, 0x01, ==), +bbs1(0x23, 0x02, !=), +bbc1(0x33, 0x02, ==), +bbs2(0x43, 0x04, !=), +bbc2(0x53, 0x04, ==), +bbs3(0x63, 0x08, !=), +bbc3(0x73, 0x08, ==), +bbs4(0x83, 0x10, !=), +bbc4(0x93, 0x10, ==), +bbs5(0xa3, 0x20, !=), +bbc5(0xb3, 0x20, ==), +bbs6(0xc3, 0x40, !=), +bbc6(0xd3, 0x40, ==), +bbs7(0xe3, 0x80, !=), +bbc7(0xf3, 0x80, ==) { +1:dp = op_readpc(); +2:sp = op_readdp(dp); +3:rd = op_readpc(); +4:op_io(); + if((sp & $1) $2 $1)end; +5:op_io(); +6:op_io(); + regs.pc += (int8)rd; +} + +cbne_dp(0x2e) { +1:dp = op_readpc(); +2:sp = op_readdp(dp); +3:rd = op_readpc(); +4:op_io(); + if(regs.a == sp)end; +5:op_io(); +6:op_io(); + regs.pc += (int8)rd; +} + +cbne_dpx(0xde) { +1:dp = op_readpc(); +2:op_io(); +3:sp = op_readdp(dp + regs.x); +4:rd = op_readpc(); +5:op_io(); + if(regs.a == sp)end; +6:op_io(); +7:op_io(); + regs.pc += (int8)rd; +} + +dbnz_dp(0x6e) { +1:dp = op_readpc(); +2:wr = op_readdp(dp); +3:op_writedp(dp, --wr); +4:rd = op_readpc(); + if(wr == 0x00)end; +5:op_io(); +6:op_io(); + regs.pc += (int8)rd; +} + +dbnz_y(0xfe) { +1:rd = op_readpc(); +2:op_io(); + regs.y--; +3:op_io(); + if(regs.y == 0x00)end; +4:op_io(); +5:op_io(); + regs.pc += (int8)rd; +} + +jmp_addr(0x5f) { +1:rd = op_readpc(); +2:rd |= op_readpc() << 8; + regs.pc = rd; +} + +jmp_iaddrx(0x1f) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:op_io(); + dp += regs.x; +4:rd = op_readaddr(dp); +5:rd |= op_readaddr(dp + 1) << 8; + regs.pc = rd; +} + +call(0x3f) { +1:rd = op_readpc(); +2:rd |= op_readpc() << 8; +3:op_io(); +4:op_io(); +5:op_io(); +6:op_writestack(regs.pc >> 8); +7:op_writestack(regs.pc); + regs.pc = rd; +} + +pcall(0x4f) { +1:rd = op_readpc(); +2:op_io(); +3:op_io(); +4:op_writestack(regs.pc >> 8); +5:op_writestack(regs.pc); + regs.pc = 0xff00 | rd; +} + +tcall_0(0x01, 0), +tcall_1(0x11, 1), +tcall_2(0x21, 2), +tcall_3(0x31, 3), +tcall_4(0x41, 4), +tcall_5(0x51, 5), +tcall_6(0x61, 6), +tcall_7(0x71, 7), +tcall_8(0x81, 8), +tcall_9(0x91, 9), +tcall_10(0xa1, 10), +tcall_11(0xb1, 11), +tcall_12(0xc1, 12), +tcall_13(0xd1, 13), +tcall_14(0xe1, 14), +tcall_15(0xf1, 15) { +1:dp = 0xffde - ($1 << 1); + rd = op_readaddr(dp); +2:rd |= op_readaddr(dp + 1) << 8; +3:op_io(); +4:op_io(); +5:op_io(); +6:op_writestack(regs.pc >> 8); +7:op_writestack(regs.pc); + regs.pc = rd; +} + +brk(0x0f) { +1:rd = op_readaddr(0xffde); +2:rd |= op_readaddr(0xffdf) << 8; +3:op_io(); +4:op_io(); +5:op_writestack(regs.pc >> 8); +6:op_writestack(regs.pc); +7:op_writestack(regs.p); + regs.pc = rd; + regs.p.b = 1; + regs.p.i = 0; +} + +ret(0x6f) { +1:rd = op_readstack(); +2:rd |= op_readstack() << 8; +3:op_io(); +4:op_io(); + regs.pc = rd; +} + +reti(0x7f) { +1:regs.p = op_readstack(); +2:rd = op_readstack(); +3:rd |= op_readstack() << 8; +4:op_io(); +5:op_io(); + regs.pc = rd; +} diff --git a/bsnes/smp/ssmp/core/op_pc.cpp b/bsnes/smp/ssmp/core/op_pc.cpp new file mode 100755 index 0000000..b9b5992 --- /dev/null +++ b/bsnes/smp/ssmp/core/op_pc.cpp @@ -0,0 +1,606 @@ +#ifdef SSMP_CPP + +//bra +case 0x2f: { + rd = op_readpc(); + if(0)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//beq +case 0xf0: { + rd = op_readpc(); + if(!regs.p.z)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bne +case 0xd0: { + rd = op_readpc(); + if(regs.p.z)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bcs +case 0xb0: { + rd = op_readpc(); + if(!regs.p.c)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bcc +case 0x90: { + rd = op_readpc(); + if(regs.p.c)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bvs +case 0x70: { + rd = op_readpc(); + if(!regs.p.v)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bvc +case 0x50: { + rd = op_readpc(); + if(regs.p.v)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bmi +case 0x30: { + rd = op_readpc(); + if(!regs.p.n)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bpl +case 0x10: { + rd = op_readpc(); + if(regs.p.n)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbs0 +case 0x03: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x01) != 0x01)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbc0 +case 0x13: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x01) == 0x01)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbs1 +case 0x23: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x02) != 0x02)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbc1 +case 0x33: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x02) == 0x02)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbs2 +case 0x43: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x04) != 0x04)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbc2 +case 0x53: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x04) == 0x04)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbs3 +case 0x63: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x08) != 0x08)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbc3 +case 0x73: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x08) == 0x08)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbs4 +case 0x83: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x10) != 0x10)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbc4 +case 0x93: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x10) == 0x10)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbs5 +case 0xa3: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x20) != 0x20)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbc5 +case 0xb3: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x20) == 0x20)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbs6 +case 0xc3: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x40) != 0x40)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbc6 +case 0xd3: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x40) == 0x40)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbs7 +case 0xe3: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x80) != 0x80)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//bbc7 +case 0xf3: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x80) == 0x80)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//cbne_dp +case 0x2e: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if(regs.a == sp)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//cbne_dpx +case 0xde: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + regs.x); + rd = op_readpc(); + op_io(); + if(regs.a == sp)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//dbnz_dp +case 0x6e: { + dp = op_readpc(); + wr = op_readdp(dp); + op_writedp(dp, --wr); + rd = op_readpc(); + if(wr == 0x00)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//dbnz_y +case 0xfe: { + rd = op_readpc(); + op_io(); + regs.y--; + op_io(); + if(regs.y == 0x00)break; + op_io(); + op_io(); + regs.pc += (int8)rd; +} break; + +//jmp_addr +case 0x5f: { + rd = op_readpc(); + rd |= op_readpc() << 8; + regs.pc = rd; +} break; + +//jmp_iaddrx +case 0x1f: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + dp += regs.x; + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + regs.pc = rd; +} break; + +//call +case 0x3f: { + rd = op_readpc(); + rd |= op_readpc() << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//pcall +case 0x4f: { + rd = op_readpc(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = 0xff00 | rd; +} break; + +//tcall_0 +case 0x01: { + dp = 0xffde - (0 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_1 +case 0x11: { + dp = 0xffde - (1 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_2 +case 0x21: { + dp = 0xffde - (2 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_3 +case 0x31: { + dp = 0xffde - (3 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_4 +case 0x41: { + dp = 0xffde - (4 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_5 +case 0x51: { + dp = 0xffde - (5 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_6 +case 0x61: { + dp = 0xffde - (6 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_7 +case 0x71: { + dp = 0xffde - (7 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_8 +case 0x81: { + dp = 0xffde - (8 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_9 +case 0x91: { + dp = 0xffde - (9 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_10 +case 0xa1: { + dp = 0xffde - (10 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_11 +case 0xb1: { + dp = 0xffde - (11 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_12 +case 0xc1: { + dp = 0xffde - (12 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_13 +case 0xd1: { + dp = 0xffde - (13 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_14 +case 0xe1: { + dp = 0xffde - (14 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//tcall_15 +case 0xf1: { + dp = 0xffde - (15 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; +} break; + +//brk +case 0x0f: { + rd = op_readaddr(0xffde); + rd |= op_readaddr(0xffdf) << 8; + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + op_writestack(regs.p); + regs.pc = rd; + regs.p.b = 1; + regs.p.i = 0; +} break; + +//ret +case 0x6f: { + rd = op_readstack(); + rd |= op_readstack() << 8; + op_io(); + op_io(); + regs.pc = rd; +} break; + +//reti +case 0x7f: { + regs.p = op_readstack(); + rd = op_readstack(); + rd |= op_readstack() << 8; + op_io(); + op_io(); + regs.pc = rd; +} break; + +#endif diff --git a/bsnes/smp/ssmp/core/op_read.b b/bsnes/smp/ssmp/core/op_read.b new file mode 100755 index 0000000..fd2f9d8 --- /dev/null +++ b/bsnes/smp/ssmp/core/op_read.b @@ -0,0 +1,205 @@ +adc_a_const(0x88, adc, a), +and_a_const(0x28, and, a), +cmp_a_const(0x68, cmp, a), +cmp_x_const(0xc8, cmp, x), +cmp_y_const(0xad, cmp, y), +eor_a_const(0x48, eor, a), +or_a_const(0x08, or, a), +sbc_a_const(0xa8, sbc, a) { +1:rd = op_readpc(); + regs.$2 = op_$1(regs.$2, rd); +} + +adc_a_ix(0x86, adc), +and_a_ix(0x26, and), +cmp_a_ix(0x66, cmp), +eor_a_ix(0x46, eor), +or_a_ix(0x06, or), +sbc_a_ix(0xa6, sbc) { +1:op_io(); +2:rd = op_readdp(regs.x); + regs.a = op_$1(regs.a, rd); +} + +adc_a_dp(0x84, adc, a), +and_a_dp(0x24, and, a), +cmp_a_dp(0x64, cmp, a), +cmp_x_dp(0x3e, cmp, x), +cmp_y_dp(0x7e, cmp, y), +eor_a_dp(0x44, eor, a), +or_a_dp(0x04, or, a), +sbc_a_dp(0xa4, sbc, a) { +1:dp = op_readpc(); +2:rd = op_readdp(dp); + regs.$2 = op_$1(regs.$2, rd); +} + +adc_a_dpx(0x94, adc), +and_a_dpx(0x34, and), +cmp_a_dpx(0x74, cmp), +eor_a_dpx(0x54, eor), +or_a_dpx(0x14, or), +sbc_a_dpx(0xb4, sbc) { +1:dp = op_readpc(); +2:op_io(); +3:rd = op_readdp(dp + regs.x); + regs.a = op_$1(regs.a, rd); +} + +adc_a_addr(0x85, adc, a), +and_a_addr(0x25, and, a), +cmp_a_addr(0x65, cmp, a), +cmp_x_addr(0x1e, cmp, x), +cmp_y_addr(0x5e, cmp, y), +eor_a_addr(0x45, eor, a), +or_a_addr(0x05, or, a), +sbc_a_addr(0xa5, sbc, a) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:rd = op_readaddr(dp); + regs.$2 = op_$1(regs.$2, rd); +} + +adc_a_addrx(0x95, adc, x), +adc_a_addry(0x96, adc, y), +and_a_addrx(0x35, and, x), +and_a_addry(0x36, and, y), +cmp_a_addrx(0x75, cmp, x), +cmp_a_addry(0x76, cmp, y), +eor_a_addrx(0x55, eor, x), +eor_a_addry(0x56, eor, y), +or_a_addrx(0x15, or, x), +or_a_addry(0x16, or, y), +sbc_a_addrx(0xb5, sbc, x), +sbc_a_addry(0xb6, sbc, y) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:op_io(); +4:rd = op_readaddr(dp + regs.$2); + regs.a = op_$1(regs.a, rd); +} + +adc_a_idpx(0x87, adc), +and_a_idpx(0x27, and), +cmp_a_idpx(0x67, cmp), +eor_a_idpx(0x47, eor), +or_a_idpx(0x07, or), +sbc_a_idpx(0xa7, sbc) { +1:dp = op_readpc() + regs.x; +2:op_io(); +3:sp = op_readdp(dp); +4:sp |= op_readdp(dp + 1) << 8; +5:rd = op_readaddr(sp); + regs.a = op_$1(regs.a, rd); +} + +adc_a_idpy(0x97, adc), +and_a_idpy(0x37, and), +cmp_a_idpy(0x77, cmp), +eor_a_idpy(0x57, eor), +or_a_idpy(0x17, or), +sbc_a_idpy(0xb7, sbc) { +1:dp = op_readpc(); +2:op_io(); +3:sp = op_readdp(dp); +4:sp |= op_readdp(dp + 1) << 8; +5:rd = op_readaddr(sp + regs.y); + regs.a = op_$1(regs.a, rd); +} + +adc_ix_iy(0x99, adc, 1), +and_ix_iy(0x39, and, 1), +cmp_ix_iy(0x79, cmp, 0), +eor_ix_iy(0x59, eor, 1), +or_ix_iy(0x19, or, 1), +sbc_ix_iy(0xb9, sbc, 1) { +1:op_io(); +2:rd = op_readdp(regs.y); +3:wr = op_readdp(regs.x); + wr = op_$1(wr, rd); +4:($2) ? op_writedp(regs.x, wr) : op_io(); +} + +adc_dp_dp(0x89, adc, 1), +and_dp_dp(0x29, and, 1), +cmp_dp_dp(0x69, cmp, 0), +eor_dp_dp(0x49, eor, 1), +or_dp_dp(0x09, or, 1), +sbc_dp_dp(0xa9, sbc, 1) { +1:sp = op_readpc(); +2:rd = op_readdp(sp); +3:dp = op_readpc(); +4:wr = op_readdp(dp); +5:wr = op_$1(wr, rd); + ($2) ? op_writedp(dp, wr) : op_io(); +} + +adc_dp_const(0x98, adc, 1), +and_dp_const(0x38, and, 1), +cmp_dp_const(0x78, cmp, 0), +eor_dp_const(0x58, eor, 1), +or_dp_const(0x18, or, 1), +sbc_dp_const(0xb8, sbc, 1) { +1:rd = op_readpc(); +2:dp = op_readpc(); +3:wr = op_readdp(dp); +4:wr = op_$1(wr, rd); + ($2) ? op_writedp(dp, wr) : op_io(); +} + +addw_ya_dp(0x7a, addw), +subw_ya_dp(0x9a, subw) { +1:dp = op_readpc(); +2:rd = op_readdp(dp); +3:op_io(); +4:rd |= op_readdp(dp + 1) << 8; + regs.ya = op_$1(regs.ya, rd); +} + +cmpw_ya_dp(0x5a) { +1:dp = op_readpc(); +2:rd = op_readdp(dp); +3:rd |= op_readdp(dp + 1) << 8; + op_cmpw(regs.ya, rd); +} + +and1_bit(0x4a, !!), +and1_notbit(0x6a, !) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + regs.p.c = regs.p.c & $1(rd & (1 << bit)); +} + +eor1_bit(0x8a) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); +4:op_io(); + regs.p.c = regs.p.c ^ !!(rd & (1 << bit)); +} + +not1_bit(0xea) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + rd ^= (1 << bit); +4:op_writeaddr(dp, rd); +} + +or1_bit(0x0a, !!), +or1_notbit(0x2a, !) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); +4:op_io(); + regs.p.c = regs.p.c | $1(rd & (1 << bit)); +} diff --git a/bsnes/smp/ssmp/core/op_read.cpp b/bsnes/smp/ssmp/core/op_read.cpp new file mode 100755 index 0000000..3f8f77f --- /dev/null +++ b/bsnes/smp/ssmp/core/op_read.cpp @@ -0,0 +1,747 @@ +#ifdef SSMP_CPP + +//adc_a_const +case 0x88: { + rd = op_readpc(); + regs.a = op_adc(regs.a, rd); +} break; + +//and_a_const +case 0x28: { + rd = op_readpc(); + regs.a = op_and(regs.a, rd); +} break; + +//cmp_a_const +case 0x68: { + rd = op_readpc(); + regs.a = op_cmp(regs.a, rd); +} break; + +//cmp_x_const +case 0xc8: { + rd = op_readpc(); + regs.x = op_cmp(regs.x, rd); +} break; + +//cmp_y_const +case 0xad: { + rd = op_readpc(); + regs.y = op_cmp(regs.y, rd); +} break; + +//eor_a_const +case 0x48: { + rd = op_readpc(); + regs.a = op_eor(regs.a, rd); +} break; + +//or_a_const +case 0x08: { + rd = op_readpc(); + regs.a = op_or(regs.a, rd); +} break; + +//sbc_a_const +case 0xa8: { + rd = op_readpc(); + regs.a = op_sbc(regs.a, rd); +} break; + +//adc_a_ix +case 0x86: { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_adc(regs.a, rd); +} break; + +//and_a_ix +case 0x26: { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_and(regs.a, rd); +} break; + +//cmp_a_ix +case 0x66: { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_cmp(regs.a, rd); +} break; + +//eor_a_ix +case 0x46: { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_eor(regs.a, rd); +} break; + +//or_a_ix +case 0x06: { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_or(regs.a, rd); +} break; + +//sbc_a_ix +case 0xa6: { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_sbc(regs.a, rd); +} break; + +//adc_a_dp +case 0x84: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_adc(regs.a, rd); +} break; + +//and_a_dp +case 0x24: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_and(regs.a, rd); +} break; + +//cmp_a_dp +case 0x64: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_cmp(regs.a, rd); +} break; + +//cmp_x_dp +case 0x3e: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.x = op_cmp(regs.x, rd); +} break; + +//cmp_y_dp +case 0x7e: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.y = op_cmp(regs.y, rd); +} break; + +//eor_a_dp +case 0x44: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_eor(regs.a, rd); +} break; + +//or_a_dp +case 0x04: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_or(regs.a, rd); +} break; + +//sbc_a_dp +case 0xa4: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_sbc(regs.a, rd); +} break; + +//adc_a_dpx +case 0x94: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_adc(regs.a, rd); +} break; + +//and_a_dpx +case 0x34: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_and(regs.a, rd); +} break; + +//cmp_a_dpx +case 0x74: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_cmp(regs.a, rd); +} break; + +//eor_a_dpx +case 0x54: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_eor(regs.a, rd); +} break; + +//or_a_dpx +case 0x14: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_or(regs.a, rd); +} break; + +//sbc_a_dpx +case 0xb4: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_sbc(regs.a, rd); +} break; + +//adc_a_addr +case 0x85: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_adc(regs.a, rd); +} break; + +//and_a_addr +case 0x25: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_and(regs.a, rd); +} break; + +//cmp_a_addr +case 0x65: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_cmp(regs.a, rd); +} break; + +//cmp_x_addr +case 0x1e: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.x = op_cmp(regs.x, rd); +} break; + +//cmp_y_addr +case 0x5e: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.y = op_cmp(regs.y, rd); +} break; + +//eor_a_addr +case 0x45: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_eor(regs.a, rd); +} break; + +//or_a_addr +case 0x05: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_or(regs.a, rd); +} break; + +//sbc_a_addr +case 0xa5: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_sbc(regs.a, rd); +} break; + +//adc_a_addrx +case 0x95: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_adc(regs.a, rd); +} break; + +//adc_a_addry +case 0x96: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_adc(regs.a, rd); +} break; + +//and_a_addrx +case 0x35: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_and(regs.a, rd); +} break; + +//and_a_addry +case 0x36: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_and(regs.a, rd); +} break; + +//cmp_a_addrx +case 0x75: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_cmp(regs.a, rd); +} break; + +//cmp_a_addry +case 0x76: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_cmp(regs.a, rd); +} break; + +//eor_a_addrx +case 0x55: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_eor(regs.a, rd); +} break; + +//eor_a_addry +case 0x56: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_eor(regs.a, rd); +} break; + +//or_a_addrx +case 0x15: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_or(regs.a, rd); +} break; + +//or_a_addry +case 0x16: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_or(regs.a, rd); +} break; + +//sbc_a_addrx +case 0xb5: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_sbc(regs.a, rd); +} break; + +//sbc_a_addry +case 0xb6: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_sbc(regs.a, rd); +} break; + +//adc_a_idpx +case 0x87: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_adc(regs.a, rd); +} break; + +//and_a_idpx +case 0x27: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_and(regs.a, rd); +} break; + +//cmp_a_idpx +case 0x67: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_cmp(regs.a, rd); +} break; + +//eor_a_idpx +case 0x47: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_eor(regs.a, rd); +} break; + +//or_a_idpx +case 0x07: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_or(regs.a, rd); +} break; + +//sbc_a_idpx +case 0xa7: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_sbc(regs.a, rd); +} break; + +//adc_a_idpy +case 0x97: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_adc(regs.a, rd); +} break; + +//and_a_idpy +case 0x37: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_and(regs.a, rd); +} break; + +//cmp_a_idpy +case 0x77: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_cmp(regs.a, rd); +} break; + +//eor_a_idpy +case 0x57: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_eor(regs.a, rd); +} break; + +//or_a_idpy +case 0x17: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_or(regs.a, rd); +} break; + +//sbc_a_idpy +case 0xb7: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_sbc(regs.a, rd); +} break; + +//adc_ix_iy +case 0x99: { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_adc(wr, rd); + (1) ? op_writedp(regs.x, wr) : op_io(); +} break; + +//and_ix_iy +case 0x39: { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_and(wr, rd); + (1) ? op_writedp(regs.x, wr) : op_io(); +} break; + +//cmp_ix_iy +case 0x79: { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_cmp(wr, rd); + (0) ? op_writedp(regs.x, wr) : op_io(); +} break; + +//eor_ix_iy +case 0x59: { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_eor(wr, rd); + (1) ? op_writedp(regs.x, wr) : op_io(); +} break; + +//or_ix_iy +case 0x19: { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_or(wr, rd); + (1) ? op_writedp(regs.x, wr) : op_io(); +} break; + +//sbc_ix_iy +case 0xb9: { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_sbc(wr, rd); + (1) ? op_writedp(regs.x, wr) : op_io(); +} break; + +//adc_dp_dp +case 0x89: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_adc(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); +} break; + +//and_dp_dp +case 0x29: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_and(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); +} break; + +//cmp_dp_dp +case 0x69: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_cmp(wr, rd); + (0) ? op_writedp(dp, wr) : op_io(); +} break; + +//eor_dp_dp +case 0x49: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_eor(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); +} break; + +//or_dp_dp +case 0x09: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_or(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); +} break; + +//sbc_dp_dp +case 0xa9: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_sbc(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); +} break; + +//adc_dp_const +case 0x98: { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_adc(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); +} break; + +//and_dp_const +case 0x38: { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_and(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); +} break; + +//cmp_dp_const +case 0x78: { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_cmp(wr, rd); + (0) ? op_writedp(dp, wr) : op_io(); +} break; + +//eor_dp_const +case 0x58: { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_eor(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); +} break; + +//or_dp_const +case 0x18: { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_or(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); +} break; + +//sbc_dp_const +case 0xb8: { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_sbc(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); +} break; + +//addw_ya_dp +case 0x7a: { + dp = op_readpc(); + rd = op_readdp(dp); + op_io(); + rd |= op_readdp(dp + 1) << 8; + regs.ya = op_addw(regs.ya, rd); +} break; + +//subw_ya_dp +case 0x9a: { + dp = op_readpc(); + rd = op_readdp(dp); + op_io(); + rd |= op_readdp(dp + 1) << 8; + regs.ya = op_subw(regs.ya, rd); +} break; + +//cmpw_ya_dp +case 0x5a: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= op_readdp(dp + 1) << 8; + op_cmpw(regs.ya, rd); +} break; + +//and1_bit +case 0x4a: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + regs.p.c = regs.p.c & !!(rd & (1 << bit)); +} break; + +//and1_notbit +case 0x6a: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + regs.p.c = regs.p.c & !(rd & (1 << bit)); +} break; + +//eor1_bit +case 0x8a: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c ^ !!(rd & (1 << bit)); +} break; + +//not1_bit +case 0xea: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + rd ^= (1 << bit); + op_writeaddr(dp, rd); +} break; + +//or1_bit +case 0x0a: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c | !!(rd & (1 << bit)); +} break; + +//or1_notbit +case 0x2a: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c | !(rd & (1 << bit)); +} break; + +#endif diff --git a/bsnes/smp/ssmp/core/op_rmw.b b/bsnes/smp/ssmp/core/op_rmw.b new file mode 100755 index 0000000..425574e --- /dev/null +++ b/bsnes/smp/ssmp/core/op_rmw.b @@ -0,0 +1,74 @@ +inc_a(0xbc, inc, a), +inc_x(0x3d, inc, x), +inc_y(0xfc, inc, y), +dec_a(0x9c, dec, a), +dec_x(0x1d, dec, x), +dec_y(0xdc, dec, y), +asl_a(0x1c, asl, a), +lsr_a(0x5c, lsr, a), +rol_a(0x3c, rol, a), +ror_a(0x7c, ror, a) { +1:op_io(); + regs.$2 = op_$1(regs.$2); +} + +inc_dp(0xab, inc), +dec_dp(0x8b, dec), +asl_dp(0x0b, asl), +lsr_dp(0x4b, lsr), +rol_dp(0x2b, rol), +ror_dp(0x6b, ror) { +1:dp = op_readpc(); +2:rd = op_readdp(dp); +3:rd = op_$1(rd); + op_writedp(dp, rd); +} + +inc_dpx(0xbb, inc), +dec_dpx(0x9b, dec), +asl_dpx(0x1b, asl), +lsr_dpx(0x5b, lsr), +rol_dpx(0x3b, rol), +ror_dpx(0x7b, ror) { +1:dp = op_readpc(); +2:op_io(); +3:rd = op_readdp(dp + regs.x); +4:rd = op_$1(rd); + op_writedp(dp + regs.x, rd); +} + +inc_addr(0xac, inc), +dec_addr(0x8c, dec), +asl_addr(0x0c, asl), +lsr_addr(0x4c, lsr), +rol_addr(0x2c, rol), +ror_addr(0x6c, ror) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:rd = op_readaddr(dp); +4:rd = op_$1(rd); + op_writeaddr(dp, rd); +} + +tset_addr_a(0x0e, |), +tclr_addr_a(0x4e, &~) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:rd = op_readaddr(dp); + regs.p.n = !!((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); +4:op_readaddr(dp); +5:op_writeaddr(dp, rd $1 regs.a); +} + +incw_dp(0x3a, ++), +decw_dp(0x1a, --) { +1:dp = op_readpc(); +2:rd = op_readdp(dp); + rd$1; +3:op_writedp(dp++, rd); +4:rd += op_readdp(dp) << 8; +5:op_writedp(dp, rd >> 8); + regs.p.n = !!(rd & 0x8000); + regs.p.z = (rd == 0); +} diff --git a/bsnes/smp/ssmp/core/op_rmw.cpp b/bsnes/smp/ssmp/core/op_rmw.cpp new file mode 100755 index 0000000..7bba31b --- /dev/null +++ b/bsnes/smp/ssmp/core/op_rmw.cpp @@ -0,0 +1,265 @@ +#ifdef SSMP_CPP + +//inc_a +case 0xbc: { + op_io(); + regs.a = op_inc(regs.a); +} break; + +//inc_x +case 0x3d: { + op_io(); + regs.x = op_inc(regs.x); +} break; + +//inc_y +case 0xfc: { + op_io(); + regs.y = op_inc(regs.y); +} break; + +//dec_a +case 0x9c: { + op_io(); + regs.a = op_dec(regs.a); +} break; + +//dec_x +case 0x1d: { + op_io(); + regs.x = op_dec(regs.x); +} break; + +//dec_y +case 0xdc: { + op_io(); + regs.y = op_dec(regs.y); +} break; + +//asl_a +case 0x1c: { + op_io(); + regs.a = op_asl(regs.a); +} break; + +//lsr_a +case 0x5c: { + op_io(); + regs.a = op_lsr(regs.a); +} break; + +//rol_a +case 0x3c: { + op_io(); + regs.a = op_rol(regs.a); +} break; + +//ror_a +case 0x7c: { + op_io(); + regs.a = op_ror(regs.a); +} break; + +//inc_dp +case 0xab: { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_inc(rd); + op_writedp(dp, rd); +} break; + +//dec_dp +case 0x8b: { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_dec(rd); + op_writedp(dp, rd); +} break; + +//asl_dp +case 0x0b: { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_asl(rd); + op_writedp(dp, rd); +} break; + +//lsr_dp +case 0x4b: { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_lsr(rd); + op_writedp(dp, rd); +} break; + +//rol_dp +case 0x2b: { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_rol(rd); + op_writedp(dp, rd); +} break; + +//ror_dp +case 0x6b: { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_ror(rd); + op_writedp(dp, rd); +} break; + +//inc_dpx +case 0xbb: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_inc(rd); + op_writedp(dp + regs.x, rd); +} break; + +//dec_dpx +case 0x9b: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_dec(rd); + op_writedp(dp + regs.x, rd); +} break; + +//asl_dpx +case 0x1b: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_asl(rd); + op_writedp(dp + regs.x, rd); +} break; + +//lsr_dpx +case 0x5b: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_lsr(rd); + op_writedp(dp + regs.x, rd); +} break; + +//rol_dpx +case 0x3b: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_rol(rd); + op_writedp(dp + regs.x, rd); +} break; + +//ror_dpx +case 0x7b: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_ror(rd); + op_writedp(dp + regs.x, rd); +} break; + +//inc_addr +case 0xac: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_inc(rd); + op_writeaddr(dp, rd); +} break; + +//dec_addr +case 0x8c: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_dec(rd); + op_writeaddr(dp, rd); +} break; + +//asl_addr +case 0x0c: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_asl(rd); + op_writeaddr(dp, rd); +} break; + +//lsr_addr +case 0x4c: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_lsr(rd); + op_writeaddr(dp, rd); +} break; + +//rol_addr +case 0x2c: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_rol(rd); + op_writeaddr(dp, rd); +} break; + +//ror_addr +case 0x6c: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_ror(rd); + op_writeaddr(dp, rd); +} break; + +//tset_addr_a +case 0x0e: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.p.n = !!((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); + op_readaddr(dp); + op_writeaddr(dp, rd | regs.a); +} break; + +//tclr_addr_a +case 0x4e: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.p.n = !!((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); + op_readaddr(dp); + op_writeaddr(dp, rd &~ regs.a); +} break; + +//incw_dp +case 0x3a: { + dp = op_readpc(); + rd = op_readdp(dp); + rd++; + op_writedp(dp++, rd); + rd += op_readdp(dp) << 8; + op_writedp(dp, rd >> 8); + regs.p.n = !!(rd & 0x8000); + regs.p.z = (rd == 0); +} break; + +//decw_dp +case 0x1a: { + dp = op_readpc(); + rd = op_readdp(dp); + rd--; + op_writedp(dp++, rd); + rd += op_readdp(dp) << 8; + op_writedp(dp, rd >> 8); + regs.p.n = !!(rd & 0x8000); + regs.p.z = (rd == 0); +} break; + +#endif diff --git a/bsnes/smp/ssmp/core/opfn.cpp b/bsnes/smp/ssmp/core/opfn.cpp new file mode 100755 index 0000000..8e9b3ea --- /dev/null +++ b/bsnes/smp/ssmp/core/opfn.cpp @@ -0,0 +1,126 @@ +#ifdef SSMP_CPP + +uint8 sSMP::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 sSMP::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 sSMP::op_and(uint8 x, uint8 y) { + x &= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 sSMP::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 sSMP::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 sSMP::op_eor(uint8 x, uint8 y) { + x ^= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 sSMP::op_or(uint8 x, uint8 y) { + x |= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 sSMP::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 sSMP::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 sSMP::op_inc(uint8 x) { + x++; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 sSMP::op_dec(uint8 x) { + x--; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 sSMP::op_asl(uint8 x) { + regs.p.c = x & 0x80; + x <<= 1; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 sSMP::op_lsr(uint8 x) { + regs.p.c = x & 0x01; + x >>= 1; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 sSMP::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 sSMP::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 //ifdef SSMP_CPP diff --git a/bsnes/smp/ssmp/core/ssmpgen.cpp b/bsnes/smp/ssmp/core/ssmpgen.cpp new file mode 100755 index 0000000..b6a67db --- /dev/null +++ b/bsnes/smp/ssmp/core/ssmpgen.cpp @@ -0,0 +1,12 @@ +#define CLASS_NAME "sSMP" +#include + +int main() { + generate("op_mov.cpp", "op_mov.b"); + generate("op_pc.cpp", "op_pc.b"); + generate("op_read.cpp", "op_read.b"); + generate("op_rmw.cpp", "op_rmw.b"); + generate("op_misc.cpp", "op_misc.b"); + + return 0; +} diff --git a/bsnes/smp/ssmp/memory/memory.cpp b/bsnes/smp/ssmp/memory/memory.cpp new file mode 100755 index 0000000..431b87d --- /dev/null +++ b/bsnes/smp/ssmp/memory/memory.cpp @@ -0,0 +1,269 @@ +#ifdef SSMP_CPP + +alwaysinline +uint8 sSMP::ram_read(uint16 addr) { + if(addr < 0xffc0) return memory::apuram[addr]; + if(status.iplrom_enabled == false) return memory::apuram[addr]; + return iplrom[addr & 0x3f]; +} + +alwaysinline +void sSMP::ram_write(uint16 addr, uint8 data) { + //writes to $ffc0-$ffff always go to spcram, even if the iplrom is enabled + 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) { + //addr >= 0x00f0 && addr <= 0x00ff + 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 + scheduler.sync_smpcpu(); + r = cpu.port_read(addr & 3); + } break; + + case 0xf8: { //??? + r = status.smp_f8; + } break; + + case 0xf9: { //??? + 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) { + //addr >= 0x00f0 && addr >= 0x00ff + if(status.mmio_disabled == true) return; + + switch(addr) { + case 0xf0: { //TEST + if(regs.p.p) break; //writes only valid when P flag is clear + + //multiplier table may not be 100% accurate, some settings crash + //the processor due to S-SMP <> S-DSP bus access misalignment + //static uint8 clock_speed_tbl[16] = + //{ 3, 5, 9, 17, 4, 6, 10, 18, 6, 8, 12, 20, 10, 12, 16, 24 }; + + //status.clock_speed = 24 * clock_speed_tbl[data >> 4] / 3; + status.mmio_disabled = !!(data & 0x04); + status.ram_writable = !!(data & 0x02); + + //if((data >> 4) != 0) { + //dprintf("* S-SMP critical warning: $00f0 (TEST) clock speed control modified!"); + //dprintf("* S-SMP may crash on hardware as a result!"); + //} + } 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 + scheduler.sync_smpcpu(); + 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 + scheduler.sync_smpcpu(); + port_write(addr & 3, data); + } break; + + case 0xf8: { //??? + status.smp_f8 = data; + } break; + + case 0xf9: { //??? + 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 + if(status.ram_writable == true) { + ram_write(addr, data); + } +} + +// + +void sSMP::op_io() { + add_clocks(24); + tick_timers(); +} + +uint8 sSMP::op_read(uint16 addr) { + add_clocks(12); + uint8 r = op_busread(addr); + add_clocks(12); + tick_timers(); + return r; +} + +void sSMP::op_write(uint16 addr, uint8 data) { + add_clocks(24); + op_buswrite(addr, data); + tick_timers(); +} + +// + +alwaysinline +uint8 sSMP::op_readpc() { + return op_read(regs.pc++); +} + +alwaysinline +uint8 sSMP::op_readstack() { + return op_read(0x0100 | ++regs.sp); +} + +alwaysinline +void sSMP::op_writestack(uint8 data) { + op_write(0x0100 | regs.sp--, data); +} + +alwaysinline +uint8 sSMP::op_readaddr(uint16 addr) { + return op_read(addr); +} + +alwaysinline +void sSMP::op_writeaddr(uint16 addr, uint8 data) { + op_write(addr, data); +} + +alwaysinline +uint8 sSMP::op_readdp(uint8 addr) { + return op_read((unsigned(regs.p.p) << 8) + addr); +} + +alwaysinline +void sSMP::op_writedp(uint8 addr, uint8 data) { + op_write((unsigned(regs.p.p) << 8) + addr, data); +} + +#endif //ifdef SSMP_CPP diff --git a/bsnes/smp/ssmp/memory/memory.hpp b/bsnes/smp/ssmp/memory/memory.hpp new file mode 100755 index 0000000..d6e36d5 --- /dev/null +++ b/bsnes/smp/ssmp/memory/memory.hpp @@ -0,0 +1,29 @@ + uint8 ram_read(uint16 addr); + void ram_write(uint16 addr, uint8 data); + + uint8 port_read(uint8 port); + void port_write(uint8 port, uint8 data); + + //====================== + //core SMP bus functions + //====================== + uint8 op_busread(uint16 addr); + void op_buswrite(uint16 addr, uint8 data); + + void op_io(); + uint8 op_read(uint16 addr); + void op_write(uint16 addr, uint8 data); + + //=================================================== + //helper memory addressing functions used by SMP core + //=================================================== + uint8 op_readpc(); + + uint8 op_readstack(); + void op_writestack(uint8 data); + + uint8 op_readaddr(uint16 addr); + void op_writeaddr(uint16 addr, uint8 data); + + uint8 op_readdp(uint8 addr); + void op_writedp(uint8 addr, uint8 data); diff --git a/bsnes/smp/ssmp/ssmp.cpp b/bsnes/smp/ssmp/ssmp.cpp new file mode 100755 index 0000000..8ec73bc --- /dev/null +++ b/bsnes/smp/ssmp/ssmp.cpp @@ -0,0 +1,70 @@ +#include <../base.hpp> +#define SSMP_CPP + +#include "core/core.cpp" +#include "memory/memory.cpp" +#include "timing/timing.cpp" + +void sSMP::power() { + for(unsigned i = 0; i < memory::apuram.size(); i++) { + //SNES hardware APURAM contains pseudo-random data upon power up (exact formula is unknown.) + //memory::apuram.write(i, (i & 32) ? 0xff : 0x00); + memory::apuram.write(i, 0x00); + } + + //targets not initialized/changed upon reset + t0.target = 0; + t1.target = 0; + t2.target = 0; + + reset(); +} + +void sSMP::reset() { + regs.pc = 0xffc0; + regs.a = 0x00; + regs.x = 0x00; + regs.y = 0x00; + regs.sp = 0xef; + regs.p = 0x02; + + status.clock_counter = 0; + status.dsp_counter = 0; + + //$00f0 + status.clock_speed = 24 * 3 / 3; + status.mmio_disabled = false; + status.ram_writable = true; + + //$00f1 + status.iplrom_enabled = true; + + //$00f2 + status.dsp_addr = 0x00; + + //$00f8,$00f9 + status.smp_f8 = 0x00; + status.smp_f9 = 0x00; + + t0.enabled = false; + t1.enabled = false; + t2.enabled = false; + + 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; +} + +sSMP::sSMP() { +} + +sSMP::~sSMP() { +} diff --git a/bsnes/smp/ssmp/ssmp.hpp b/bsnes/smp/ssmp/ssmp.hpp new file mode 100755 index 0000000..c8389b2 --- /dev/null +++ b/bsnes/smp/ssmp/ssmp.hpp @@ -0,0 +1,38 @@ +class sSMP : public SMP { +public: + void enter(); + + #include "core/core.hpp" + #include "memory/memory.hpp" + #include "timing/timing.hpp" + + struct { + uint8 opcode; + bool in_opcode; + + //timing + uint32 clock_counter; + uint32 dsp_counter; + + //$00f0 + uint8 clock_speed; + bool mmio_disabled; + bool ram_writable; + + //$00f1 + bool iplrom_enabled; + + //$00f2 + uint8 dsp_addr; + + //$00f8,$00f9 + uint8 smp_f8, smp_f9; + } status; + + //ssmp.cpp + void power(); + void reset(); + + sSMP(); + ~sSMP(); +}; diff --git a/bsnes/smp/ssmp/timing/timing.cpp b/bsnes/smp/ssmp/timing/timing.cpp new file mode 100755 index 0000000..80d9a79 --- /dev/null +++ b/bsnes/smp/ssmp/timing/timing.cpp @@ -0,0 +1,13 @@ +#ifdef SSMP_CPP + +void sSMP::add_clocks(unsigned clocks) { + scheduler.addclocks_smp(clocks); +} + +void sSMP::tick_timers() { + t0.tick(); + t1.tick(); + t2.tick(); +} + +#endif diff --git a/bsnes/smp/ssmp/timing/timing.hpp b/bsnes/smp/ssmp/timing/timing.hpp new file mode 100755 index 0000000..e72b39c --- /dev/null +++ b/bsnes/smp/ssmp/timing/timing.hpp @@ -0,0 +1,34 @@ +template +class sSMPTimer { +public: + uint8 target; + uint8 stage1_ticks, stage2_ticks, stage3_ticks; + bool enabled; + + void tick() { + //stage 1 increment + stage1_ticks++; + if(stage1_ticks < cycle_frequency) return; + + stage1_ticks -= cycle_frequency; + if(enabled == false) return; + + //stage 2 increment + stage2_ticks++; + + if(stage2_ticks != target) return; + + //stage 3 increment + stage2_ticks = 0; + stage3_ticks++; + stage3_ticks &= 15; + } +}; + + sSMPTimer<128> t0; + sSMPTimer<128> t1; + sSMPTimer< 16> t2; + + alwaysinline void add_clocks(unsigned clocks); + alwaysinline void tick_timers(); + uint32 clocks_executed(); diff --git a/bsnes/snes/audio/audio.cpp b/bsnes/snes/audio/audio.cpp new file mode 100755 index 0000000..5dd800b --- /dev/null +++ b/bsnes/snes/audio/audio.cpp @@ -0,0 +1,10 @@ +#ifdef SNES_CPP + +void SNES::Audio::update(uint16 l_sample, uint16 r_sample) { + snesinterface.audio_sample(l_sample, r_sample); +} + +void SNES::Audio::init() { +} + +#endif //ifdef SNES_CPP diff --git a/bsnes/snes/audio/audio.hpp b/bsnes/snes/audio/audio.hpp new file mode 100755 index 0000000..2643d91 --- /dev/null +++ b/bsnes/snes/audio/audio.hpp @@ -0,0 +1,7 @@ +class Audio { +public: + void update(uint16 l_sample, uint16 r_sample); + void init(); + + friend class SNES; +} audio; diff --git a/bsnes/snes/input/input.cpp b/bsnes/snes/input/input.cpp new file mode 100755 index 0000000..dcf3cc0 --- /dev/null +++ b/bsnes/snes/input/input.cpp @@ -0,0 +1,341 @@ +#ifdef SNES_CPP + +uint8 SNES::Input::port_read(bool portnumber) { + port_t &p = port[portnumber]; + + switch(p.device) { + case DeviceJoypad: { + if(p.counter0 >= 16) return 1; + unsigned deviceid = (portnumber == 0 ? DeviceIDJoypad1 : DeviceIDJoypad2); + return snesinterface.input_poll(deviceid, p.counter0++); + } //case DeviceJoypad + + case DeviceMultitap: { + if(cpu.joylatch()) return 2; //when latch is high -- data2 = 1, data1 = 0 + + unsigned deviceidx, deviceid0, deviceid1; + if(portnumber == 0) { + if(cpu.pio() & 0x40) { + deviceidx = p.counter0; + if(deviceidx >= 16) return 3; + p.counter0++; + + deviceid0 = DeviceIDMultitap1A; + deviceid1 = DeviceIDMultitap1B; + } else { + deviceidx = p.counter1; + if(deviceidx >= 16) return 3; + p.counter1++; + + deviceid0 = DeviceIDMultitap1C; + deviceid1 = DeviceIDMultitap1D; + } + } else { + if(cpu.pio() & 0x80) { + deviceidx = p.counter0; + if(deviceidx >= 16) return 3; + p.counter0++; + + deviceid0 = DeviceIDMultitap2A; + deviceid1 = DeviceIDMultitap2B; + } else { + deviceidx = p.counter1; + if(deviceidx >= 16) return 3; + p.counter1++; + + deviceid0 = DeviceIDMultitap2C; + deviceid1 = DeviceIDMultitap2D; + } + } + + return (snesinterface.input_poll(deviceid0, deviceidx) << 0) + | (snesinterface.input_poll(deviceid1, deviceidx) << 1); + } //case DeviceMultitap + + case DeviceMouse: { + if(p.counter0 >= 32) return 1; + unsigned deviceid = (portnumber == 0 ? DeviceIDMouse1 : DeviceIDMouse2); + + int position_x = snesinterface.input_poll(deviceid, MouseX); //-n = left, 0 = center, +n = right + int position_y = snesinterface.input_poll(deviceid, MouseY); //-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 snesinterface.input_poll(deviceid, MouseRight); + case 9: return snesinterface.input_poll(deviceid, MouseLeft); + 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 DeviceMouse + + case DeviceSuperScope: { + 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 = snesinterface.input_poll(DeviceIDSuperScope, SuperScopeTurbo); + 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 = snesinterface.input_poll(DeviceIDSuperScope, SuperScopeTrigger); + 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 = snesinterface.input_poll(DeviceIDSuperScope, SuperScopeCursor); + + //pause is a button; it is always edge sensitive + p.superscope.pause = false; + bool pause = snesinterface.input_poll(DeviceIDSuperScope, SuperScopePause); + 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 DeviceSuperScope + + case DeviceJustifier: + case DeviceJustifiers: { + if(portnumber == 0) break; //Justifier in port 1 not supported ... + if(p.counter0 >= 32) return 1; + + if(p.counter0 == 0) { + p.justifier.trigger1 = snesinterface.input_poll(DeviceIDJustifier1, JustifierTrigger); + p.justifier.start1 = snesinterface.input_poll(DeviceIDJustifier1, JustifierStart); + + if(p.device == DeviceJustifiers) { + p.justifier.trigger2 = snesinterface.input_poll(DeviceIDJustifier2, JustifierTrigger); + p.justifier.start2 = snesinterface.input_poll(DeviceIDJustifier2, JustifierStart); + } 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 DeviceJustifier(s) + } //switch(p.device) + + //no device connected + return 0; +} + +//scan all input; update cursor positions if needed +void SNES::Input::update() { + snesinterface.input_poll(); + port_t &p = port[1]; + + switch(p.device) { + case DeviceSuperScope: { + int x = snesinterface.input_poll(DeviceIDSuperScope, SuperScopeX); + int y = snesinterface.input_poll(DeviceIDSuperScope, SuperScopeY); + 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 DeviceJustifier: + case DeviceJustifiers: { + int x1 = snesinterface.input_poll(DeviceIDJustifier1, JustifierX); + int y1 = snesinterface.input_poll(DeviceIDJustifier1, JustifierY); + 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 = snesinterface.input_poll(DeviceIDJustifier2, JustifierX); + int y2 = snesinterface.input_poll(DeviceIDJustifier2, JustifierY); + 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 == DeviceJustifiers ? p.justifier.x2 : -1); + latchy = (p.device == DeviceJustifiers ? p.justifier.y2 : -1); + } + } break; + } +} + +void SNES::Input::port_set_device(bool portnumber, unsigned 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 == DeviceSuperScope + || port[1].device == DeviceJustifier + || port[1].device == DeviceJustifiers; + latchx = -1; + latchy = -1; + + if(device == DeviceSuperScope) { + 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 == DeviceJustifier) { + 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 == DeviceJustifiers) { + 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 SNES::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 SNES::Input::init() { +} + +#endif //ifdef SNES_CPP diff --git a/bsnes/snes/input/input.hpp b/bsnes/snes/input/input.hpp new file mode 100755 index 0000000..339f174 --- /dev/null +++ b/bsnes/snes/input/input.hpp @@ -0,0 +1,114 @@ +class Input { +public: + enum Device { + DeviceNone, + DeviceJoypad, + DeviceMultitap, + DeviceMouse, + DeviceSuperScope, + DeviceJustifier, + DeviceJustifiers, + }; + + enum DeviceID { + DeviceIDNone, + DeviceIDJoypad1, + DeviceIDJoypad2, + DeviceIDMultitap1A, + DeviceIDMultitap1B, + DeviceIDMultitap1C, + DeviceIDMultitap1D, + DeviceIDMultitap2A, + DeviceIDMultitap2B, + DeviceIDMultitap2C, + DeviceIDMultitap2D, + DeviceIDMouse1, + DeviceIDMouse2, + DeviceIDSuperScope, + DeviceIDJustifier1, + DeviceIDJustifier2, + }; + + enum JoypadID { + JoypadB = 0, JoypadY = 1, + JoypadSelect = 2, JoypadStart = 3, + JoypadUp = 4, JoypadDown = 5, + JoypadLeft = 6, JoypadRight = 7, + JoypadA = 8, JoypadX = 9, + JoypadL = 10, JoypadR = 11, + }; + + enum MouseID { + MouseX = 0, MouseY = 1, + MouseLeft = 2, MouseRight = 3, + }; + + enum SuperScopeID { + SuperScopeX = 0, SuperScopeY = 1, + SuperScopeTrigger = 2, SuperScopeCursor = 3, + SuperScopeTurbo = 4, SuperScopePause = 5, + }; + + enum JustifierID { + JustifierX = 0, JustifierY = 1, + JustifierTrigger = 2, JustifierStart = 3, + }; + + uint8 port_read(bool port); + void port_set_device(bool port, unsigned 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) { + if(ppu.vcounter() == latchy //test Y cursor position + && ppu.hcounter() == latchx << 2 //test X cursor position (cycles == pixels << 2) + && latchy < (ppu.overscan() ? 240 : 225) //verify Y is not offscreen + && latchx < 256 //verify X is not offscreen + ) ppu.latch_counters(); + } + } + +private: + bool iobit; + uint16_t latchx, latchy; + + struct port_t { + unsigned 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 SNES; +} input; diff --git a/bsnes/snes/interface/interface.hpp b/bsnes/snes/interface/interface.hpp new file mode 100755 index 0000000..2656c20 --- /dev/null +++ b/bsnes/snes/interface/interface.hpp @@ -0,0 +1,17 @@ +//==================== +//SNES interface class +//==================== +//Interfaces SNES core with platform-specific functionality (video, audio, input, ...) + +class SNESInterface { +public: + void video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height); + void audio_sample(uint16_t l_sample, uint16_t r_sample); + void input_poll(); + int16_t input_poll(unsigned deviceid, unsigned id); + + void init(); + void term(); +}; + +extern SNESInterface snesinterface; diff --git a/bsnes/snes/scheduler/scheduler.cpp b/bsnes/snes/scheduler/scheduler.cpp new file mode 100755 index 0000000..512e33c --- /dev/null +++ b/bsnes/snes/scheduler/scheduler.cpp @@ -0,0 +1,56 @@ +#ifdef SNES_CPP + +Scheduler scheduler; + +void threadentry_cpu() { cpu.enter(); } +void threadentry_smp() { smp.enter(); } +void threadentry_ppu() { ppu.enter(); } +void threadentry_dsp() { dsp.enter(); } + +void Scheduler::enter() { + switch(clock.active) { + case THREAD_CPU: co_switch(thread_cpu); break; + case THREAD_SMP: co_switch(thread_smp); break; + case THREAD_PPU: co_switch(thread_ppu); break; + case THREAD_DSP: co_switch(thread_dsp); break; + } +} + +void Scheduler::exit() { + co_switch(thread_snes); +} + +void Scheduler::init() { + clock.cpu_freq = snes.region() == SNES::NTSC + ? snes.config.cpu.ntsc_clock_rate + : snes.config.cpu.pal_clock_rate; + clock.smp_freq = snes.region() == SNES::NTSC + ? snes.config.smp.ntsc_clock_rate + : snes.config.smp.pal_clock_rate; + + clock.active = THREAD_CPU; + clock.cpuppu = 0; + clock.cpusmp = 0; + clock.smpdsp = 0; + + if(thread_cpu) co_delete(thread_cpu); + if(thread_smp) co_delete(thread_smp); + if(thread_ppu) co_delete(thread_ppu); + if(thread_dsp) co_delete(thread_dsp); + + thread_snes = co_active(); + thread_cpu = co_create(65536 * sizeof(void*), threadentry_cpu); + thread_smp = co_create(65536 * sizeof(void*), threadentry_smp); + thread_ppu = co_create(65536 * sizeof(void*), threadentry_ppu); + thread_dsp = co_create(65536 * sizeof(void*), threadentry_dsp); +} + +Scheduler::Scheduler() { + thread_snes = 0; + thread_cpu = 0; + thread_smp = 0; + thread_ppu = 0; + thread_dsp = 0; +} + +#endif //ifdef SNES_CPP diff --git a/bsnes/snes/scheduler/scheduler.hpp b/bsnes/snes/scheduler/scheduler.hpp new file mode 100755 index 0000000..114ca2d --- /dev/null +++ b/bsnes/snes/scheduler/scheduler.hpp @@ -0,0 +1,123 @@ +class Scheduler { +public: + cothread_t thread_snes; + cothread_t thread_cpu; + cothread_t thread_smp; + cothread_t thread_ppu; + cothread_t thread_dsp; + + enum ActiveThread { + THREAD_CPU, + THREAD_SMP, + THREAD_PPU, + THREAD_DSP, + }; + + struct { + unsigned cpu_freq; + unsigned smp_freq; + + ActiveThread active; + int64 cpuppu; + int64 cpusmp; + int64 smpdsp; + } clock; + + //========== + //CPU <> PPU + //========== + + alwaysinline void sync_cpuppu() { + if(clock.cpuppu < 0) { + clock.active = THREAD_PPU; + co_switch(thread_ppu); + } + } + + alwaysinline void sync_ppucpu() { + if(clock.cpuppu >= 0) { + clock.active = THREAD_CPU; + co_switch(thread_cpu); + } + } + + //========== + //CPU <> SMP + //========== + + alwaysinline void sync_cpusmp() { + if(clock.cpusmp < 0) { + clock.active = THREAD_SMP; + co_switch(thread_smp); + } + } + + alwaysinline void sync_smpcpu() { + if(clock.cpusmp >= 0) { + clock.active = THREAD_CPU; + co_switch(thread_cpu); + } + } + + //========== + //SMP <> DSP + //========== + + alwaysinline void sync_smpdsp() { + if(clock.smpdsp < 0) { + clock.active = THREAD_DSP; + co_switch(thread_dsp); + } + } + + alwaysinline void sync_dspsmp() { + if(clock.smpdsp >= 0) { + clock.active = THREAD_SMP; + co_switch(thread_smp); + } + } + + //====== + //timing + //====== + + alwaysinline void addclocks_cpu(unsigned clocks) { + clock.cpuppu -= clocks; + sync_cpuppu(); + + clock.cpusmp -= clocks * (uint64)clock.smp_freq; + if(clock.cpusmp < -(20000 * (int64)24000000)) sync_cpusmp(); + } + + alwaysinline void addclocks_ppu(unsigned clocks) { + clock.cpuppu += clocks; + sync_ppucpu(); + } + + alwaysinline void addclocks_smp(unsigned clocks) { + clock.cpusmp += clocks * (uint64)clock.cpu_freq; + if(clock.cpusmp > +(20000 * (int64)24000000)) sync_smpcpu(); + + clock.smpdsp -= clocks; + #if !defined(USE_STATE_MACHINE) + sync_smpdsp(); + #else + while(clock.smpdsp < 0) dsp.enter(); + #endif + } + + alwaysinline void addclocks_dsp(unsigned clocks) { + clock.smpdsp += clocks; + #if !defined(USE_STATE_MACHINE) + sync_dspsmp(); + #endif + } + + void enter(); + void exit(); + void init(); + + Scheduler(); +}; + +extern Scheduler scheduler; diff --git a/bsnes/snes/snes.cpp b/bsnes/snes/snes.cpp new file mode 100755 index 0000000..84b706f --- /dev/null +++ b/bsnes/snes/snes.cpp @@ -0,0 +1,210 @@ +#include <../base.hpp> +#include <../chip/chip.hpp> +#include <../cart/cart.hpp> +#define SNES_CPP + +SNES snes; +BUSCORE bus; +CPUCORE cpu; +SMPCORE smp; +DSPCORE dsp; +PPUCORE ppu; + +BSXBase bsxbase; +BSXCart bsxcart; +BSXFlash bsxflash; +SRTC srtc; +SDD1 sdd1; +SPC7110 spc7110; +Cx4 cx4; +DSP1 dsp1; +DSP2 dsp2; +DSP3 dsp3; +DSP4 dsp4; +OBC1 obc1; +ST010 st010; + +#include "scheduler/scheduler.cpp" +#include "tracer/tracer.cpp" + +#include "video/video.cpp" +#include "audio/audio.cpp" +#include "input/input.cpp" + +void SNES::run() { +} + +void SNES::runtoframe() { + scheduler.enter(); +} + +void SNES::init() { + bsxbase.init(); + bsxcart.init(); + bsxflash.init(); + srtc.init(); + sdd1.init(); + spc7110.init(); + cx4.init(); + dsp1.init(); + dsp2.init(); + dsp3.init(); + dsp4.init(); + obc1.init(); + st010.init(); + + video.init(); + audio.init(); + input.init(); + snesinterface.init(); +} + +void SNES::term() { + snesinterface.term(); +} + +void SNES::power() { + snes_region = max(0, min(2, snes.config.region)); + snes_expansion = max(0, min(1, snes.config.expansion_port)); + + if(snes_region == Autodetect) { + snes_region = (cartridge.region() == Cartridge::NTSC ? NTSC : PAL); + } + + scheduler.init(); + + ppu.PPUcounter::reset(); + cpu.power(); + smp.power(); + dsp.power(); + ppu.power(); + bus.power(); + + if(expansion() == ExpansionBSX) bsxbase.power(); + if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.power(); + if(cartridge.bsx_flash_loaded()) bsxflash.power(); + + if(cartridge.has_srtc()) srtc.power(); + if(cartridge.has_sdd1()) sdd1.power(); + if(cartridge.has_spc7110()) spc7110.power(); + if(cartridge.has_cx4()) cx4.power(); + if(cartridge.has_dsp1()) dsp1.power(); + if(cartridge.has_dsp2()) dsp2.power(); + if(cartridge.has_dsp3()) dsp3.power(); + if(cartridge.has_dsp4()) dsp4.power(); + if(cartridge.has_obc1()) obc1.power(); + if(cartridge.has_st010()) st010.power(); + + for(unsigned i = 0x2100; i <= 0x213f; i++) memory::mmio.map(i, ppu); + for(unsigned i = 0x2140; i <= 0x217f; i++) memory::mmio.map(i, cpu); + for(unsigned i = 0x2180; i <= 0x2183; i++) memory::mmio.map(i, cpu); + for(unsigned i = 0x4016; i <= 0x4017; i++) memory::mmio.map(i, cpu); + for(unsigned i = 0x4200; i <= 0x421f; i++) memory::mmio.map(i, cpu); + for(unsigned i = 0x4300; i <= 0x437f; i++) memory::mmio.map(i, cpu); + + if(expansion() == ExpansionBSX) bsxbase.enable(); + if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.enable(); + if(cartridge.bsx_flash_loaded()) bsxflash.enable(); + + if(cartridge.has_srtc()) srtc.enable(); + if(cartridge.has_sdd1()) sdd1.enable(); + if(cartridge.has_spc7110()) spc7110.enable(); + if(cartridge.has_cx4()) cx4.enable(); + if(cartridge.has_dsp1()) dsp1.enable(); + if(cartridge.has_dsp2()) dsp2.enable(); + if(cartridge.has_dsp3()) dsp3.enable(); + if(cartridge.has_dsp4()) dsp4.enable(); + if(cartridge.has_obc1()) obc1.enable(); + if(cartridge.has_st010()) st010.enable(); + + input.port_set_device(0, snes.config.controller_port1); + input.port_set_device(1, snes.config.controller_port2); + input.update(); + video.update(); +} + +void SNES::reset() { + scheduler.init(); + + ppu.PPUcounter::reset(); + cpu.reset(); + smp.reset(); + dsp.reset(); + ppu.reset(); + bus.reset(); + + if(expansion() == ExpansionBSX) bsxbase.reset(); + if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.reset(); + if(cartridge.bsx_flash_loaded()) bsxflash.reset(); + + if(cartridge.has_srtc()) srtc.reset(); + if(cartridge.has_sdd1()) sdd1.reset(); + if(cartridge.has_spc7110()) spc7110.reset(); + if(cartridge.has_cx4()) cx4.reset(); + if(cartridge.has_dsp1()) dsp1.reset(); + if(cartridge.has_dsp2()) dsp2.reset(); + if(cartridge.has_dsp3()) dsp3.reset(); + if(cartridge.has_dsp4()) dsp4.reset(); + if(cartridge.has_obc1()) obc1.reset(); + if(cartridge.has_st010()) st010.reset(); + + input.port_set_device(0, snes.config.controller_port1); + input.port_set_device(1, snes.config.controller_port2); + input.update(); + video.update(); +} + +void SNES::scanline() { + video.scanline(); + + if(ppu.vcounter() == 241) { + input.update(); + video.update(); + scheduler.exit(); + } +} + +void SNES::frame() { +} + +SNES::Region SNES::region() const { + return (SNES::Region)snes_region; +} + +SNES::ExpansionPortDevice SNES::expansion() const { + return (SNES::ExpansionPortDevice)snes_expansion; +} + +SNES::SNES() : snes_region(NTSC), snes_expansion(ExpansionNone) { + config.controller_port1 = Input::DeviceJoypad; + config.controller_port2 = Input::DeviceJoypad; + config.expansion_port = ExpansionBSX; + config.region = Autodetect; + + config.file.autodetect_type = false; + config.file.bypass_patch_crc32 = false; + + config.path.base = ""; + config.path.user = ""; + config.path.current = ""; + config.path.rom = ""; + config.path.save = ""; + config.path.patch = ""; + config.path.cheat = ""; + config.path.data = ""; + config.path.bsx = ""; + config.path.st = ""; + + config.cpu.version = 2; + config.cpu.ntsc_clock_rate = 21477272; + config.cpu.pal_clock_rate = 21281370; + config.cpu.alu_mul_delay = 2; + config.cpu.alu_div_delay = 2; + config.cpu.wram_init_value = 0x55; + + config.smp.ntsc_clock_rate = 32041 * 768; + config.smp.pal_clock_rate = 32041 * 768; + + config.ppu1.version = 1; + config.ppu2.version = 3; +} diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp new file mode 100755 index 0000000..20fd742 --- /dev/null +++ b/bsnes/snes/snes.hpp @@ -0,0 +1,84 @@ +#include "interface/interface.hpp" +#include "scheduler/scheduler.hpp" +#include "tracer/tracer.hpp" + +class VideoFilter; + +class SNES { +public: + enum Region { NTSC = 0, PAL = 1 }; + enum RegionAutodetect { Autodetect = 2 }; + enum ExpansionPortDevice { ExpansionNone = 0, ExpansionBSX = 1 }; + + struct Config { + unsigned controller_port1; + unsigned controller_port2; + unsigned expansion_port; + unsigned region; + + struct File { + bool autodetect_type; + bool bypass_patch_crc32; + } file; + + struct Path { + string base; //binary path + string user; //user profile path (bsnes.cfg, ...) + string current; //current working directory (path to currently loaded cartridge) + string rom, save, patch, cheat, data; + string bsx, st; + } path; + + struct CPU { + unsigned version; + unsigned ntsc_clock_rate; + unsigned pal_clock_rate; + unsigned alu_mul_delay; + unsigned alu_div_delay; + unsigned wram_init_value; + } cpu; + + struct SMP { + unsigned ntsc_clock_rate; + unsigned pal_clock_rate; + } smp; + + struct PPU1 { + unsigned version; + } ppu1; + + struct PPU2 { + unsigned version; + } ppu2; + } config; + + //system functions + virtual void run(); + virtual void runtoframe(); + + virtual void init(); + virtual void term(); + virtual void power(); + virtual void reset(); + + virtual void frame(); + virtual void scanline(); + + //return *active* region / expansion port device information + //settings cached upon power-on + Region region() const; + ExpansionPortDevice expansion() const; + + #include "video/video.hpp" + #include "audio/audio.hpp" + #include "input/input.hpp" + + SNES(); + virtual ~SNES() {} + +private: + unsigned snes_region; + unsigned snes_expansion; +}; + +extern SNES snes; diff --git a/bsnes/snes/tracer/tracer.cpp b/bsnes/snes/tracer/tracer.cpp new file mode 100755 index 0000000..dbfdb7b --- /dev/null +++ b/bsnes/snes/tracer/tracer.cpp @@ -0,0 +1,94 @@ +#ifdef SNES_CPP + +Tracer tracer; + +void tprintf(const char *s, ...) { + if(tracer.enabled() == false) return; + + char str[4096]; + va_list args; + va_start(args, s); + vsprintf(str, s, args); + va_end(args); + fprintf(tracer.fp, "%s\r\n", str); +} + +void Tracer::trace_cpuop() { + if(enabled() == false) return; + if(cpuop_enabled() == false) return; + if(cpu.in_opcode() == true) return; + + if(cpuopmask_enabled() == true) { + unsigned addr = cpu.regs.pc.d; + if(settings.cpuopmasktbl[addr >> 3] & 0x80 >> (addr & 7)) return; + settings.cpuopmasktbl[addr >> 3] |= 0x80 >> (addr & 7); + } + + char t[1024]; + cpu.disassemble_opcode(t); + fprintf(fp, "%s\r\n", t); +} + +void Tracer::trace_smpop() { + if(enabled() == false) return; + if(smpop_enabled() == false) return; + if(smp.in_opcode() == true) return; + + if(smpopmask_enabled() == true) { + unsigned addr = smp.regs.pc; + if(settings.smpopmasktbl[addr >> 3] & 0x80 >> (addr & 7)) return; + settings.smpopmasktbl[addr >> 3] |= 0x80 >> (addr & 7); + } + + char t[1024]; + smp.disassemble_opcode(t); + fprintf(fp, "%s\r\n", t); +} + +void Tracer::enable(bool en) { + if(en == true && enabled() == false) { + fp = fopen(Cartridge::filepath("trace.log", snes.config.path.data), "wb"); + } else if(en == false && enabled() == true) { + fclose(fp); + fp = 0; + } + + settings.enabled = en; +} + +void Tracer::cpuopmask_enable(bool en) { + if(en == true && cpuopmask_enabled() == false) { + settings.cpuopmasktbl = new(zeromemory) uint8_t[0x200000]; + } else if(en == false && cpuopmask_enabled() == true) { + delete[] settings.cpuopmasktbl; + } + + settings.cpuopmask = en; +} + +void Tracer::smpopmask_enable(bool en) { + if(en == true && smpopmask_enabled() == false) { + settings.smpopmasktbl = new(zeromemory) uint8_t[0x2000]; + } else if(en == false && smpopmask_enabled() == true) { + delete[] settings.smpopmasktbl; + } + + settings.smpopmask = en; +} + +Tracer::Tracer() { + fp = 0; + + settings.cpuop = false; + settings.cpuopmask = false; + settings.cpuopmasktbl = 0; + + settings.smpop = false; + settings.smpopmask = false; + settings.smpopmasktbl = 0; +} + +Tracer::~Tracer() { +} + +#endif //ifdef SNES_CPP diff --git a/bsnes/snes/tracer/tracer.hpp b/bsnes/snes/tracer/tracer.hpp new file mode 100755 index 0000000..882a2c2 --- /dev/null +++ b/bsnes/snes/tracer/tracer.hpp @@ -0,0 +1,45 @@ +void tprintf(const char *s, ...); + +class Tracer { +private: + +FILE *fp; + +struct { + bool enabled; + + bool cpuop; + bool cpuopmask; + uint8 *cpuopmasktbl; + + bool smpop; + bool smpopmask; + uint8 *smpopmasktbl; +} settings; + +public: + void enable(bool en); + bool enabled() { return settings.enabled; } + + void cpuop_enable(bool en) { settings.cpuop = en; } + bool cpuop_enabled() { return settings.cpuop; } + + void cpuopmask_enable(bool en); + bool cpuopmask_enabled() { return settings.cpuopmask; } + + void smpop_enable(bool en) { settings.smpop = en; } + bool smpop_enabled() { return settings.smpop; } + + void smpopmask_enable(bool en); + bool smpopmask_enabled() { return settings.smpopmask; } + + void trace_cpuop(); + void trace_smpop(); + + Tracer(); + ~Tracer(); + + friend void tprintf(const char *s, ...); +}; + +extern Tracer tracer; diff --git a/bsnes/snes/video/video.cpp b/bsnes/snes/video/video.cpp new file mode 100755 index 0000000..a88712c --- /dev/null +++ b/bsnes/snes/video/video.cpp @@ -0,0 +1,103 @@ +#ifdef SNES_CPP + +const uint8_t SNES::Video::cursor[15 * 15] = { + 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, + 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, + 0,0,0,1,2,2,1,2,1,2,2,1,0,0,0, + 0,0,1,2,1,1,0,1,0,1,1,2,1,0,0, + 0,1,2,1,0,0,0,1,0,0,0,1,2,1,0, + 0,1,2,1,0,0,1,2,1,0,0,1,2,1,0, + 1,2,1,0,0,1,1,2,1,1,0,0,1,2,1, + 1,2,2,1,1,2,2,2,2,2,1,1,2,2,1, + 1,2,1,0,0,1,1,2,1,1,0,0,1,2,1, + 0,1,2,1,0,0,1,2,1,0,0,1,2,1,0, + 0,1,2,1,0,0,0,1,0,0,0,1,2,1,0, + 0,0,1,2,1,1,0,1,0,1,1,2,1,0,0, + 0,0,0,1,2,2,1,2,1,2,2,1,0,0,0, + 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, + 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, +}; + +void SNES::Video::draw_cursor(uint16_t color, int x, int y) { + for(int cy = 0; cy < 15; cy++) { + int vy = y + cy - 7; + if(vy <= 0 || vy >= 240) continue; //do not draw offscreen + + bool hires = (pline_width[vy] == 512); + for(int cx = 0; cx < 15; cx++) { + int vx = x + cx - 7; + if(vx < 0 || vx >= 256) continue; //do not draw offscreen + uint8_t pixel = cursor[cy * 15 + cx]; + if(pixel == 0) continue; + uint16_t pixelcolor = (pixel == 1) ? 0 : color; + + if(hires == false) { + *((uint16_t*)ppu.output + vy * 1024 + 0 + vx) = pixelcolor; + *((uint16_t*)ppu.output + vy * 1024 + 512 + vx) = pixelcolor; + } else { + *((uint16_t*)ppu.output + vy * 1024 + 0 + vx * 2 + 0) = pixelcolor; + *((uint16_t*)ppu.output + vy * 1024 + 512 + vx * 2 + 0) = pixelcolor; + *((uint16_t*)ppu.output + vy * 1024 + 0 + vx * 2 + 1) = pixelcolor; + *((uint16_t*)ppu.output + vy * 1024 + 512 + vx * 2 + 1) = pixelcolor; + } + } + } +} + +void SNES::Video::update() { + uint16_t *data = (uint16_t*)ppu.output; + unsigned width, height; + + switch(snes.input.port[1].device) { + case SNES::Input::DeviceSuperScope: draw_cursor(0x001f, snes.input.port[1].superscope.x, snes.input.port[1].superscope.y); break; + case SNES::Input::DeviceJustifiers: draw_cursor(0x02e0, snes.input.port[1].justifier.x2, snes.input.port[1].justifier.y2); //fallthrough + case SNES::Input::DeviceJustifier: draw_cursor(0x001f, snes.input.port[1].justifier.x1, snes.input.port[1].justifier.y1); break; + } + + unsigned yoffset = 1; //scanline 0 is always black, skip this line for video output + if(mode == ModeNTSC && ppu.overscan()) yoffset += 8; //NTSC overscan centers x240 height image + + switch(mode) { default: + case ModeNTSC: { width = 256; height = 224; } break; + case ModePAL: { width = 256; height = 239; } break; + } + + if(frame_hires) width <<= 1; + if(frame_interlace) height <<= 1; + + snesinterface.video_refresh( + data + yoffset * 1024, + /* pitch = */ height <= 240 ? 2048 : 1024, + /* line[] = */ height <= 240 ? (pline_width + yoffset) : (iline_width + yoffset * 2), + width, height + ); + + frame_hires = false; + frame_interlace = false; +} + +void SNES::Video::scanline() { + unsigned y = ppu.vcounter(); + if(y >= 240) return; + + unsigned width = (ppu.hires() == false ? 256 : 512); + pline_width[y] = width; + iline_width[y * 2 + (int)ppu.field()] = width; + + frame_hires |= ppu.hires(); + frame_interlace |= ppu.interlace(); +} + +void SNES::Video::set_mode(Mode mode_) { + mode = mode_; +} + +void SNES::Video::init() { + for(unsigned i = 0; i < 240; i++) pline_width[i] = 256; + for(unsigned i = 0; i < 480; i++) iline_width[i] = 256; + frame_hires = false; + frame_interlace = false; + set_mode(ModeNTSC); +} + +#endif //ifdef SNES_CPP diff --git a/bsnes/snes/video/video.hpp b/bsnes/snes/video/video.hpp new file mode 100755 index 0000000..310b1d9 --- /dev/null +++ b/bsnes/snes/video/video.hpp @@ -0,0 +1,25 @@ +class Video { +public: + enum Mode { + ModeNTSC, + ModePAL, + }; + void set_mode(Mode); + +private: + Mode mode; + bool frame_hires; + bool frame_interlace; + + unsigned pline_width[240]; //progressive + unsigned iline_width[480]; //interlace + + void update(); + void scanline(); + void init(); + + static const uint8_t cursor[15 * 15]; + void draw_cursor(uint16_t color, int x, int y); + + friend class SNES; +} video; diff --git a/bsnes/ui_qt/Makefile b/bsnes/ui_qt/Makefile new file mode 100755 index 0000000..a9b15ae --- /dev/null +++ b/bsnes/ui_qt/Makefile @@ -0,0 +1,85 @@ +############################## +### platform configuration ### +############################## + +objects := main $(if $(call streq,$(platform),win),resource) $(objects) + +moc = moc +rcc = rcc + +ifeq ($(platform),x) # X11 + link += $(call mklib,Xtst) + link += `pkg-config --libs QtCore QtGui` + qtflags = `pkg-config --cflags QtCore QtGui` +else ifeq ($(platform),win) # Windows + qtdir = c:/qt450 + + link += $(call mklibpath,$(qtdir)/lib) + link += $(call mklibpath,$(qtdir)/plugins/imageformats) + + link += $(call mklib,mingw32) + link += $(call mklib,qtmain) + link += $(call mklib,QtGui) + link += $(call mklib,comdlg32) + link += $(call mklib,oleaut32) + link += $(call mklib,imm32) + link += $(call mklib,winmm) + link += $(call mklib,winspool) + link += $(call mklib,msimg32) + link += $(call mklib,QtCore) + link += $(call mklib,ole32) + link += $(call mklib,advapi32) + link += $(call mklib,ws2_32) + link += $(call mklib,uuid) + link += $(call mklib,gdi32) + + # optional image-file support: + # link += $(call mklib,qjpeg) + # link += $(call mklib,qmng) + + qtflags = $(call mkincpath,$(qtdir)/include) + qtflags += $(call mkincpath,$(qtdir)/include/QtCore) + qtflags += $(call mkincpath,$(qtdir)/include/QtGui) +endif + +moc_objects = \ + $(ui)/base/main.moc \ + $(ui)/base/loader.moc \ + $(ui)/base/htmlviewer.moc \ + $(ui)/base/about.moc \ + $(ui)/settings/settings.moc \ + $(ui)/settings/video.moc \ + $(ui)/settings/audio.moc \ + $(ui)/settings/input.moc \ + $(ui)/settings/paths.moc \ + $(ui)/settings/cheateditor.moc \ + $(ui)/settings/advanced.moc \ + $(ui)/settings/utility/inputcapture.moc \ + $(ui)/settings/utility/codeeditor.moc \ + +############# +### rules ### +############# + +%.moc: $<; $(moc) $(patsubst %.moc,%.hpp,$@) -o $@ +$(foreach object,$(moc_objects),$(eval $(object): $(patsubst %.moc,%.hpp,$(object)))) + +obj/main.$(obj): $(ui)/main.cpp \ +$(ui)/* $(ui)/input/* $(ui)/utility/* $(ui)/base/* $(ui)/settings/* $(ui)/settings/utility/* \ +data/* + $(call compile,$(qtflags)) + +$(ui)/resource/resource.rcc: $(ui)/resource/resource.qrc data/* + $(rcc) $(ui)/resource/resource.qrc -o $(ui)/resource/resource.rcc + +obj/resource.$(obj): $(ui)/resource/resource.rc + windres $(ui)/resource/resource.rc obj/resource.$(obj) + +############### +### targets ### +############### + +ui_build: $(ui)/resource/resource.rcc $(moc_objects); +ui_clean: + -$(foreach object,$(moc_objects),@$(call delete,$(object))) + -@$(call delete,$(ui)/resource/resource.rcc) diff --git a/bsnes/ui_qt/base/about.cpp b/bsnes/ui_qt/base/about.cpp new file mode 100755 index 0000000..eb13f3c --- /dev/null +++ b/bsnes/ui_qt/base/about.cpp @@ -0,0 +1,35 @@ +void AboutWindow::setup() { + window = new QWidget; + window->setObjectName("about-window"); + window->setWindowTitle("About bsnes ..."); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(0); { + logo = new Logo; + logo->setFixedSize(600, 106); + layout->addWidget(logo); + layout->addSpacing(Style::WidgetSpacing); + + info = new QLabel( + "" + "" + "" + "" + "
Version: " BSNES_VERSION "
Author: byuu
Homepage: http://byuu.org/
" + ); + layout->addWidget(info); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(spacer); + } + + window->setLayout(layout); +} + +void AboutWindow::Logo::paintEvent(QPaintEvent*) { + QPainter painter(this); + QPixmap pixmap(":/logo.png"); + painter.drawPixmap(0, 0, pixmap); +} diff --git a/bsnes/ui_qt/base/about.hpp b/bsnes/ui_qt/base/about.hpp new file mode 100755 index 0000000..8312394 --- /dev/null +++ b/bsnes/ui_qt/base/about.hpp @@ -0,0 +1,16 @@ +class AboutWindow : public QObject { + Q_OBJECT + +public: + QWidget *window; + QVBoxLayout *layout; + struct Logo : public QWidget { + void paintEvent(QPaintEvent*); + } *logo; + QLabel *info; + QWidget *spacer; + + void setup(); + +public slots: +} *winAbout; diff --git a/bsnes/ui_qt/base/htmlviewer.cpp b/bsnes/ui_qt/base/htmlviewer.cpp new file mode 100755 index 0000000..5855315 --- /dev/null +++ b/bsnes/ui_qt/base/htmlviewer.cpp @@ -0,0 +1,20 @@ +void HtmlViewerWindow::setup() { + window = new QWidget; + window->setObjectName("html-window"); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(0); { + document = new QTextBrowser; + layout->addWidget(document); + } + + window->setLayout(layout); + window->setMinimumSize(560, 480); +} + +void HtmlViewerWindow::show(const char *title, const char *htmlData) { + document->setHtml(utf8() << htmlData); + window->setWindowTitle(title); + utility.showCentered(window); +} diff --git a/bsnes/ui_qt/base/htmlviewer.hpp b/bsnes/ui_qt/base/htmlviewer.hpp new file mode 100755 index 0000000..b76fdfc --- /dev/null +++ b/bsnes/ui_qt/base/htmlviewer.hpp @@ -0,0 +1,13 @@ +class HtmlViewerWindow : public QObject { + Q_OBJECT + +public: + QWidget *window; + QVBoxLayout *layout; + QTextBrowser *document; + + void setup(); + void show(const char *title, const char *htmlData); + +public slots: +} *winHtmlViewer; diff --git a/bsnes/ui_qt/base/loader.cpp b/bsnes/ui_qt/base/loader.cpp new file mode 100755 index 0000000..1842205 --- /dev/null +++ b/bsnes/ui_qt/base/loader.cpp @@ -0,0 +1,198 @@ +void LoaderWindow::setup() { + window = new QWidget; + window->setObjectName("loader-window"); + window->setMinimumWidth(520); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(0); + + grid = new QGridLayout; { + baseLabel = new QLabel("Base cartridge:"); + grid->addWidget(baseLabel, 0, 0); + + baseFile = new QLineEdit; + baseFile->setReadOnly(true); + grid->addWidget(baseFile, 0, 1); + + baseBrowse = new QPushButton("Browse ..."); + grid->addWidget(baseBrowse, 0, 2); + + baseClear = new QPushButton("Clear"); + grid->addWidget(baseClear, 0, 3); + + slot1Label = new QLabel("Slot A cartridge:"); + grid->addWidget(slot1Label, 1, 0); + + slot1File = new QLineEdit; + slot1File->setReadOnly(true); + grid->addWidget(slot1File, 1, 1); + + slot1Browse = new QPushButton("Browse ..."); + grid->addWidget(slot1Browse, 1, 2); + + slot1Clear = new QPushButton("Clear"); + grid->addWidget(slot1Clear, 1, 3); + + slot2Label = new QLabel("Slot B cartridge:"); + grid->addWidget(slot2Label, 2, 0); + + slot2File = new QLineEdit; + slot2File->setReadOnly(true); + grid->addWidget(slot2File, 2, 1); + + slot2Browse = new QPushButton("Browse ..."); + grid->addWidget(slot2Browse, 2, 2); + + slot2Clear = new QPushButton("Clear"); + grid->addWidget(slot2Clear, 2, 3); + } + grid->setSpacing(Style::WidgetSpacing); + layout->addLayout(grid); + layout->addSpacing(Style::WidgetSpacing); + + controls = new QHBoxLayout; { + load = new QPushButton("Load"); + controls->addWidget(load); + + cancel = new QPushButton("Cancel"); + controls->addWidget(cancel); + } + controls->setSpacing(Style::WidgetSpacing); + layout->addLayout(controls); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(spacer); + + window->setLayout(layout); + connect(baseBrowse, SIGNAL(released()), this, SLOT(selectBaseCartridge())); + connect(baseClear, SIGNAL(released()), this, SLOT(clearBaseCartridge())); + connect(slot1Browse, SIGNAL(released()), this, SLOT(selectSlot1Cartridge())); + connect(slot1Clear, SIGNAL(released()), this, SLOT(clearSlot1Cartridge())); + connect(slot2Browse, SIGNAL(released()), this, SLOT(selectSlot2Cartridge())); + connect(slot2Clear, SIGNAL(released()), this, SLOT(clearSlot2Cartridge())); + connect(load, SIGNAL(released()), this, SLOT(onLoad())); + connect(cancel, SIGNAL(released()), this, SLOT(onCancel())); +} + +void LoaderWindow::syncUi() { + //only allow load when a base file is specified ... + load->setEnabled(baseFile->text().length() > 0); +} + +void LoaderWindow::loadBsxSlottedCartridge(const char *filebase, const char *fileSlot1) { + window->hide(); + baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show(); + slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show(); + slot2Label->hide(), slot2File->hide(), slot2Browse->hide(), slot2Clear->hide(); + + slot1Label->setText("Slot cartridge:"); + + baseFile->setText(filebase); + slot1File->setText(fileSlot1); + + syncUi(); + mode = ModeBsxSlotted; + showWindow("Load BS-X Slotted Cartridge"); +} + +void LoaderWindow::loadBsxCartridge(const char *fileBase, const char *fileSlot1) { + window->hide(); + baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show(); + slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show(); + slot2Label->hide(), slot2File->hide(), slot2Browse->hide(), slot2Clear->hide(); + + slot1Label->setText("Slot cartridge:"); + + baseFile->setText(fileBase); + slot1File->setText(fileSlot1); + + syncUi(); + mode = ModeBsx; + showWindow("Load BS-X Cartridge"); +} + +void LoaderWindow::loadSufamiTurboCartridge(const char *fileBase, const char *fileSlot1, const char *fileSlot2) { + window->hide(); + baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show(); + slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show(); + slot2Label->show(), slot2File->show(), slot2Browse->show(), slot2Clear->show(); + + slot1Label->setText("Slot A cartridge:"); + slot2Label->setText("Slot B cartridge:"); + + baseFile->setText(fileBase); + slot1File->setText(fileSlot1); + slot2File->setText(fileSlot2); + + syncUi(); + mode = ModeSufamiTurbo; + showWindow("Load Sufami Turbo Cartridge"); +} + +void LoaderWindow::showWindow(const char *title) { + window->setWindowTitle(title); + utility.showCentered(window); + load->setFocus(); +} + +void LoaderWindow::selectBaseCartridge() { + string filename = utility.selectCartridge(); + if(filename.length() > 0) baseFile->setText(utf8() << filename); + syncUi(); +} + +void LoaderWindow::clearBaseCartridge() { + baseFile->setText(""); + syncUi(); +} + +void LoaderWindow::selectSlot1Cartridge() { + string filename = utility.selectCartridge(); + if(filename.length() > 0) slot1File->setText(utf8() << filename); + syncUi(); +} + +void LoaderWindow::clearSlot1Cartridge() { + slot1File->setText(""); + syncUi(); +} + +void LoaderWindow::selectSlot2Cartridge() { + string filename = utility.selectCartridge(); + if(filename.length() > 0) slot2File->setText(utf8() << filename); + syncUi(); +} + +void LoaderWindow::clearSlot2Cartridge() { + slot2File->setText(""); + syncUi(); +} + +void LoaderWindow::onLoad() { + window->hide(); + string base = baseFile->text().toUtf8().data(); + string slot1 = slot1File->text().toUtf8().data(); + string slot2 = slot2File->text().toUtf8().data(); + + switch(mode) { + case ModeBsxSlotted: { + utility.loadCartridgeBsxSlotted(base, slot1); + } break; + + case ModeBsx: { + snes.config.path.bsx = base; + utility.loadCartridgeBsx(base, slot1); + } break; + + case ModeSufamiTurbo: { + snes.config.path.st = base; + utility.loadCartridgeSufamiTurbo(base, slot1, slot2); + } break; + } +} + +void LoaderWindow::onCancel() { + window->hide(); +} diff --git a/bsnes/ui_qt/base/loader.hpp b/bsnes/ui_qt/base/loader.hpp new file mode 100755 index 0000000..fdf6d2e --- /dev/null +++ b/bsnes/ui_qt/base/loader.hpp @@ -0,0 +1,45 @@ +class LoaderWindow : public QObject { + Q_OBJECT + +public: + QWidget *window; + QVBoxLayout *layout; + QGridLayout *grid; + QLabel *baseLabel; + QLineEdit *baseFile; + QPushButton *baseBrowse; + QPushButton *baseClear; + QLabel *slot1Label; + QLineEdit *slot1File; + QPushButton *slot1Browse; + QPushButton *slot1Clear; + QLabel *slot2Label; + QLineEdit *slot2File; + QPushButton *slot2Browse; + QPushButton *slot2Clear; + QHBoxLayout *controls; + QPushButton *load; + QPushButton *cancel; + QWidget *spacer; + + void setup(); + void syncUi(); + void loadBsxSlottedCartridge(const char*, const char*); + void loadBsxCartridge(const char*, const char*); + void loadSufamiTurboCartridge(const char*, const char*, const char*); + +public slots: + void selectBaseCartridge(); + void clearBaseCartridge(); + void selectSlot1Cartridge(); + void clearSlot1Cartridge(); + void selectSlot2Cartridge(); + void clearSlot2Cartridge(); + + void onLoad(); + void onCancel(); + +private: + enum mode_t { ModeBsxSlotted, ModeBsx, ModeSufamiTurbo } mode; + void showWindow(const char *title); +} *winLoader; diff --git a/bsnes/ui_qt/base/main.cpp b/bsnes/ui_qt/base/main.cpp new file mode 100755 index 0000000..6d2d8bd --- /dev/null +++ b/bsnes/ui_qt/base/main.cpp @@ -0,0 +1,371 @@ +void MainWindow::setup() { + window = new Window; + window->setObjectName("main-window"); + window->setWindowTitle(BSNES_TITLE); + + system = window->menuBar()->addMenu("&System"); + system_load = system->addAction("&Load Cartridge ..."); + system->addSeparator(); + system_power = system->addMenu("&Power"); + system_power_on = system_power->addAction("On"); + system_power_on->setCheckable(true); + system_power_off = system_power->addAction("Off"); + system_power_off->setCheckable(true); + system_reset = system->addAction("&Reset"); + system->addSeparator(); + system_port1 = system->addMenu("Controller Port 1"); + system_port1_none = system_port1->addAction("&None"); + system_port1_none->setCheckable(true); + system_port1_joypad = system_port1->addAction("&Joypad"); + system_port1_joypad->setCheckable(true); + system_port1_multitap = system_port1->addAction("&Multitap"); + system_port1_multitap->setCheckable(true); + system_port1_mouse = system_port1->addAction("M&ouse"); + system_port1_mouse->setCheckable(true); + system_port2 = system->addMenu("Controller Port 2"); + system_port2_none = system_port2->addAction("&None"); + system_port2_none->setCheckable(true); + system_port2_joypad = system_port2->addAction("&Joypad"); + system_port2_joypad->setCheckable(true); + system_port2_multitap = system_port2->addAction("&Multitap"); + system_port2_multitap->setCheckable(true); + system_port2_mouse = system_port2->addAction("M&ouse"); + system_port2_mouse->setCheckable(true); + system_port2_superscope = system_port2->addAction("&Super Scope"); + system_port2_superscope->setCheckable(true); + system_port2_justifier = system_port2->addAction("&Justifier"); + system_port2_justifier->setCheckable(true); + system_port2_justifiers = system_port2->addAction("&Two Justifiers"); + system_port2_justifiers->setCheckable(true); + system->addSeparator(); + system_exit = system->addAction("E&xit"); + system_exit->setMenuRole(QAction::QuitRole); + + settings = window->menuBar()->addMenu("S&ettings"); + settings_videoMode = settings->addMenu("&Video Mode"); + settings_videoMode_1x = settings_videoMode->addAction("Scale &1x"); + settings_videoMode_1x->setCheckable(true); + settings_videoMode_2x = settings_videoMode->addAction("Scale &2x"); + settings_videoMode_2x->setCheckable(true); + settings_videoMode_3x = settings_videoMode->addAction("Scale &3x"); + settings_videoMode_3x->setCheckable(true); + settings_videoMode_4x = settings_videoMode->addAction("Scale &4x"); + settings_videoMode_4x->setCheckable(true); + settings_videoMode_max = settings_videoMode->addAction("Scale &Max"); + settings_videoMode_max->setCheckable(true); + settings_videoMode_max->setStatusTip("Scale video output to fill as much of the screen as possible"); + settings_videoMode->addSeparator(); + settings_videoMode_correctAspectRatio = settings_videoMode->addAction("&Correct Aspect Ratio"); + settings_videoMode_correctAspectRatio->setStatusTip("Match pixel width-to-height ratio of TV"); + settings_videoMode_correctAspectRatio->setCheckable(true); + settings_videoMode_fullscreen = settings_videoMode->addAction("&Fullscreen"); + settings_videoMode_fullscreen->setCheckable(true); + settings_videoMode->addSeparator(); + settings_videoMode_ntsc = settings_videoMode->addAction("&NTSC"); + settings_videoMode_ntsc->setCheckable(true); + settings_videoMode_ntsc->setStatusTip("Size video output window to match NTSC TV spec"); + settings_videoMode_pal = settings_videoMode->addAction("&PAL"); + settings_videoMode_pal->setCheckable(true); + settings_videoMode_pal->setStatusTip("Size video output window to match PAL TV spec"); + settings_videoFilter = settings->addMenu("Video &Filter"); + settings_videoFilter_point = settings_videoFilter->addAction("&Point"); + settings_videoFilter_point->setCheckable(true); + settings_videoFilter_point->setStatusTip("Use pixellated hardware video scaling"); + settings_videoFilter_linear = settings_videoFilter->addAction("&Linear"); + settings_videoFilter_linear->setCheckable(true); + settings_videoFilter_linear->setStatusTip("Use smoothed hardware video scaling"); + settings_videoFilter->addSeparator(); + settings_videoFilter_none = settings_videoFilter->addAction("&None"); + settings_videoFilter_none->setCheckable(true); + settings_videoFilter_scanline = settings_videoFilter->addAction("&Scanline"); + settings_videoFilter_scanline->setCheckable(true); + settings_videoFilter_scale2x = settings_videoFilter->addAction("S&cale2x"); + settings_videoFilter_scale2x->setCheckable(true); + settings_videoFilter_hq2x = settings_videoFilter->addAction("&HQ2x"); + settings_videoFilter_hq2x->setCheckable(true); + settings_videoFilter_ntsc = settings_videoFilter->addAction("N&TSC"); + settings_videoFilter_ntsc->setCheckable(true); + settings->addSeparator(); + settings_muteAudio = settings->addAction("&Mute Audio Output"); + settings_muteAudio->setCheckable(true); + settings->addSeparator(); + settings_emulationSpeed = settings->addMenu("&Emulation Speed"); + settings_emulationSpeed_slowest = settings_emulationSpeed->addAction("50%"); + settings_emulationSpeed_slowest->setCheckable(true); + settings_emulationSpeed_slow = settings_emulationSpeed->addAction("75%"); + settings_emulationSpeed_slow->setCheckable(true); + settings_emulationSpeed_normal = settings_emulationSpeed->addAction("100%"); + settings_emulationSpeed_normal->setCheckable(true); + settings_emulationSpeed_fast = settings_emulationSpeed->addAction("150%"); + settings_emulationSpeed_fast->setCheckable(true); + settings_emulationSpeed_fastest = settings_emulationSpeed->addAction("200%"); + settings_emulationSpeed_fastest->setCheckable(true); + settings_emulationSpeed->addSeparator(); + settings_emulationSpeed_syncVideo = settings_emulationSpeed->addAction("Sync &Video"); + settings_emulationSpeed_syncVideo->setCheckable(true); + settings_emulationSpeed_syncVideo->setStatusTip("Sync video output to vertical refresh rate"); + settings_emulationSpeed_syncAudio = settings_emulationSpeed->addAction("Sync &Audio"); + settings_emulationSpeed_syncAudio->setCheckable(true); + settings_emulationSpeed_syncAudio->setStatusTip("Sync audio output to sound card output rate"); + settings_configuration = settings->addAction("&Configuration ..."); + settings_configuration->setMenuRole(QAction::PreferencesRole); + + help = window->menuBar()->addMenu("&Help"); + help_documentation = help->addAction("Documentation ..."); + help_license = help->addAction("License ..."); + help->addSeparator(); + help_about = help->addAction("&About ..."); + help_about->setMenuRole(QAction::AboutRole); + + canvasContainer = new CanvasObject; + canvasContainer->setAcceptDrops(true); { + canvasContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + canvasContainer->setObjectName("backdrop"); + + canvasLayout = new QVBoxLayout; { + canvasLayout->setMargin(0); + canvasLayout->setAlignment(Qt::AlignCenter); + + canvas = new CanvasWidget; + canvas->setAcceptDrops(true); + canvas->setAttribute(Qt::WA_PaintOnScreen, true); //disable Qt painting on focus / resize + + QPalette palette; + palette.setColor(QPalette::Window, QColor(0, 0, 0)); + canvas->setPalette(palette); + canvas->setAutoFillBackground(true); + } + canvasLayout->addWidget(canvas); + } + canvasContainer->setLayout(canvasLayout); + + window->statusBar()->showMessage(""); + systemState = new QLabel; + window->statusBar()->addPermanentWidget(systemState); + + window->setCentralWidget(canvasContainer); + + //slots + connect(system_load, SIGNAL(triggered()), this, SLOT(loadCartridge())); + connect(system_power_on, SIGNAL(triggered()), this, SLOT(powerOn())); + connect(system_power_off, SIGNAL(triggered()), this, SLOT(powerOff())); + connect(system_reset, SIGNAL(triggered()), this, SLOT(reset())); + connect(system_port1_none, SIGNAL(triggered()), this, SLOT(setPort1None())); + connect(system_port1_joypad, SIGNAL(triggered()), this, SLOT(setPort1Joypad())); + connect(system_port1_multitap, SIGNAL(triggered()), this, SLOT(setPort1Multitap())); + connect(system_port1_mouse, SIGNAL(triggered()), this, SLOT(setPort1Mouse())); + connect(system_port2_none, SIGNAL(triggered()), this, SLOT(setPort2None())); + connect(system_port2_joypad, SIGNAL(triggered()), this, SLOT(setPort2Joypad())); + connect(system_port2_multitap, SIGNAL(triggered()), this, SLOT(setPort2Multitap())); + connect(system_port2_mouse, SIGNAL(triggered()), this, SLOT(setPort2Mouse())); + connect(system_port2_superscope, SIGNAL(triggered()), this, SLOT(setPort2SuperScope())); + connect(system_port2_justifier, SIGNAL(triggered()), this, SLOT(setPort2Justifier())); + connect(system_port2_justifiers, SIGNAL(triggered()), this, SLOT(setPort2Justifiers())); + connect(system_exit, SIGNAL(triggered()), this, SLOT(quit())); + connect(settings_videoMode_1x, SIGNAL(triggered()), this, SLOT(setVideoMode1x())); + connect(settings_videoMode_2x, SIGNAL(triggered()), this, SLOT(setVideoMode2x())); + connect(settings_videoMode_3x, SIGNAL(triggered()), this, SLOT(setVideoMode3x())); + connect(settings_videoMode_4x, SIGNAL(triggered()), this, SLOT(setVideoMode4x())); + connect(settings_videoMode_max, SIGNAL(triggered()), this, SLOT(setVideoModeMax())); + connect(settings_videoMode_correctAspectRatio, SIGNAL(triggered()), this, SLOT(toggleAspectCorrection())); + connect(settings_videoMode_fullscreen, SIGNAL(triggered()), this, SLOT(toggleFullscreen())); + connect(settings_videoMode_ntsc, SIGNAL(triggered()), this, SLOT(setVideoNtsc())); + connect(settings_videoMode_pal, SIGNAL(triggered()), this, SLOT(setVideoPal())); + connect(settings_videoFilter_point, SIGNAL(triggered()), this, SLOT(setPointFilter())); + connect(settings_videoFilter_linear, SIGNAL(triggered()), this, SLOT(setLinearFilter())); + connect(settings_videoFilter_none, SIGNAL(triggered()), this, SLOT(setNoFilter())); + connect(settings_videoFilter_scanline, SIGNAL(triggered()), this, SLOT(setScanlineFilter())); + connect(settings_videoFilter_scale2x, SIGNAL(triggered()), this, SLOT(setScale2xFilter())); + connect(settings_videoFilter_hq2x, SIGNAL(triggered()), this, SLOT(setHq2xFilter())); + connect(settings_videoFilter_ntsc, SIGNAL(triggered()), this, SLOT(setNtscFilter())); + connect(settings_muteAudio, SIGNAL(triggered()), this, SLOT(muteAudio())); + connect(settings_emulationSpeed_slowest, SIGNAL(triggered()), this, SLOT(setSpeedSlowest())); + connect(settings_emulationSpeed_slow, SIGNAL(triggered()), this, SLOT(setSpeedSlow())); + connect(settings_emulationSpeed_normal, SIGNAL(triggered()), this, SLOT(setSpeedNormal())); + connect(settings_emulationSpeed_fast, SIGNAL(triggered()), this, SLOT(setSpeedFast())); + connect(settings_emulationSpeed_fastest, SIGNAL(triggered()), this, SLOT(setSpeedFastest())); + connect(settings_emulationSpeed_syncVideo, SIGNAL(triggered()), this, SLOT(syncVideo())); + connect(settings_emulationSpeed_syncAudio, SIGNAL(triggered()), this, SLOT(syncAudio())); + connect(settings_configuration, SIGNAL(triggered()), this, SLOT(showConfigWindow())); + connect(help_documentation, SIGNAL(triggered()), this, SLOT(showDocumentation())); + connect(help_license, SIGNAL(triggered()), this, SLOT(showLicense())); + connect(help_about, SIGNAL(triggered()), this, SLOT(showAbout())); + + syncUi(); +} + +void MainWindow::syncUi() { + system_power->setEnabled(cartridge.loaded()); + system_power_on->setChecked (application.power == true); + system_power_off->setChecked(application.power == false); + system_reset->setEnabled(cartridge.loaded() && application.power); + + system_port1_none->setChecked (snes.config.controller_port1 == SNES::Input::DeviceNone); + system_port1_joypad->setChecked (snes.config.controller_port1 == SNES::Input::DeviceJoypad); + system_port1_multitap->setChecked (snes.config.controller_port1 == SNES::Input::DeviceMultitap); + system_port1_mouse->setChecked (snes.config.controller_port1 == SNES::Input::DeviceMouse); + system_port2_none->setChecked (snes.config.controller_port2 == SNES::Input::DeviceNone); + system_port2_joypad->setChecked (snes.config.controller_port2 == SNES::Input::DeviceJoypad); + system_port2_multitap->setChecked (snes.config.controller_port2 == SNES::Input::DeviceMultitap); + system_port2_mouse->setChecked (snes.config.controller_port2 == SNES::Input::DeviceMouse); + system_port2_superscope->setChecked(snes.config.controller_port2 == SNES::Input::DeviceSuperScope); + system_port2_justifier->setChecked (snes.config.controller_port2 == SNES::Input::DeviceJustifier); + system_port2_justifiers->setChecked(snes.config.controller_port2 == SNES::Input::DeviceJustifiers); + + settings_videoMode_1x->setChecked (config.video.context->multiplier == 1); + settings_videoMode_2x->setChecked (config.video.context->multiplier == 2); + settings_videoMode_3x->setChecked (config.video.context->multiplier == 3); + settings_videoMode_4x->setChecked (config.video.context->multiplier == 4); + settings_videoMode_max->setChecked(config.video.context->multiplier >= 5); + + settings_videoMode_correctAspectRatio->setChecked(config.video.context->correctAspectRatio); + settings_videoMode_fullscreen->setChecked(config.video.isFullscreen); + settings_videoMode_ntsc->setChecked(config.video.context->region == 0); + settings_videoMode_pal->setChecked (config.video.context->region == 1); + + settings_videoFilter_point->setChecked (config.video.context->hwFilter == 0); + settings_videoFilter_linear->setChecked (config.video.context->hwFilter == 1); + settings_videoFilter_none->setChecked (config.video.context->swFilter == 0); + settings_videoFilter_scanline->setChecked(config.video.context->swFilter == 1); + settings_videoFilter_scale2x->setChecked (config.video.context->swFilter == 2); + settings_videoFilter_hq2x->setChecked (config.video.context->swFilter == 3); + settings_videoFilter_ntsc->setChecked (config.video.context->swFilter == 4); + + settings_muteAudio->setChecked(config.audio.mute); + + settings_emulationSpeed_slowest->setChecked(config.system.speed == 0); + settings_emulationSpeed_slow->setChecked (config.system.speed == 1); + settings_emulationSpeed_normal->setChecked (config.system.speed == 2); + settings_emulationSpeed_fast->setChecked (config.system.speed == 3); + settings_emulationSpeed_fastest->setChecked(config.system.speed == 4); + + settings_emulationSpeed_syncVideo->setChecked(config.video.synchronize); + settings_emulationSpeed_syncAudio->setChecked(config.audio.synchronize); +} + +void MainWindow::loadCartridge() { + string filename = utility.selectCartridge(); + if(filename.length() > 0) utility.loadCartridge(filename); +} + +void MainWindow::powerOn() { utility.modifySystemState(Utility::PowerOn); } +void MainWindow::powerOff() { utility.modifySystemState(Utility::PowerOff); } +void MainWindow::reset() { utility.modifySystemState(Utility::Reset); } + +void MainWindow::setPort1None() { snes.config.controller_port1 = SNES::Input::DeviceNone; utility.updateControllers(); syncUi(); } +void MainWindow::setPort1Joypad() { snes.config.controller_port1 = SNES::Input::DeviceJoypad; utility.updateControllers(); syncUi(); } +void MainWindow::setPort1Multitap() { snes.config.controller_port1 = SNES::Input::DeviceMultitap; utility.updateControllers(); syncUi(); } +void MainWindow::setPort1Mouse() { snes.config.controller_port1 = SNES::Input::DeviceMouse; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2None() { snes.config.controller_port2 = SNES::Input::DeviceNone; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2Joypad() { snes.config.controller_port2 = SNES::Input::DeviceJoypad; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2Multitap() { snes.config.controller_port2 = SNES::Input::DeviceMultitap; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2Mouse() { snes.config.controller_port2 = SNES::Input::DeviceMouse; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2SuperScope() { snes.config.controller_port2 = SNES::Input::DeviceSuperScope; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2Justifier() { snes.config.controller_port2 = SNES::Input::DeviceJustifier; utility.updateControllers(); syncUi(); } +void MainWindow::setPort2Justifiers() { snes.config.controller_port2 = SNES::Input::DeviceJustifiers; utility.updateControllers(); syncUi(); } + +void MainWindow::quit() { + application.terminate = true; +} + +void MainWindow::setVideoMode1x() { config.video.context->multiplier = 1; utility.resizeMainWindow(); syncUi(); } +void MainWindow::setVideoMode2x() { config.video.context->multiplier = 2; utility.resizeMainWindow(); syncUi(); } +void MainWindow::setVideoMode3x() { config.video.context->multiplier = 3; utility.resizeMainWindow(); syncUi(); } +void MainWindow::setVideoMode4x() { config.video.context->multiplier = 4; utility.resizeMainWindow(); syncUi(); } +void MainWindow::setVideoModeMax() { config.video.context->multiplier = 9; utility.resizeMainWindow(); syncUi(); } + +void MainWindow::toggleAspectCorrection() { + config.video.context->correctAspectRatio = settings_videoMode_correctAspectRatio->isChecked(); + utility.resizeMainWindow(); +} + +void MainWindow::toggleFullscreen() { config.video.isFullscreen = settings_videoMode_fullscreen->isChecked(); utility.updateFullscreenState(); syncUi(); } + +void MainWindow::setVideoNtsc() { config.video.context->region = 0; utility.updateVideoMode(); utility.resizeMainWindow(); syncUi(); } +void MainWindow::setVideoPal() { config.video.context->region = 1; utility.updateVideoMode(); utility.resizeMainWindow(); syncUi(); } + +void MainWindow::setPointFilter() { config.video.context->hwFilter = 0; utility.updateHardwareFilter(); syncUi(); } +void MainWindow::setLinearFilter() { config.video.context->hwFilter = 1; utility.updateHardwareFilter(); syncUi(); } +void MainWindow::setNoFilter() { config.video.context->swFilter = 0; utility.updateSoftwareFilter(); syncUi(); } +void MainWindow::setScanlineFilter() { config.video.context->swFilter = 1; utility.updateSoftwareFilter(); syncUi(); } +void MainWindow::setScale2xFilter() { config.video.context->swFilter = 2; utility.updateSoftwareFilter(); syncUi(); } +void MainWindow::setHq2xFilter() { config.video.context->swFilter = 3; utility.updateSoftwareFilter(); syncUi(); } +void MainWindow::setNtscFilter() { config.video.context->swFilter = 4; utility.updateSoftwareFilter(); syncUi(); } + +void MainWindow::muteAudio() { config.audio.mute = settings_muteAudio->isChecked(); } + +void MainWindow::setSpeedSlowest() { config.system.speed = 0; utility.updateEmulationSpeed(); syncUi(); } +void MainWindow::setSpeedSlow() { config.system.speed = 1; utility.updateEmulationSpeed(); syncUi(); } +void MainWindow::setSpeedNormal() { config.system.speed = 2; utility.updateEmulationSpeed(); syncUi(); } +void MainWindow::setSpeedFast() { config.system.speed = 3; utility.updateEmulationSpeed(); syncUi(); } +void MainWindow::setSpeedFastest() { config.system.speed = 4; utility.updateEmulationSpeed(); syncUi(); } + +void MainWindow::syncVideo() { config.video.synchronize = settings_emulationSpeed_syncVideo->isChecked(); utility.updateAvSync(); } +void MainWindow::syncAudio() { config.audio.synchronize = settings_emulationSpeed_syncAudio->isChecked(); utility.updateAvSync(); } + +void MainWindow::showConfigWindow() { + utility.showCentered(winSettings->window); +} + +void MainWindow::showDocumentation() { + QFile file(":/documentation.html"); + if(file.open(QIODevice::ReadOnly | QIODevice::Text)) { + winHtmlViewer->show("Usage Documentation", file.readAll().constData()); + file.close(); + } +} + +void MainWindow::showLicense() { + QFile file(":/license.html"); + if(file.open(QIODevice::ReadOnly | QIODevice::Text)) { + winHtmlViewer->show("License Agreement", file.readAll().constData()); + file.close(); + } +} +void MainWindow::showAbout() { + utility.showCentered(winAbout->window); +} + +void MainWindow::Window::closeEvent(QCloseEvent*) { + winMain->quit(); +} + +//============ +//CanvasObject +//============ +//implement drag-and-drop support: +//drag cartridge image onto main window canvas area to load + +void CanvasObject::dragEnterEvent(QDragEnterEvent *event) { + if(event->mimeData()->hasUrls()) { + //do not accept multiple files at once + if(event->mimeData()->urls().count() == 1) event->acceptProposedAction(); + } +} + +void CanvasObject::dropEvent(QDropEvent *event) { + if(event->mimeData()->hasUrls()) { + QList list = event->mimeData()->urls(); + if(list.count() == 1) utility.loadCartridge(list.at(0).toLocalFile().toUtf8().constData()); + } +} + +//=========== +//CanvasWidget +//============ +//custom video render and mouse capture functionality + +QPaintEngine* CanvasWidget::paintEngine() const { + if(cartridge.loaded()) return 0; + return QWidget::paintEngine(); +} + +void CanvasWidget::mouseReleaseEvent(QMouseEvent *event) { + //acquire exclusive mode access to mouse when video output widget is clicked + //(will only acquire if cart is loaded, and mouse / lightgun is in use.) + utility.acquireMouse(); +} + +void CanvasWidget::paintEvent(QPaintEvent *event) { + event->ignore(); +} diff --git a/bsnes/ui_qt/base/main.hpp b/bsnes/ui_qt/base/main.hpp new file mode 100755 index 0000000..f24638a --- /dev/null +++ b/bsnes/ui_qt/base/main.hpp @@ -0,0 +1,129 @@ +class CanvasObject : public QWidget { +public: + void dragEnterEvent(QDragEnterEvent*); + void dropEvent(QDropEvent*); +}; + +class CanvasWidget : public CanvasObject { +public: + QPaintEngine* paintEngine() const; + void mouseReleaseEvent(QMouseEvent*); + void paintEvent(QPaintEvent*); +}; + +class MainWindow : public QObject { + Q_OBJECT + +public: + struct Window : public QMainWindow { + void closeEvent(QCloseEvent*); + } *window; + QVBoxLayout *layout; + QMenu *system; + QAction *system_load; + QMenu *system_power; + QAction *system_power_on; + QAction *system_power_off; + QAction *system_reset; + QMenu *system_port1; + QAction *system_port1_none; + QAction *system_port1_joypad; + QAction *system_port1_multitap; + QAction *system_port1_mouse; + QMenu *system_port2; + QAction *system_port2_none; + QAction *system_port2_joypad; + QAction *system_port2_multitap; + QAction *system_port2_mouse; + QAction *system_port2_superscope; + QAction *system_port2_justifier; + QAction *system_port2_justifiers; + QAction *system_exit; + QMenu *settings; + QMenu *settings_videoMode; + QAction *settings_videoMode_1x; + QAction *settings_videoMode_2x; + QAction *settings_videoMode_3x; + QAction *settings_videoMode_4x; + QAction *settings_videoMode_max; + QAction *settings_videoMode_correctAspectRatio; + QAction *settings_videoMode_fullscreen; + QAction *settings_videoMode_ntsc; + QAction *settings_videoMode_pal; + QMenu *settings_videoFilter; + QAction *settings_videoFilter_point; + QAction *settings_videoFilter_linear; + QAction *settings_videoFilter_none; + QAction *settings_videoFilter_scanline; + QAction *settings_videoFilter_scale2x; + QAction *settings_videoFilter_hq2x; + QAction *settings_videoFilter_ntsc; + QAction *settings_muteAudio; + QMenu *settings_emulationSpeed; + QAction *settings_emulationSpeed_slowest; + QAction *settings_emulationSpeed_slow; + QAction *settings_emulationSpeed_normal; + QAction *settings_emulationSpeed_fast; + QAction *settings_emulationSpeed_fastest; + QAction *settings_emulationSpeed_syncVideo; + QAction *settings_emulationSpeed_syncAudio; + QAction *settings_configuration; + QMenu *help; + QAction *help_documentation; + QAction *help_license; + QAction *help_about; + // + CanvasObject *canvasContainer; + QVBoxLayout *canvasLayout; + CanvasWidget *canvas; + QLabel *systemState; + + void setup(); + void syncUi(); + +public slots: + void loadCartridge(); + void powerOn(); + void powerOff(); + void reset(); + void setPort1None(); + void setPort1Joypad(); + void setPort1Multitap(); + void setPort1Mouse(); + void setPort2None(); + void setPort2Joypad(); + void setPort2Multitap(); + void setPort2Mouse(); + void setPort2SuperScope(); + void setPort2Justifier(); + void setPort2Justifiers(); + void quit(); + void setVideoMode1x(); + void setVideoMode2x(); + void setVideoMode3x(); + void setVideoMode4x(); + void setVideoModeMax(); + void toggleAspectCorrection(); + void toggleFullscreen(); + void setVideoNtsc(); + void setVideoPal(); + void setPointFilter(); + void setLinearFilter(); + void setNoFilter(); + void setScanlineFilter(); + void setScale2xFilter(); + void setHq2xFilter(); + void setNtscFilter(); + void muteAudio(); + void setSpeedSlowest(); + void setSpeedSlow(); + void setSpeedNormal(); + void setSpeedFast(); + void setSpeedFastest(); + void syncVideo(); + void syncAudio(); + void showConfigWindow(); + void showDocumentation(); + void showLicense(); + void showAbout(); +} *winMain; diff --git a/bsnes/ui_qt/config.cpp b/bsnes/ui_qt/config.cpp new file mode 100755 index 0000000..e764d63 --- /dev/null +++ b/bsnes/ui_qt/config.cpp @@ -0,0 +1,237 @@ +class Configuration : public configuration { +public: + struct System { + string video, audio, input; + bool crashedOnLastRun; + unsigned speed; + } system; + + struct Video { + bool isFullscreen; + bool synchronize; + signed contrastAdjust, brightnessAdjust, gammaAdjust; + bool enableGammaRamp; + bool enableNtscMergeFields; + double ntscAspectRatio, palAspectRatio; + + struct Context { + bool correctAspectRatio; + unsigned multiplier, region; + unsigned hwFilter, swFilter; + } *context, windowed, fullscreen; + } video; + + struct Audio { + bool synchronize; + bool mute; + unsigned volume, latency, outputFrequency, inputFrequency; + } audio; + + struct Input { + enum policy_t { FocusPolicyPauseEmulation, FocusPolicyIgnoreInput, FocusPolicyAllowInput }; + unsigned focusPolicy; + bool allowInvalidInput; + + struct Joypad { + string up, down, left, right, a, b, x, y, l, r, select, start; + } joypad1, joypad2, + multitap1a, multitap1b, multitap1c, multitap1d, + multitap2a, multitap2b, multitap2c, multitap2d; + + struct Mouse { + string x, y, left, right; + } mouse1, mouse2; + + struct SuperScope { + string x, y, trigger, turbo, cursor, pause; + } superscope; + + struct Justifier { + string x, y, trigger, start; + } justifier1, justifier2; + + struct UiGeneral { + string loadCartridge; + string pauseEmulation; + string resetSystem; + string powerCycleSystem; + string lowerSpeed; + string raiseSpeed; + string toggleCheatSystem; + string toggleFullscreen; + string toggleMenu; + string toggleStatus; + string exitEmulator; + } uiGeneral; + } input; + + Configuration() { + //======== + //external + //======== + + attach(snes.config.controller_port1 = SNES::Input::DeviceJoypad, "snes.controllerPort1"); + attach(snes.config.controller_port2 = SNES::Input::DeviceJoypad, "snes.controllerPort2"); + attach(snes.config.expansion_port = SNES::ExpansionBSX, "snes.expansionPort"); + attach(snes.config.region = SNES::Autodetect, "snes.region"); + + attach(snes.config.file.autodetect_type = false, "file.autodetectType"); + attach(snes.config.file.bypass_patch_crc32 = false, "file.bypassPatchCrc32"); + + attach(snes.config.path.rom = "", "path.rom"); + attach(snes.config.path.save = "", "path.save"); + attach(snes.config.path.patch = "", "path.patch"); + attach(snes.config.path.cheat = "", "path.cheat"); + attach(snes.config.path.data = "", "path.data"); + attach(snes.config.path.bsx = "", "path.bsx"); + attach(snes.config.path.st = "", "path.st"); + + attach(snes.config.cpu.version = 2, "cpu.version", "Valid version(s) are: 1, 2"); + attach(snes.config.cpu.ntsc_clock_rate = 21477272, "cpu.ntscClockRate"); + attach(snes.config.cpu.pal_clock_rate = 21281370, "cpu.palClockRate"); + attach(snes.config.cpu.alu_mul_delay = 2, "cpu.aluMulDelay"); + attach(snes.config.cpu.alu_div_delay = 2, "cpu.aluDivDelay"); + attach(snes.config.cpu.wram_init_value = 0x55, "cpu.wramInitValue"); + + attach(snes.config.smp.ntsc_clock_rate = 32041 * 768, "smp.ntscClockRate"); + attach(snes.config.smp.pal_clock_rate = 32041 * 768, "smp.palClockRate"); + + attach(snes.config.ppu1.version = 1, "ppu1.version", "Valid version(s) are: 1"); + attach(snes.config.ppu2.version = 3, "ppu2.version", "Valid version(s) are: 1, 2, 3"); + + //======== + //internal + //======== + + attach(system.video = "", "system.video"); + attach(system.audio = "", "system.audio"); + attach(system.input = "", "system.input"); + attach(system.crashedOnLastRun = false, "system.crashedOnLastRun"); + attach(system.speed = 2, "system.speed"); + + video.context = &video.windowed; + attach(video.isFullscreen = false, "video.isFullscreen"); + attach(video.synchronize = false, "video.synchronize"); + + attach(video.contrastAdjust = 0, "video.contrastAdjust"); + attach(video.brightnessAdjust = 0, "video.brightnessAdjust"); + attach(video.gammaAdjust = 0, "video.gammaAdjust"); + + attach(video.enableGammaRamp = true, "video.enableGammaRamp"); + attach(video.enableNtscMergeFields = false, "video.enableNtscMergeFields"); + + attach(video.ntscAspectRatio = 54.0 / 47.0, "video.ntscAspectRatio", "NTSC aspect ratio (x / y)"); + attach(video.palAspectRatio = 32.0 / 23.0, "video.palAspectRatio", "PAL aspect ratio (x / y)"); + + attach(video.windowed.correctAspectRatio = true, "video.windowed.correctAspectRatio"); + attach(video.windowed.multiplier = 2, "video.windowed.multiplier"); + attach(video.windowed.region = 0, "video.windowed.region"); + + attach(video.windowed.hwFilter = 1, "video.windowed.hwFilter"); + attach(video.windowed.swFilter = 0, "video.windowed.swFilter"); + + attach(video.fullscreen.correctAspectRatio = true, "video.fullscreen.correctAspectRatio"); + attach(video.fullscreen.multiplier = 9, "video.fullscreen.multiplier"); + attach(video.fullscreen.region = 0, "video.fullscreen.region"); + + attach(video.fullscreen.hwFilter = 1, "video.fullscreen.hwFilter"); + attach(video.fullscreen.swFilter = 0, "video.fullscreen.swFilter"); + + attach(audio.synchronize = true, "audio.synchronize"); + attach(audio.mute = false, "audio.mute"); + + attach(audio.volume = 100, "audio.volume"); + attach(audio.latency = 80, "audio.latency"); + attach(audio.outputFrequency = 48000, "audio.outputFrequency"); + attach(audio.inputFrequency = 32000, "audio.inputFrequency"); + + attach(input.focusPolicy = Input::FocusPolicyPauseEmulation, "input.focusPolicy"); + attach(input.allowInvalidInput = false, "input.allowInvalidInput", "Allow up+down / left+right combinations; may trigger bugs in some games"); + + attach(input.joypad1.up = "keyboard00.up", "input.joypad1.up"); + attach(input.joypad1.down = "keyboard00.down", "input.joypad1.down"); + attach(input.joypad1.left = "keyboard00.left", "input.joypad1.left"); + attach(input.joypad1.right = "keyboard00.right", "input.joypad1.right"); + attach(input.joypad1.a = "keyboard00.x", "input.joypad1.a"); + attach(input.joypad1.b = "keyboard00.z", "input.joypad1.b"); + attach(input.joypad1.x = "keyboard00.s", "input.joypad1.x"); + attach(input.joypad1.y = "keyboard00.a", "input.joypad1.y"); + attach(input.joypad1.l = "keyboard00.d", "input.joypad1.l"); + attach(input.joypad1.r = "keyboard00.c", "input.joypad1.r"); + attach(input.joypad1.select = "keyboard00.rshift", "input.joypad1.select"); + attach(input.joypad1.start = "keyboard00.return", "input.joypad1.start"); + + //these are all mapped to "none" by default + attachJoypad(input.joypad2, "input.joypad2"); + attachJoypad(input.multitap1a, "input.multitap1a"); + attachJoypad(input.multitap1b, "input.multitap1b"); + attachJoypad(input.multitap1c, "input.multitap1c"); + attachJoypad(input.multitap1d, "input.multitap1d"); + attachJoypad(input.multitap2a, "input.multitap2a"); + attachJoypad(input.multitap2b, "input.multitap2b"); + attachJoypad(input.multitap2c, "input.multitap2c"); + attachJoypad(input.multitap2d, "input.multitap2d"); + + attach(input.mouse1.x = "mouse00.x", "input.mouse1.x"); + attach(input.mouse1.y = "mouse00.y", "input.mouse1.y"); + attach(input.mouse1.left = "mouse00.button00", "input.mouse1.left"); + attach(input.mouse1.right = "mouse00.button02", "input.mouse1.right"); + + //more likely a user will only use one mouse at a time, than for a system to have two mice + attach(input.mouse2.x = "mouse00.x", "input.mouse2.x"); + attach(input.mouse2.y = "mouse00.y", "input.mouse2.y"); + attach(input.mouse2.left = "mouse00.button00", "input.mouse2.left"); + attach(input.mouse2.right = "mouse00.button02", "input.mouse2.right"); + + //unlikely a user will have a five-button mouse for turbo / pause mapping + attach(input.superscope.x = "mouse00.x", "input.superscope.x"); + attach(input.superscope.y = "mouse00.y", "input.superscope.y"); + attach(input.superscope.trigger = "mouse00.button00", "input.superscope.trigger"); + attach(input.superscope.cursor = "mouse00.button02", "input.superscope.cursor"); + attach(input.superscope.turbo = "keyboard00.t", "input.superscope.turbo"); + attach(input.superscope.pause = "keyboard00.p", "input.superscope.pause"); + + attach(input.justifier1.x = "mouse00.x", "input.justifier1.x"); + attach(input.justifier1.y = "mouse00.y", "input.justifier1.y"); + attach(input.justifier1.trigger = "mouse00.button00", "input.justifier1.trigger"); + attach(input.justifier1.start = "mouse00.button02", "input.jusitifer1.start"); + + attach(input.justifier2.x = "mouse01.x", "input.justifier2.x"); + attach(input.justifier2.y = "mouse01.y", "input.justifier2.y"); + attach(input.justifier2.trigger = "mouse01.button00", "input.justifier2.trigger"); + attach(input.justifier2.start = "mouse01.button02", "input.justifier2.start"); + + attach(input.uiGeneral.loadCartridge = "none", "input.uiGeneral.loadCartridge"); + attach(input.uiGeneral.pauseEmulation = "keyboard00.pause", "input.uiGeneral.pauseEmulation"); + attach(input.uiGeneral.resetSystem = "none", "input.uiGeneral.resetSystem"); + attach(input.uiGeneral.powerCycleSystem = "none", "input.uiGeneral.powerCycleSystem"); + attach(input.uiGeneral.lowerSpeed = "keyboard00.divide", "input.uiGeneral.lowerSpeed"); + attach(input.uiGeneral.raiseSpeed = "keyboard00.multiply", "input.uiGeneral.raiseSpeed"); + attach(input.uiGeneral.toggleCheatSystem = "none", "input.uiGeneral.toggleCheatSystem"); + attach(input.uiGeneral.toggleFullscreen = "keyboard00.f11", "input.uiGeneral.toggleFullscreen"); + attach(input.uiGeneral.toggleMenu = "keyboard00.escape", "input.uiGeneral.toggleMenu"); + attach(input.uiGeneral.toggleStatus = "keyboard00.escape", "input.uiGeneral.toggleStatus"); + attach(input.uiGeneral.exitEmulator = "none", "input.uiGeneral.exitEmulator"); + } + + void attachJoypad(Input::Joypad &joypad, const char *name) { + attach(joypad.up = "none", string() << name << ".up"); + attach(joypad.down = "none", string() << name << ".down"); + attach(joypad.left = "none", string() << name << ".left"); + attach(joypad.right = "none", string() << name << ".right"); + attach(joypad.a = "none", string() << name << ".a"); + attach(joypad.b = "none", string() << name << ".b"); + attach(joypad.x = "none", string() << name << ".x"); + attach(joypad.y = "none", string() << name << ".y"); + attach(joypad.l = "none", string() << name << ".l"); + attach(joypad.r = "none", string() << name << ".r"); + attach(joypad.select = "none", string() << name << ".select"); + attach(joypad.start = "none", string() << name << ".start"); + } + + bool load(const char *filename) { + if(configuration::load(filename) == false) return false; + video.context = (video.isFullscreen == false) ? &video.windowed : &video.fullscreen; + return true; + } +} config; diff --git a/bsnes/ui_qt/input/device.cpp b/bsnes/ui_qt/input/device.cpp new file mode 100755 index 0000000..6b41ba1 --- /dev/null +++ b/bsnes/ui_qt/input/device.cpp @@ -0,0 +1,261 @@ +//=========== +//InputDevice +//=========== + +InputDevice::InputDevice(SNES::Input::DeviceID i, bool p, const char *n) : InputGroup(n), id(i), port(p) { +} + +//====== +//Joypad +//====== + +int16_t Joypad::state(unsigned index) const { + if(config.input.allowInvalidInput == false) { + //SNES D-pads have central pivot point, making up+down or left+right combinations impossible. + //some software programs rely on this, and will crash if these combinations are allowed. + if(index == SNES::Input::JoypadDown && up.state ) return 0; + if(index == SNES::Input::JoypadRight && left.state) return 0; + } + + switch(index) { + case SNES::Input::JoypadUp: return up.state; + case SNES::Input::JoypadDown: return down.state; + case SNES::Input::JoypadLeft: return left.state; + case SNES::Input::JoypadRight: return right.state; + case SNES::Input::JoypadA: return a.state; + case SNES::Input::JoypadB: return b.state; + case SNES::Input::JoypadX: return x.state; + case SNES::Input::JoypadY: return y.state; + case SNES::Input::JoypadL: return l.state; + case SNES::Input::JoypadR: return r.state; + case SNES::Input::JoypadSelect: return select.state; + case SNES::Input::JoypadStart: return start.state; + } + + return 0; +} + +Joypad::Joypad(SNES::Input::DeviceID id, bool port, const char *name, +string &up_t, string &down_t, string &left_t, string &right_t, string &a_t, string &b_t, +string &x_t, string &y_t, string &l_t, string &r_t, string &select_t, string &start_t +) : +InputDevice(id, port, name), +up (InputObject::Button, "Up", up_t), +down (InputObject::Button, "Down", down_t), +left (InputObject::Button, "Left", left_t), +right (InputObject::Button, "Right", right_t), +a (InputObject::Button, "A", a_t), +b (InputObject::Button, "B", b_t), +x (InputObject::Button, "X", x_t), +y (InputObject::Button, "Y", y_t), +l (InputObject::Button, "L", l_t), +r (InputObject::Button, "R", r_t), +select(InputObject::Button, "Select", select_t), +start (InputObject::Button, "Start", start_t) { + attach(up); attach(down); attach(left); attach(right); attach(a); attach(b); + attach(x); attach(y); attach(l); attach(r); attach(select); attach(start); +} + +//===== +//Mouse +//===== + +int16_t Mouse::state(unsigned index) const { + switch(index) { + case SNES::Input::MouseX: return x.state; + case SNES::Input::MouseY: return y.state; + case SNES::Input::MouseLeft: return left.state; + case SNES::Input::MouseRight: return right.state; + } + + return 0; +} + +Mouse::Mouse(SNES::Input::DeviceID id, bool port, const char *name, +string &x_t, string &y_t, string &left_t, string &right_t +) : +InputDevice(id, port, name), +x (InputObject::Axis, "X-axis", x_t), +y (InputObject::Axis, "Y-axis", y_t), +left (InputObject::Button, "Left button", left_t), +right(InputObject::Button, "Right button", right_t) { + attach(x); attach(y); attach(left); attach(right); +} + +//========== +//SuperScope +//========== + +int16_t SuperScope::state(unsigned index) const { + switch(index) { + case SNES::Input::SuperScopeX: return x.state; + case SNES::Input::SuperScopeY: return y.state; + case SNES::Input::SuperScopeTrigger: return trigger.state; + case SNES::Input::SuperScopeCursor: return cursor.state; + case SNES::Input::SuperScopeTurbo: return turbo.state; + case SNES::Input::SuperScopePause: return pause.state; + } + + return 0; +} + +SuperScope::SuperScope(SNES::Input::DeviceID id, bool port, const char *name, +string &x_t, string &y_t, string &trigger_t, string &cursor_t, string &turbo_t, string &pause_t +) : +InputDevice(id, port, name), +x (InputObject::Axis, "X-axis", x_t), +y (InputObject::Axis, "Y-axis", y_t), +trigger(InputObject::Button, "Trigger", trigger_t), +cursor (InputObject::Button, "Cursor", cursor_t), +turbo (InputObject::Button, "Turbo", turbo_t), +pause (InputObject::Button, "Pause", pause_t) { + attach(x); attach(y); attach(trigger); attach(cursor); attach(turbo); attach(pause); +} + +//========= +//Justifier +//========= + +int16_t Justifier::state(unsigned index) const { + switch(index) { + case SNES::Input::JustifierX: return x.state; + case SNES::Input::JustifierY: return y.state; + case SNES::Input::JustifierTrigger: return trigger.state; + case SNES::Input::JustifierStart: return start.state; + } + + return 0; +} + +Justifier::Justifier(SNES::Input::DeviceID id, bool port, const char *name, +string &x_t, string &y_t, string &trigger_t, string &start_t +) : +InputDevice(id, port, name), +x (InputObject::Axis, "X-axis", x_t), +y (InputObject::Axis, "Y-axis", y_t), +trigger(InputObject::Button, "Trigger", trigger_t), +start (InputObject::Button, "Start", start_t) { + attach(x); attach(y); attach(trigger); attach(start); +} + +//=============== +//InputDevicePool +//=============== + +void InputDevicePool::attach(InputDevice &device) { + list.add(&device); +} + +void InputDevicePool::bind() { + for(unsigned i = 0; i < list.size(); i++) list[i]->bind(); +} + +void InputDevicePool::clear() { + for(unsigned i = 0; i < list.size(); i++) list[i]->clear(); +} + +void InputDevicePool::poll(const int16_t *table) { + for(unsigned i = 0; i < list.size(); i++) list[i]->poll(table); +} + +InputDevice* InputDevicePool::find(SNES::Input::DeviceID id) { + for(unsigned i = 0; i < list.size(); i++) { + if(list[i]->id == id) return list[i]; + } + + return 0; +} + +InputDevicePool::InputDevicePool() : list(*this) { +} + +// + +Joypad joypad1(SNES::Input::DeviceIDJoypad1, InputDevice::Port1, "Joypad", +config.input.joypad1.up, config.input.joypad1.down, config.input.joypad1.left, config.input.joypad1.right, +config.input.joypad1.a, config.input.joypad1.b, config.input.joypad1.x, config.input.joypad1.y, +config.input.joypad1.l, config.input.joypad1.r, config.input.joypad1.select, config.input.joypad1.start); + +Joypad joypad2(SNES::Input::DeviceIDJoypad2, InputDevice::Port2, "Joypad", +config.input.joypad2.up, config.input.joypad2.down, config.input.joypad2.left, config.input.joypad2.right, +config.input.joypad2.a, config.input.joypad2.b, config.input.joypad2.x, config.input.joypad2.y, +config.input.joypad2.l, config.input.joypad2.r, config.input.joypad2.select, config.input.joypad2.start); + +Joypad multitap1a(SNES::Input::DeviceIDMultitap1A, InputDevice::Port1, "Multitap - Port 1", +config.input.multitap1a.up, config.input.multitap1a.down, config.input.multitap1a.left, config.input.multitap1a.right, +config.input.multitap1a.a, config.input.multitap1a.b, config.input.multitap1a.x, config.input.multitap1a.y, +config.input.multitap1a.l, config.input.multitap1a.r, config.input.multitap1a.select, config.input.multitap1a.start); + +Joypad multitap1b(SNES::Input::DeviceIDMultitap1B, InputDevice::Port1, "Multitap - Port 2", +config.input.multitap1b.up, config.input.multitap1b.down, config.input.multitap1b.left, config.input.multitap1b.right, +config.input.multitap1b.a, config.input.multitap1b.b, config.input.multitap1b.x, config.input.multitap1b.y, +config.input.multitap1b.l, config.input.multitap1b.r, config.input.multitap1b.select, config.input.multitap1b.start); + +Joypad multitap1c(SNES::Input::DeviceIDMultitap1C, InputDevice::Port1, "Multitap - Port 3", +config.input.multitap1c.up, config.input.multitap1c.down, config.input.multitap1c.left, config.input.multitap1c.right, +config.input.multitap1c.a, config.input.multitap1c.b, config.input.multitap1c.x, config.input.multitap1c.y, +config.input.multitap1c.l, config.input.multitap1c.r, config.input.multitap1c.select, config.input.multitap1c.start); + +Joypad multitap1d(SNES::Input::DeviceIDMultitap1D, InputDevice::Port1, "Multitap - Port 4", +config.input.multitap1d.up, config.input.multitap1d.down, config.input.multitap1d.left, config.input.multitap1d.right, +config.input.multitap1d.a, config.input.multitap1d.b, config.input.multitap1d.x, config.input.multitap1d.y, +config.input.multitap1d.l, config.input.multitap1d.r, config.input.multitap1d.select, config.input.multitap1d.start); + +Joypad multitap2a(SNES::Input::DeviceIDMultitap2A, InputDevice::Port2, "Multitap - Port 1", +config.input.multitap2a.up, config.input.multitap2a.down, config.input.multitap2a.left, config.input.multitap2a.right, +config.input.multitap2a.a, config.input.multitap2a.b, config.input.multitap2a.x, config.input.multitap2a.y, +config.input.multitap2a.l, config.input.multitap2a.r, config.input.multitap2a.select, config.input.multitap2a.start); + +Joypad multitap2b(SNES::Input::DeviceIDMultitap2B, InputDevice::Port2, "Multitap - Port 2", +config.input.multitap2b.up, config.input.multitap2b.down, config.input.multitap2b.left, config.input.multitap2b.right, +config.input.multitap2b.a, config.input.multitap2b.b, config.input.multitap2b.x, config.input.multitap2b.y, +config.input.multitap2b.l, config.input.multitap2b.r, config.input.multitap2b.select, config.input.multitap2b.start); + +Joypad multitap2c(SNES::Input::DeviceIDMultitap2C, InputDevice::Port2, "Multitap - Port 3", +config.input.multitap2c.up, config.input.multitap2c.down, config.input.multitap2c.left, config.input.multitap2c.right, +config.input.multitap2c.a, config.input.multitap2c.b, config.input.multitap2c.x, config.input.multitap2c.y, +config.input.multitap2c.l, config.input.multitap2c.r, config.input.multitap2c.select, config.input.multitap2c.start); + +Joypad multitap2d(SNES::Input::DeviceIDMultitap2D, InputDevice::Port2, "Multitap - Port 4", +config.input.multitap2d.up, config.input.multitap2d.down, config.input.multitap2d.left, config.input.multitap2d.right, +config.input.multitap2d.a, config.input.multitap2d.b, config.input.multitap2d.x, config.input.multitap2d.y, +config.input.multitap2d.l, config.input.multitap2d.r, config.input.multitap2d.select, config.input.multitap2d.start); + +Mouse mouse1(SNES::Input::DeviceIDMouse1, InputDevice::Port1, "Mouse", +config.input.mouse1.x, config.input.mouse1.y, config.input.mouse1.left, config.input.mouse1.right); + +Mouse mouse2(SNES::Input::DeviceIDMouse2, InputDevice::Port2, "Mouse", +config.input.mouse2.x, config.input.mouse2.y, config.input.mouse2.left, config.input.mouse2.right); + +SuperScope superscope(SNES::Input::DeviceIDSuperScope, InputDevice::Port2, "Super Scope", +config.input.superscope.x, config.input.superscope.y, +config.input.superscope.trigger, config.input.superscope.cursor, +config.input.superscope.turbo, config.input.superscope.pause); + +Justifier justifier1(SNES::Input::DeviceIDJustifier1, InputDevice::Port2, "Justifier 1", +config.input.justifier1.x, config.input.justifier1.y, +config.input.justifier1.trigger, config.input.justifier1.start); + +Justifier justifier2(SNES::Input::DeviceIDJustifier2, InputDevice::Port2, "Justifier 2", +config.input.justifier2.x, config.input.justifier2.y, +config.input.justifier2.trigger, config.input.justifier2.start); + +InputSnesDevicePool inputPool; + +InputSnesDevicePool::InputSnesDevicePool() { + attach(joypad1); + attach(joypad2); + attach(multitap1a); + attach(multitap1b); + attach(multitap1c); + attach(multitap1d); + attach(multitap2a); + attach(multitap2b); + attach(multitap2c); + attach(multitap2d); + attach(mouse1); + attach(mouse2); + attach(superscope); + attach(justifier1); + attach(justifier2); +} diff --git a/bsnes/ui_qt/input/device.hpp b/bsnes/ui_qt/input/device.hpp new file mode 100755 index 0000000..5ac95f7 --- /dev/null +++ b/bsnes/ui_qt/input/device.hpp @@ -0,0 +1,73 @@ +struct InputDevice : InputGroup { + SNES::Input::DeviceID id; + enum Port { Port1, Port2 }; + const bool port; + + InputDevice(SNES::Input::DeviceID i, bool p, const char *n); +}; + +struct Joypad : InputDevice { + InputObject up, down, left, right, a, b, x, y, l, r, select, start; + + int16_t state(unsigned index) const; + Joypad(SNES::Input::DeviceID id, bool port, const char *name, + string&, string&, string&, string&, string&, string&, + string&, string&, string&, string&, string&, string&); +}; + +struct Mouse : InputDevice { + InputObject x, y, left, right; + + int16_t state(unsigned index) const; + Mouse(SNES::Input::DeviceID id, bool port, const char *name, + string&, string&, string&, string&); +}; + +struct SuperScope : InputDevice { + InputObject x, y, trigger, cursor, turbo, pause; + + int16_t state(unsigned index) const; + SuperScope(SNES::Input::DeviceID id, bool port, const char *name, + string&, string&, string&, string&, string&, string&); +}; + +struct Justifier : InputDevice { + InputObject x, y, trigger, start; + + int16_t state(unsigned index) const; + Justifier(SNES::Input::DeviceID id, bool port, const char *name, + string&, string&, string&, string&); +}; + +struct InputDevicePool : public array { + void attach(InputDevice &device); + void bind(); + void clear(); + void poll(const int16_t *table); + InputDevice* find(SNES::Input::DeviceID id); + InputDevicePool(); + +private: + array &list; +}; + +struct InputSnesDevicePool : public InputDevicePool { + InputSnesDevicePool(); +}; + +extern Joypad joypad1; +extern Joypad joypad2; +extern Joypad multitap1a; +extern Joypad multitap1b; +extern Joypad multitap1c; +extern Joypad multitap1d; +extern Joypad multitap2a; +extern Joypad multitap2b; +extern Joypad multitap2c; +extern Joypad multitap2d; +extern Mouse mouse1; +extern Mouse mouse2; +extern SuperScope superscope; +extern Justifier justifier1; +extern Justifier justifier2; +extern InputSnesDevicePool inputPool; diff --git a/bsnes/ui_qt/input/input.cpp b/bsnes/ui_qt/input/input.cpp new file mode 100755 index 0000000..f958685 --- /dev/null +++ b/bsnes/ui_qt/input/input.cpp @@ -0,0 +1,269 @@ +#include "device.cpp" +#include "userinterface.cpp" + +//========= +//InputCode +//========= + +InputCode::type_t InputCode::type(uint16_t code) { + for(unsigned i = 0; i < keyboard<>::count; i++) { + unsigned index = keyboard<>::index(i, keyboard<>::none); + if(code >= index && code < index + keyboard<>::length) return KeyboardButton; + } + + for(unsigned i = 0; i < mouse<>::count; i++) { + unsigned index = mouse<>::index(i, mouse<>::none); + if(code == index + mouse<>::x) return MouseAxis; + if(code == index + mouse<>::y) return MouseAxis; + if(code == index + mouse<>::z) return MouseAxis; + + index = mouse<>::index(i, mouse<>::button); + if(code >= index && code < index + mouse<>::buttons) return MouseButton; + } + + for(unsigned i = 0; i < joypad<>::count; i++) { + unsigned index; + + index = joypad<>::index(i, joypad<>::hat); + if(code >= index && code < index + joypad<>::hats) return JoypadHat; + + index = joypad<>::index(i, joypad<>::axis); + if(code >= index && code < index + joypad<>::axes) return JoypadAxis; + + index = joypad<>::index(i, joypad<>::button); + if(code >= index && code < index + joypad<>::buttons) return JoypadButton; + } + + return Unknown; +} + +InputCode::axistype_t InputCode::axisType(uint16_t code) { + for(unsigned i = 0; i < joypad<>::count; i++) { + unsigned index = joypad<>::index(i, joypad<>::axis); + if(code >= index && code < index + joypad<>::axes) { + return (InputCode::axistype_t)inputManager.axisType(i, code - index); + } + } + return InvalidAxis; +} + +//finds what mouse# is associated with code, returns -1 if not a mouse code +int InputCode::mouseNumber(uint16_t code) { + for(unsigned i = 0; i < mouse<>::count; i++) { + unsigned index = mouse<>::index(i, mouse<>::none); + if(code >= index && code < index + mouse<>::length) return i; + } + return -1; +} + +//finds what joypad# is associated with code, returns -1 if not a joypad code +int InputCode::joypadNumber(uint16_t code) { + for(unsigned i = 0; i < joypad<>::count; i++) { + unsigned index = joypad<>::index(i, joypad<>::none); + if(code >= index && code < index + joypad<>::length) return i; + } + return -1; +} + +//============ +//InputManager +//============ + +void InputManager::bind() { + inputPool.bind(); + inputUiPool.bind(); +} + +void InputManager::poll() { + if(config.input.focusPolicy == Configuration::Input::FocusPolicyIgnoreInput + && winMain->window->isActiveWindow() == false) { + inputPool.clear(); + } else { + inputPool.poll(stateTable[activeState]); + } +} + +void InputManager::clear() { + inputPool.clear(); +} + +void InputManager::flush() { + for(unsigned i = 0; i < nall::input_limit; i++) { + stateTable[0][i] = 0; + stateTable[1][i] = 0; + } +} + +int16_t InputManager::state(uint16_t code) const { + return stateTable[ activeState][code]; +} + +int16_t InputManager::lastState(uint16_t code) const { + return stateTable[!activeState][code]; +} + +int16_t InputManager::getStatus(unsigned deviceid, unsigned id) const { + InputDevice *device = inputPool.find((SNES::Input::DeviceID)deviceid); + if(device) return device->state(id); + return 0; +} + +void InputManager::refresh() { + bool last = activeState; + activeState = !activeState; + bool next = activeState; + + input.poll(stateTable[next]); + for(unsigned i = 0; i < nall::input_limit; i++) { + //alert via callback whenever input state changes for any ID ... + if(onInput && stateTable[last][i] != stateTable[next][i]) onInput(i); + } +} + +void InputManager::calibrate(unsigned joy) { + unsigned index = joypad<>::index(joy, joypad<>::none); + + for(unsigned axis = 0; axis < joypad<>::axes; axis++) { + int16_t value = state(index + joypad<>::axis + axis); + pad[joy].axis[axis] = (value >= -16384 && value <= +16384) ? InputCode::Stick : InputCode::Trigger; + } + + pad[joy].calibrated = true; +} + +bool InputManager::calibrated(unsigned joy) const { + return pad[joy].calibrated; +} + +InputCode::axistype_t InputManager::axisType(unsigned joy, unsigned axis) const { + return pad[joy].axis[axis]; +} + +InputManager::InputManager() { + activeState = 0; + flush(); + + for(unsigned i = 0; i < joypad<>::count; i++) { + pad[i].calibrated = false; + for(unsigned n = 0; n < joypad<>::axes; n++) pad[i].axis[n] = InputCode::InvalidAxis; + } +} + +//=========== +//InputObject +//=========== + +void InputObject::bind() { + lstring part; + part.split("::", id); + + code = nall::input_find((const char*)part[0]); + codetype = InputCode::type(code); + + modifier = None; + if(part.size() > 1) { + if(part[1] == "up" ) modifier = Up; + if(part[1] == "down" ) modifier = Down; + if(part[1] == "left" ) modifier = Left; + if(part[1] == "right" ) modifier = Right; + if(part[1] == "lo" ) modifier = Lo; + if(part[1] == "hi" ) modifier = Hi; + if(part[1] == "trigger") modifier = Trigger; + } + + if(code == nall::input_none) id = "none"; +} + +void InputObject::bind(uint16_t newCode) { + code = newCode; + codetype = InputCode::type(code); + + id = nall::input_find(newCode); + modifier = None; + + if(type == Button && codetype == InputCode::JoypadHat) { + switch(inputManager.state(code)) { + case joypad<>::hat_up: id << "::up"; modifier = Up; break; + case joypad<>::hat_down: id << "::down"; modifier = Down; break; + case joypad<>::hat_left: id << "::left"; modifier = Left; break; + case joypad<>::hat_right: id << "::right"; modifier = Right; break; + } + } else if(type == Button && codetype == InputCode::JoypadAxis) { + InputCode::axistype_t type = InputCode::axisType(code); + int16_t state = inputManager.state(code); + + if(type == InputCode::Stick) { + if(state < 0) { + id << "::lo"; + modifier = Lo; + } else { + id << "::hi"; + modifier = Hi; + } + } else if(type == InputCode::Trigger) { + id << "::trigger"; + modifier = Trigger; + } + } +} + +void InputObject::poll(int16_t newState) { + if(type == Button && codetype == InputCode::JoypadHat) { + //map 4-way hat input to button state + state = (modifier == Up && (newState & joypad<>::hat_up )) + || (modifier == Down && (newState & joypad<>::hat_down )) + || (modifier == Left && (newState & joypad<>::hat_left )) + || (modifier == Right && (newState & joypad<>::hat_right)); + } else if(type == Button && codetype == InputCode::JoypadAxis) { + //convert analog input to button state + state = (modifier == Lo && newState < -16384) + || (modifier == Hi && newState > +16384) + || (modifier == Trigger && newState < 0); + } else if(codetype == InputCode::MouseAxis && !input.acquired()) { + //mouse must be acquired (locked to window) to move axes + state = 0; + } else if(0 && codetype == InputCode::MouseButton && !input.acquired()) { + //same for buttons + //note: disabled for now ... requiring exclusive access makes it much more difficult + //to utilize mouse buttons for eg SNES joypad input. + state = 0; + } else if(codetype == InputCode::JoypadAxis) { + //joypad axis range = -32768 to +32767, scale to -8 to +7 to roughly match mouse delta + //todo: scale mouse up instead of joypad down? (would require mouse DPI / resolution) + state = newState / 4096; + } else { + state = newState; + } +} + +InputObject::InputObject(InputObject::type_t t, const char *n, string &s) : parent(0), type(t), name(n), id(s) { +} + +//========== +//InputGroup +//========== + +void InputGroup::attach(InputObject &object) { + list.add(&object); + object.parent = this; +} + +void InputGroup::bind() { + for(unsigned i = 0; i < list.size(); i++) list[i]->bind(); +} + +void InputGroup::clear() { + for(unsigned i = 0; i < list.size(); i++) list[i]->state = 0; +} + +void InputGroup::poll(const int16_t *table) { + for(unsigned i = 0; i < list.size(); i++) list[i]->poll(table[list[i]->code]); +} + +int16_t InputGroup::state(unsigned index) const { + if(index < list.size()) return list[index]->state; + return 0; +} + +InputGroup::InputGroup(const char *n) : list(*this), name(n) { +} diff --git a/bsnes/ui_qt/input/input.hpp b/bsnes/ui_qt/input/input.hpp new file mode 100755 index 0000000..9f53682 --- /dev/null +++ b/bsnes/ui_qt/input/input.hpp @@ -0,0 +1,88 @@ +struct InputCode { + enum type_t { + KeyboardButton, + MouseAxis, + MouseButton, + JoypadHat, + JoypadAxis, + JoypadButton, + Unknown, + }; + enum axistype_t { + Stick, //bi-directional, centered analog stick (min = -32768, inactive = 0, max = +32767) + Trigger, //uni-directional, pressure-sensitive analog trigger button (min = -32768, inactive = max = +32767) + InvalidAxis, //not a joypad axis code, or said joypad is not calibrated + }; + static type_t type(uint16_t code); + static axistype_t axisType(uint16_t code); + static int mouseNumber(uint16_t code); + static int joypadNumber(uint16_t code); +}; + +class InputManager { +public: + void bind(); + void poll(); + void clear(); + void flush(); + + int16_t state(uint16_t code) const; + int16_t lastState(uint16_t code) const; + int16_t getStatus(unsigned deviceid, unsigned id) const; + + void refresh(); + void calibrate(unsigned joypad); + bool calibrated(unsigned joypad) const; + InputCode::axistype_t axisType(unsigned joypad, unsigned axis) const; + function onInput; + + InputManager(); + +private: + bool activeState; + int16_t stateTable[2][nall::input_limit]; + + //joypad axis calibration data + struct Pad { + bool calibrated; + InputCode::axistype_t axis[joypad<>::axes]; + } pad[joypad<>::count]; +} inputManager; + +struct InputGroup; + +struct InputObject { + InputGroup *parent; + enum type_t { Button, Axis }; + enum modifier_t { None, Up, Down, Left, Right, Lo, Hi, Trigger }; + + type_t type; //type of input this object is mapped to + const char *name; //plain-text name ("Up", "Down", ... "Start") + string &id; //config-item reference ("joypad1.start") name ("joypad00.button00") + uint16_t code; //nall::input code ID + InputCode::type_t codetype; //hardware button / axis type + modifier_t modifier; //hardware specialization (joypad-axis "::lo", "::hi", etc) + int16_t state; //code state as of last inputManager.poll() + + void bind(); + void bind(uint16_t code); + void poll(int16_t state); + InputObject(type_t t, const char *n, string &s); +}; + +struct InputGroup : public array { + const char *name; + + void attach(InputObject &object); + void bind(); + void clear(); + void poll(const int16_t *table); + virtual int16_t state(unsigned index) const; + InputGroup(const char *n); + +private: + array &list; +}; + +#include "device.hpp" +#include "userinterface.hpp" diff --git a/bsnes/ui_qt/input/userinterface.cpp b/bsnes/ui_qt/input/userinterface.cpp new file mode 100755 index 0000000..5740a4e --- /dev/null +++ b/bsnes/ui_qt/input/userinterface.cpp @@ -0,0 +1,56 @@ +//============== +//InputGroupPool +//============== + +void InputGroupPool::attach(InputGroup &group) { + list.add(&group); +} + +void InputGroupPool::bind() { + for(unsigned i = 0; i < list.size(); i++) list[i]->bind(); +} + +void InputGroupPool::clear() { + for(unsigned i = 0; i < list.size(); i++) list[i]->clear(); +} + +void InputGroupPool::poll(const int16_t *table) { + for(unsigned i = 0; i < list.size(); i++) list[i]->poll(table); +} + +InputGroupPool::InputGroupPool() : list(*this) { +} + +// + +InputUiGeneral inputUiGeneral; +InputUiPool inputUiPool; + +InputUiGeneral::InputUiGeneral() : InputGroup("General"), +loadCartridge(InputObject::Button, "Load cartridge", config.input.uiGeneral.loadCartridge), +pauseEmulation(InputObject::Button, "Pause emulation", config.input.uiGeneral.pauseEmulation), +resetSystem(InputObject::Button, "Reset system", config.input.uiGeneral.resetSystem), +powerCycleSystem(InputObject::Button, "Power cycle system", config.input.uiGeneral.powerCycleSystem), +lowerSpeed(InputObject::Button, "Decrease emulation speed", config.input.uiGeneral.lowerSpeed), +raiseSpeed(InputObject::Button, "Increase emulation speed", config.input.uiGeneral.raiseSpeed), +toggleCheatSystem(InputObject::Button, "Toggle cheat system on or off", config.input.uiGeneral.toggleCheatSystem), +toggleFullscreen(InputObject::Button, "Toggle fullscreen mode", config.input.uiGeneral.toggleFullscreen), +toggleMenu(InputObject::Button, "Toggle menubar", config.input.uiGeneral.toggleMenu), +toggleStatus(InputObject::Button, "Toggle statusbar", config.input.uiGeneral.toggleStatus), +exitEmulator(InputObject::Button, "Exit emulator", config.input.uiGeneral.exitEmulator) { + attach(loadCartridge); + attach(pauseEmulation); + attach(resetSystem); + attach(powerCycleSystem); + attach(lowerSpeed); + attach(raiseSpeed); + attach(toggleCheatSystem); + attach(toggleFullscreen); + attach(toggleMenu); + attach(toggleStatus); + attach(exitEmulator); +} + +InputUiPool::InputUiPool() { + attach(inputUiGeneral); +} diff --git a/bsnes/ui_qt/input/userinterface.hpp b/bsnes/ui_qt/input/userinterface.hpp new file mode 100755 index 0000000..328a9e2 --- /dev/null +++ b/bsnes/ui_qt/input/userinterface.hpp @@ -0,0 +1,33 @@ +struct InputUiGeneral : public InputGroup { + InputObject loadCartridge; + InputObject pauseEmulation; + InputObject resetSystem; + InputObject powerCycleSystem; + InputObject lowerSpeed; + InputObject raiseSpeed; + InputObject toggleCheatSystem; + InputObject toggleFullscreen; + InputObject toggleMenu; + InputObject toggleStatus; + InputObject exitEmulator; + + InputUiGeneral(); +}; + +struct InputGroupPool : public array { + void attach(InputGroup &group); + void bind(); + void clear(); + void poll(const int16_t *table); + InputGroupPool(); + +private: + array &list; +}; + +struct InputUiPool : public InputGroupPool { + InputUiPool(); +}; + +extern InputUiGeneral inputUiGeneral; +extern InputUiPool inputUiPool; diff --git a/bsnes/ui_qt/interface.cpp b/bsnes/ui_qt/interface.cpp new file mode 100755 index 0000000..4a343ee --- /dev/null +++ b/bsnes/ui_qt/interface.cpp @@ -0,0 +1,31 @@ +SNESInterface snesinterface; + +void SNESInterface::video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height) { + uint32_t *output; + unsigned outpitch; + if(video.lock(output, outpitch) == true) { + unsigned outwidth, outheight; + libfilter::filter.render(output, outpitch, outwidth, outheight, data, pitch, line, width, height); + video.unlock(); + video.refresh(outwidth, outheight); + } +} + +void SNESInterface::audio_sample(uint16_t left, uint16_t right) { + if(config.audio.mute) left = right = 0; + audio.sample(left, right); +} + +void SNESInterface::input_poll() { + inputManager.poll(); +} + +int16_t SNESInterface::input_poll(unsigned deviceid, unsigned id) { + return inputManager.getStatus(deviceid, id); +} + +void SNESInterface::init() { +} + +void SNESInterface::term() { +} diff --git a/bsnes/ui_qt/main.cpp b/bsnes/ui_qt/main.cpp new file mode 100755 index 0000000..764b55a --- /dev/null +++ b/bsnes/ui_qt/main.cpp @@ -0,0 +1,162 @@ +#include "main.hpp" +#include "resource/resource.rcc" + +//nall::string <> QString interface: allows string streaming; automatically converts to UTF-16 +class utf8 : public nall::string { +public: + template utf8& operator<<(T t) { string::operator<<(t); return *this; } + operator const QString() const { return QString::fromUtf8(*this); } +}; + +#include "platform.cpp" +#include "config.cpp" +#include "interface.cpp" +#include "ui.cpp" + +#include "input/input.cpp" +#include "utility/utility.cpp" + +const char defaultStylesheet[] = + "QLabel.title {" + " background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 rgba(255, 0, 0, 48), stop: 1 rgba(128, 0, 0, 96));" + " font-weight: bold;" + " margin-bottom: 5px;" + " padding: 3px;" + "}" + "\n" + "#backdrop {" + " background: #000000;" + "}" + "\n" + "#mouse-capture-box {" + " background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, " + " stop: 0.00 rgba(128, 0, 0, 96), stop: 0.25 rgba(255, 0, 0, 48), " + " stop: 0.75 rgba(255, 0, 0, 48), stop: 1.00 rgba(128, 0, 0, 96));" + " color: #000000;" + " font-weight: bold;" + "}" + "\n"; + +void Application::initPaths(const char *basename) { + char temp[PATH_MAX]; + + if(realpath(basename, temp)) { + //remove program name + strtr(temp, "\\", "/"); + for(signed i = strlen(temp) - 1; i >= 0; i--) { + if(temp[i] == '/') { + temp[i] = 0; + break; + } + } + + if(strend(temp, "/") == false) strcat(temp, "/"); + snes.config.path.base = temp; + } else { + snes.config.path.base = ""; + } + + if(userpath(temp)) { + strtr(temp, "\\", "/"); + if(strend(temp, "/") == false) strcat(temp, "/"); + snes.config.path.user = temp; + } else { + snes.config.path.user = ""; + } + + char cwd[PATH_MAX]; + snes.config.path.current = getcwd(cwd); +} + +void Application::locateFile(string &filename, bool createDataDirectory) { + //first, check if file exists in executable directory (single-user mode) + string temp = string() << snes.config.path.base << filename; + + if(file::exists(temp) == false) { + //if not, use user data path (multi-user mode) + temp = snes.config.path.user; + temp << ".bsnes"; + if(createDataDirectory) mkdir(temp); //ensure directory exists + temp << "/" << filename; + } + + filename = temp; +} + +int Application::main(int argc, char **argv) { + app = new App(argc, argv); + #if !defined(_WIN32) + //Windows port uses 256x256 icon from resource file + app->setWindowIcon(QIcon(":/bsnes.png")); + #endif + + initargs(argc, argv); //ensure argv[]s are in UTF-8 format + initPaths(argv[0]); + locateFile(configFilename = "bsnes.cfg", true); + locateFile(styleSheetFilename = "style.qss", false); + + string customStylesheet; + if(customStylesheet.readfile(styleSheetFilename) == true) { + app->setStyleSheet((const char*)customStylesheet); + } else { + app->setStyleSheet(defaultStylesheet); + } + + config.load(configFilename); + init(); + snes.init(); + + if(argc == 2) { + //if valid file was specified on the command-line, attempt to load it now + utility.loadCartridge(argv[1]); + } + + while(terminate == false) { + processEvents(); + utility.updateSystemState(); + inputManager.refresh(); + + if(config.input.focusPolicy == Configuration::Input::FocusPolicyPauseEmulation) { + bool inactive = (winMain->window->isActiveWindow() == false); + if(!autopause && inactive) { + autopause = true; + audio.clear(); + } else if(autopause && !inactive) { + autopause = false; + } + } else { + autopause = false; + } + + if(cartridge.loaded() && !pause && !autopause) { + snes.runtoframe(); + } else { + usleep(20 * 1000); + } + + supressScreenSaver(); + } + + config.save(configFilename); + return 0; +} + +void Application::processEvents() { + app->processEvents(); +} + +Application::Application() { + terminate = false; + power = false; + pause = false; + autopause = false; +} + +Application::~Application() { + //deleting (QApplication)app will segfault the application upon exit + //delete app; +} + +int main(int argc, char **argv) { + return application.main(argc, argv); +} diff --git a/bsnes/ui_qt/main.hpp b/bsnes/ui_qt/main.hpp new file mode 100755 index 0000000..73227f2 --- /dev/null +++ b/bsnes/ui_qt/main.hpp @@ -0,0 +1,62 @@ +#define UNICODE +#define QT_NO_DEBUG +#define QT_CORE_LIB +#define QT_GUI_LIB +#define QT_THREAD_SUPPORT + +#include +#include +//Q_IMPORT_PLUGIN(QJpegPlugin) +//Q_IMPORT_PLUGIN(QMngPlugin) + +#include <../base.hpp> +#include <../cart/cart.hpp> + +#include +#include +#include +using namespace nall; + +#include +using namespace ruby; + +#include + +#include "input/input.hpp" +#include "utility/utility.hpp" + +class Application { +public: + class App : public QApplication { + public: + #ifdef _WIN32 + bool winEventFilter(MSG *msg, long *result); + #endif + App(int argc, char **argv) : QApplication(argc, argv) {} + } *app; + + bool terminate; //set to true to terminate main() loop and exit emulator + bool power; + bool pause; + bool autopause; + + string configFilename; + string styleSheetFilename; + + int main(int argc, char **argv); + void processEvents(); + void locateFile(string &filename, bool createDataDirectory = false); + void initPaths(const char *basename); + void init(); + + Application(); + ~Application(); +} application; + +struct Style { + enum { + WindowMargin = 5, + WidgetSpacing = 5, + SeparatorSpacing = 5, + }; +}; diff --git a/bsnes/ui_qt/platform.cpp b/bsnes/ui_qt/platform.cpp new file mode 100755 index 0000000..d3abb4e --- /dev/null +++ b/bsnes/ui_qt/platform.cpp @@ -0,0 +1,99 @@ +//platform-specific wrappers + +#if defined(_WIN32) + //Windows 32-bit and 64-bit + #define WIN32_LEAN_AND_MEAN + #include + #include + + char* realpath(const char *filename, char *resolvedname) { + wchar_t fn[_MAX_PATH] = L""; + _wfullpath(fn, nall::utf16_t(filename), _MAX_PATH); + strcpy(resolvedname, nall::utf8_t(fn)); + return resolvedname; + } + + char* userpath(char *path) { + wchar_t fp[_MAX_PATH] = L""; + SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp); + strcpy(path, nall::utf8_t(fp)); + return path; + } + + char* getcwd(char *path) { + wchar_t fp[_MAX_PATH] = L""; + _wgetcwd(fp, _MAX_PATH); + strcpy(path, nall::utf8_t(fp)); + return path; + } + + int mkdir(const char *path) { + return _wmkdir(nall::utf16_t(path)); + } + + void initargs(int &argc, char **&argv) { + wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + argv = new char*[argc]; + for(unsigned i = 0; i < argc; i++) { + argv[i] = new char[_MAX_PATH]; + strcpy(argv[i], nall::utf8_t(wargv[i])); + } + } + + bool Application::App::winEventFilter(MSG *msg, long *result) { + if(msg->message == WM_SYSCOMMAND) { + if(msg->wParam == SC_SCREENSAVE || msg->wParam == SC_MONITORPOWER) { + *result = 0; + return true; + } + } + return false; + } + + void supressScreenSaver() { + //handled by event filter above + } +#else + #define None XNone + #define Window XWindow + #include + #undef None + #undef Window + + //POSIX-compatible (Linux, BSD, etc.) + char* userpath(char *path) { + *path = 0; + struct passwd *userinfo = getpwuid(getuid()); + if(userinfo) strcpy(path, userinfo->pw_dir); + return path; + } + + char *getcwd(char *path) { + return getcwd(path, PATH_MAX); + } + + #define mkdir(path) (mkdir)(path, 0755) + + void initargs(int &argc, char **&argv) { + } + + void supressScreenSaver() { + static clock_t delta_x = 0, delta_y = 0; + + delta_y = clock(); + if(delta_y - delta_x < CLOCKS_PER_SEC * 20) return; + delta_x = delta_y; + + //XSetScreenSaver(timeout = 0) does not work + //XResetScreenSaver() does not work + //XScreenSaverSuspend() does not work + //DPMSDisable() does not work + //XSendEvent(KeyPressMask) does not work + //use XTest extension to send fake keypress every ~20 seconds. + //keycode of 255 does not map to any actual key, + //but it will block screensaver and power management. + Display *display = XOpenDisplay(0); + XTestFakeKeyEvent(display, 255, True, 0); + XTestFakeKeyEvent(display, 255, False, 0); + } +#endif diff --git a/bsnes/ui_qt/resource/resource.qrc b/bsnes/ui_qt/resource/resource.qrc new file mode 100755 index 0000000..edc7020 --- /dev/null +++ b/bsnes/ui_qt/resource/resource.qrc @@ -0,0 +1,10 @@ + + + + ../../data/bsnes.png + ../../data/logo.png + ../../data/joypad.png + ../../data/documentation.html + ../../data/license.html + + diff --git a/bsnes/ui_qt/resource/resource.rc b/bsnes/ui_qt/resource/resource.rc new file mode 100755 index 0000000..63dfef4 --- /dev/null +++ b/bsnes/ui_qt/resource/resource.rc @@ -0,0 +1,2 @@ +1 24 "data/bsnes.Manifest" +IDI_ICON1 ICON DISCARDABLE "data/bsnes.ico" diff --git a/bsnes/ui_qt/settings/advanced.cpp b/bsnes/ui_qt/settings/advanced.cpp new file mode 100755 index 0000000..9f6161f --- /dev/null +++ b/bsnes/ui_qt/settings/advanced.cpp @@ -0,0 +1,183 @@ +void AdvancedSettingsWindow::setup() { + panel = new QWidget; + + layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + + title = new QLabel("Advanced Configuration Settings"); + title->setProperty("class", "title"); + layout->addWidget(title); + + driverLayout = new QGridLayout; + driverLayout->setVerticalSpacing(0); + driverLayout->setHorizontalSpacing(Style::WidgetSpacing); { + videoLabel = new QLabel("Video driver:"); + driverLayout->addWidget(videoLabel, 0, 0); + + audioLabel = new QLabel("Audio driver:"); + driverLayout->addWidget(audioLabel, 0, 1); + + inputLabel = new QLabel("Input driver:"); + driverLayout->addWidget(inputLabel, 0, 2); + + videoDriver = new QComboBox; + driverLayout->addWidget(videoDriver, 1, 0); + + audioDriver = new QComboBox; + driverLayout->addWidget(audioDriver, 1, 1); + + inputDriver = new QComboBox; + driverLayout->addWidget(inputDriver, 1, 2); + + driverInfo = new QLabel("Note: driver changes require restart to take effect."); + driverLayout->addWidget(driverInfo, 2, 0, 1, 3); + } + layout->addLayout(driverLayout); + layout->addSpacing(Style::WidgetSpacing); + + regionTitle = new QLabel("Hardware region:"); + layout->addWidget(regionTitle); + + regionLayout = new QHBoxLayout; + regionLayout->setSpacing(Style::WidgetSpacing); { + regionGroup = new QButtonGroup(panel); + + regionAuto = new QRadioButton("Auto-detect"); + regionAuto->setToolTip("Automatically select hardware region on cartridge load"); + regionGroup->addButton(regionAuto); + regionLayout->addWidget(regionAuto); + + regionNTSC = new QRadioButton("NTSC"); + regionNTSC->setToolTip("Force NTSC region (Japan, Korea, US)"); + regionGroup->addButton(regionNTSC); + regionLayout->addWidget(regionNTSC); + + regionPAL = new QRadioButton("PAL"); + regionPAL->setToolTip("Force PAL region (Europe, ...)"); + regionGroup->addButton(regionPAL); + regionLayout->addWidget(regionPAL); + } + layout->addLayout(regionLayout); + layout->addSpacing(Style::WidgetSpacing); + + portTitle = new QLabel("Expansion port device:"); + layout->addWidget(portTitle); + + portLayout = new QHBoxLayout; + portLayout->setSpacing(Style::WidgetSpacing); { + portGroup = new QButtonGroup(panel); + + portSatellaview = new QRadioButton("Satellaview"); + portGroup->addButton(portSatellaview); + portLayout->addWidget(portSatellaview); + + portNone = new QRadioButton("None"); + portGroup->addButton(portNone); + portLayout->addWidget(portNone); + + portSpacer = new QWidget; + portLayout->addWidget(portSpacer); + } + layout->addLayout(portLayout); + layout->addSpacing(Style::WidgetSpacing); + + focusTitle = new QLabel("When main window does not have focus:"); + layout->addWidget(focusTitle); + + focusLayout = new QHBoxLayout; + focusLayout->setSpacing(Style::WidgetSpacing); { + focusButtonGroup = new QButtonGroup(panel); + + focusPause = new QRadioButton("Pause emulation"); + focusPause->setToolTip("Ideal for prolonged multi-tasking"); + focusButtonGroup->addButton(focusPause); + focusLayout->addWidget(focusPause); + + focusIgnore = new QRadioButton("Ignore input"); + focusIgnore->setToolTip("Ideal for light multi-tasking when using keyboard"); + focusButtonGroup->addButton(focusIgnore); + focusLayout->addWidget(focusIgnore); + + focusAllow = new QRadioButton("Allow input"); + focusAllow->setToolTip("Ideal for light multi-tasking when using joypad(s)"); + focusButtonGroup->addButton(focusAllow); + focusLayout->addWidget(focusAllow); + } + layout->addLayout(focusLayout); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(spacer); + + panel->setLayout(layout); + initializeUi(); + + connect(videoDriver, SIGNAL(currentIndexChanged(int)), this, SLOT(videoDriverChange(int))); + connect(audioDriver, SIGNAL(currentIndexChanged(int)), this, SLOT(audioDriverChange(int))); + connect(inputDriver, SIGNAL(currentIndexChanged(int)), this, SLOT(inputDriverChange(int))); + connect(regionAuto, SIGNAL(pressed()), this, SLOT(setRegionAuto())); + connect(regionNTSC, SIGNAL(pressed()), this, SLOT(setRegionNTSC())); + connect(regionPAL, SIGNAL(pressed()), this, SLOT(setRegionPAL())); + connect(portSatellaview, SIGNAL(pressed()), this, SLOT(setPortSatellaview())); + connect(portNone, SIGNAL(pressed()), this, SLOT(setPortNone())); + connect(focusPause, SIGNAL(pressed()), this, SLOT(pauseWithoutFocus())); + connect(focusIgnore, SIGNAL(pressed()), this, SLOT(ignoreInputWithoutFocus())); + connect(focusAllow, SIGNAL(pressed()), this, SLOT(allowInputWithoutFocus())); +} + +void AdvancedSettingsWindow::initializeUi() { + lstring part; + + part.split(";", video.driver_list()); + for(unsigned i = 0; i < part.size(); i++) { + videoDriver->addItem(utf8() << part[i]); + if(part[i] == config.system.video) videoDriver->setCurrentIndex(i); + } + + part.split(";", audio.driver_list()); + for(unsigned i = 0; i < part.size(); i++) { + audioDriver->addItem(utf8() << part[i]); + if(part[i] == config.system.audio) audioDriver->setCurrentIndex(i); + } + + part.split(";", input.driver_list()); + for(unsigned i = 0; i < part.size(); i++) { + inputDriver->addItem(utf8() << part[i]); + if(part[i] == config.system.input) inputDriver->setCurrentIndex(i); + } + + regionAuto->setChecked(snes.config.region == SNES::Autodetect); + regionNTSC->setChecked(snes.config.region == SNES::NTSC); + regionPAL->setChecked (snes.config.region == SNES::PAL); + + portSatellaview->setChecked(snes.config.expansion_port == SNES::ExpansionBSX); + portNone->setChecked (snes.config.expansion_port == SNES::ExpansionNone); + + focusPause->setChecked (config.input.focusPolicy == Configuration::Input::FocusPolicyPauseEmulation); + focusIgnore->setChecked(config.input.focusPolicy == Configuration::Input::FocusPolicyIgnoreInput); + focusAllow->setChecked (config.input.focusPolicy == Configuration::Input::FocusPolicyAllowInput); +} + +void AdvancedSettingsWindow::videoDriverChange(int index) { + if(index >= 0) config.system.video = videoDriver->itemText(index).toUtf8().data(); +} + +void AdvancedSettingsWindow::audioDriverChange(int index) { + if(index >= 0) config.system.audio = audioDriver->itemText(index).toUtf8().data(); +} + +void AdvancedSettingsWindow::inputDriverChange(int index) { + if(index >= 0) config.system.input = inputDriver->itemText(index).toUtf8().data(); +} + +void AdvancedSettingsWindow::setRegionAuto() { snes.config.region = SNES::Autodetect; } +void AdvancedSettingsWindow::setRegionNTSC() { snes.config.region = SNES::NTSC; } +void AdvancedSettingsWindow::setRegionPAL() { snes.config.region = SNES::PAL; } + +void AdvancedSettingsWindow::setPortSatellaview() { snes.config.expansion_port = SNES::ExpansionBSX; } +void AdvancedSettingsWindow::setPortNone() { snes.config.expansion_port = SNES::ExpansionNone; } + +void AdvancedSettingsWindow::pauseWithoutFocus() { config.input.focusPolicy = Configuration::Input::FocusPolicyPauseEmulation; } +void AdvancedSettingsWindow::ignoreInputWithoutFocus() { config.input.focusPolicy = Configuration::Input::FocusPolicyIgnoreInput; } +void AdvancedSettingsWindow::allowInputWithoutFocus() { config.input.focusPolicy = Configuration::Input::FocusPolicyAllowInput; } diff --git a/bsnes/ui_qt/settings/advanced.hpp b/bsnes/ui_qt/settings/advanced.hpp new file mode 100755 index 0000000..e2cd094 --- /dev/null +++ b/bsnes/ui_qt/settings/advanced.hpp @@ -0,0 +1,54 @@ +class AdvancedSettingsWindow : public QObject { + Q_OBJECT + +public: + QWidget *panel; + QVBoxLayout *layout; + QLabel *title; + QGridLayout *driverLayout; + QLabel *videoLabel; + QLabel *audioLabel; + QLabel *inputLabel; + QComboBox *videoDriver; + QComboBox *audioDriver; + QComboBox *inputDriver; + QLabel *driverInfo; + + QLabel *regionTitle; + QHBoxLayout *regionLayout; + QButtonGroup *regionGroup; + QRadioButton *regionAuto; + QRadioButton *regionNTSC; + QRadioButton *regionPAL; + + QLabel *portTitle; + QHBoxLayout *portLayout; + QButtonGroup *portGroup; + QRadioButton *portSatellaview; + QRadioButton *portNone; + QWidget *portSpacer; + + QLabel *focusTitle; + QHBoxLayout *focusLayout; + QButtonGroup *focusButtonGroup; + QRadioButton *focusPause; + QRadioButton *focusIgnore; + QRadioButton *focusAllow; + QWidget *spacer; + + void setup(); + void initializeUi(); + +public slots: + void videoDriverChange(int index); + void audioDriverChange(int index); + void inputDriverChange(int index); + void setRegionAuto(); + void setRegionNTSC(); + void setRegionPAL(); + void setPortSatellaview(); + void setPortNone(); + void pauseWithoutFocus(); + void ignoreInputWithoutFocus(); + void allowInputWithoutFocus(); +} *winAdvancedSettings; diff --git a/bsnes/ui_qt/settings/audio.cpp b/bsnes/ui_qt/settings/audio.cpp new file mode 100755 index 0000000..e029ffc --- /dev/null +++ b/bsnes/ui_qt/settings/audio.cpp @@ -0,0 +1,129 @@ +void AudioSettingsWindow::setup() { + panel = new QWidget; + + layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + + title = new QLabel("Audio Settings"); + title->setProperty("class", "title"); + layout->addWidget(title); + + boxes = new QHBoxLayout; { + frequencyLabel = new QLabel("Frequency:"); + frequencyLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + boxes->addWidget(frequencyLabel); + + frequency = new QComboBox; + frequency->addItem("32000hz"); + frequency->addItem("44100hz"); + frequency->addItem("48000hz"); + frequency->addItem("96000hz"); + boxes->addWidget(frequency); + + latencyLabel = new QLabel("Latency:"); + latencyLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + boxes->addWidget(latencyLabel); + + latency = new QComboBox; + latency->addItem("20ms"); + latency->addItem("40ms"); + latency->addItem("60ms"); + latency->addItem("80ms"); + latency->addItem("100ms"); + latency->addItem("120ms"); + boxes->addWidget(latency); + } + boxes->setSpacing(Style::WidgetSpacing); + layout->addLayout(boxes); + layout->addSpacing(Style::WidgetSpacing); + + sliders = new QGridLayout; { + volumeLabel = new QLabel("Volume: 100%"); + volumeLabel->setToolTip("Warning: any volume other than 100% will result in a slight audio quality loss"); + sliders->addWidget(volumeLabel, 0, 0); + + volume = new QSlider(Qt::Horizontal); + volume->setMinimum(0); + volume->setMaximum(200); + sliders->addWidget(volume, 0, 1); + + frequencySkewLabel = new QLabel("Input frequency: 32000hz"); + frequencySkewLabel->setToolTip( + "Adjusts audio resampling rate.\n" + "When both video sync and audio sync are enabled, use this setting to fine-tune the output.\n" + "Lower the input frequency to clean audio output, eliminating crackling / popping.\n" + "Raise the input frequency to smooth video output, eliminating duplicated frames." + ); + sliders->addWidget(frequencySkewLabel, 1, 0); + + frequencySkew = new QSlider(Qt::Horizontal); + frequencySkew->setMinimum(31800); + frequencySkew->setMaximum(32200); + sliders->addWidget(frequencySkew); + } + sliders->setSpacing(Style::WidgetSpacing); + layout->addLayout(sliders); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(spacer); + + panel->setLayout(layout); + connect(frequency, SIGNAL(currentIndexChanged(int)), this, SLOT(frequencyChange(int))); + connect(latency, SIGNAL(currentIndexChanged(int)), this, SLOT(latencyChange(int))); + connect(volume, SIGNAL(valueChanged(int)), this, SLOT(volumeAdjust(int))); + connect(frequencySkew, SIGNAL(valueChanged(int)), this, SLOT(frequencySkewAdjust(int))); + + syncUi(); +} + +void AudioSettingsWindow::syncUi() { + int n; + + n = config.audio.outputFrequency; + if(n <= 32000) frequency->setCurrentIndex(0); + else if(n <= 44100) frequency->setCurrentIndex(1); + else if(n <= 48000) frequency->setCurrentIndex(2); + else if(n <= 96000) frequency->setCurrentIndex(3); + else frequency->setCurrentIndex(0); + + n = config.audio.latency; + latency->setCurrentIndex((n - 20) / 20); + + n = config.audio.volume; + volumeLabel->setText(utf8() << "Volume: " << n << "%"); + volume->setSliderPosition(n); + + n = config.audio.inputFrequency; + frequencySkewLabel->setText(utf8() << "Input frequency: " << n << "hz"); + frequencySkew->setSliderPosition(n); +} + +void AudioSettingsWindow::frequencyChange(int value) { + switch(value) { default: + case 0: config.audio.outputFrequency = 32000; break; + case 1: config.audio.outputFrequency = 44100; break; + case 2: config.audio.outputFrequency = 48000; break; + case 3: config.audio.outputFrequency = 96000; break; + } + audio.set(Audio::Frequency, config.audio.outputFrequency); +} + +void AudioSettingsWindow::latencyChange(int value) { + value = max(0, min(5, value)); + config.audio.latency = 20 + value * 20; + audio.set(Audio::Latency, config.audio.latency); +} + +void AudioSettingsWindow::volumeAdjust(int value) { + config.audio.volume = value; + audio.set(Audio::Volume, config.audio.volume); + syncUi(); +} + +void AudioSettingsWindow::frequencySkewAdjust(int value) { + config.audio.inputFrequency = value; + utility.updateEmulationSpeed(); + syncUi(); +} diff --git a/bsnes/ui_qt/settings/audio.hpp b/bsnes/ui_qt/settings/audio.hpp new file mode 100755 index 0000000..f0667b4 --- /dev/null +++ b/bsnes/ui_qt/settings/audio.hpp @@ -0,0 +1,28 @@ +class AudioSettingsWindow : public QObject { + Q_OBJECT + +public: + QWidget *panel; + QVBoxLayout *layout; + QLabel *title; + QHBoxLayout *boxes; + QLabel *frequencyLabel; + QComboBox *frequency; + QLabel *latencyLabel; + QComboBox *latency; + QGridLayout *sliders; + QLabel *volumeLabel; + QSlider *volume; + QLabel *frequencySkewLabel; + QSlider *frequencySkew; + QWidget *spacer; + + void setup(); + void syncUi(); + +public slots: + void frequencyChange(int value); + void latencyChange(int value); + void volumeAdjust(int value); + void frequencySkewAdjust(int value); +} *winAudioSettings; diff --git a/bsnes/ui_qt/settings/cheateditor.cpp b/bsnes/ui_qt/settings/cheateditor.cpp new file mode 100755 index 0000000..c9a93f5 --- /dev/null +++ b/bsnes/ui_qt/settings/cheateditor.cpp @@ -0,0 +1,156 @@ +void CheatEditorWindow::setup() { + panel = new QWidget; + + layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + + title = new QLabel("Cheat Code Editor"); + title->setProperty("class", "title"); + layout->addWidget(title); + + list = new QTreeWidget; + list->setColumnCount(4); + list->setHeaderLabels(QStringList() << "Hidden" << "" << "Code" << "Description"); + list->sortByColumn(0, Qt::AscendingOrder); //set initial sorting, preserve user setting after reloadList() + list->hideColumn(0); //used for default sorting + hides child expansion box + layout->addWidget(list); + layout->addSpacing(Style::WidgetSpacing); + + controls = new QHBoxLayout; { + addCode = new QPushButton("Add Code ..."); + controls->addWidget(addCode); + + editCode = new QPushButton("Edit Code ..."); + controls->addWidget(editCode); + + deleteCode = new QPushButton("Delete Code"); + controls->addWidget(deleteCode); + } + controls->setSpacing(Style::WidgetSpacing); + layout->addLayout(controls); + + panel->setLayout(layout); + connect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemChanged(QTreeWidgetItem*))); + connect(list, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(editSelectedCode())); + connect(list, SIGNAL(itemSelectionChanged()), this, SLOT(listChanged())); + connect(addCode, SIGNAL(released()), this, SLOT(addNewCode())); + connect(editCode, SIGNAL(released()), this, SLOT(editSelectedCode())); + connect(deleteCode, SIGNAL(released()), this, SLOT(deleteSelectedCode())); + + reloadList(); +} + +void CheatEditorWindow::syncUi() { + addCode->setEnabled(cartridge.loaded()); + QList itemList = list->selectedItems(); + editCode->setEnabled(cartridge.loaded() && itemList.count() == 1); + deleteCode->setEnabled(cartridge.loaded() && itemList.count() == 1); +} + +//called when loading a new game, or after adding / deleting a code: +//synchronizes list with cheat class list +void CheatEditorWindow::reloadList() { + disconnect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemChanged(QTreeWidgetItem*))); + + list->clear(); + list->setSortingEnabled(false); + listItem.reset(); + + if(cartridge.loaded()) { + for(unsigned i = 0; i < cheat.count(); i++) { + Cheat::cheat_t code; + cheat.get(i, code); + + //only want to show one code / description line in list + lstring lcode, ldesc; + lcode.split("+", code.code); + ldesc.split("\n", code.desc); + if(lcode.size() > 1) lcode[0] << "+" << (int)(lcode.size() - 1); + if(ldesc.size() > 1) ldesc[0] << " ..."; + + QTreeWidgetItem *item = new QTreeWidgetItem(list); + item->setText(0, utf8() << (int)(1000000 + i)); + item->setCheckState(1, code.enabled ? Qt::Checked : Qt::Unchecked); + item->setText(2, utf8() << lcode[0]); + item->setText(3, utf8() << ldesc[0]); + listItem.add(item); + } + } + + list->setSortingEnabled(true); + list->resizeColumnToContents(1); //shrink checkbox column width + list->resizeColumnToContents(2); //shrink code column width + connect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemChanged(QTreeWidgetItem*))); + syncUi(); +} + +//called when modifying an existing code: +//list items are all still valid, only need to update item codes + descriptions +void CheatEditorWindow::updateList() { + disconnect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemChanged(QTreeWidgetItem*))); + + for(unsigned i = 0; i < listItem.size(); i++) { + Cheat::cheat_t code; + cheat.get(i, code); + + //only want to show one code / description line in list + lstring lcode, ldesc; + lcode.split("+", code.code); + ldesc.split("\n", code.desc); + if(lcode.size() > 1) lcode[0] << "+" << (int)(lcode.size() - 1); + if(ldesc.size() > 1) ldesc[0] << " ..."; + + QTreeWidgetItem *item = listItem[i]; + item->setCheckState(1, code.enabled ? Qt::Checked : Qt::Unchecked); + item->setText(2, utf8() << lcode[0]); + item->setText(3, utf8() << ldesc[0]); + } + + list->resizeColumnToContents(1); //shrink checkbox column width + list->resizeColumnToContents(2); //shrink code column width + connect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemChanged(QTreeWidgetItem*))); + syncUi(); +} + +//called when item enabled checkbox was clicked (eg cheat code enable state was toggled on or off) +void CheatEditorWindow::itemChanged(QTreeWidgetItem *item) { + signed i = listItem.find(item); + if(i >= 0) item->checkState(1) == Qt::Checked ? cheat.enable(i) : cheat.disable(i); +} + +void CheatEditorWindow::listChanged() { + syncUi(); +} + +void CheatEditorWindow::addNewCode() { + if(cartridge.loaded()) winCodeEditor->addCode(); +} + +void CheatEditorWindow::editSelectedCode() { + if(cartridge.loaded()) { + QTreeWidgetItem *item = list->currentItem(); + if(item && item->isSelected()) { + signed i = listItem.find(item); + if(i >= 0) winCodeEditor->editCode(i); + } + } +} + +void CheatEditorWindow::deleteSelectedCode() { + if(cartridge.loaded()) { + QTreeWidgetItem *item = list->currentItem(); + if(item && item->isSelected()) { + signed i = listItem.find(item); + if(i >= 0) { + //if code editor is modifying an existing code, its index will be invalidated by delete; + //therefore, the editor window must be closed first + if(winCodeEditor->activeCode >= 0) winCodeEditor->dismiss(); + + //remove code, and resync listItem with cheat list + cheat.remove(i); + reloadList(); + } + } + } +} diff --git a/bsnes/ui_qt/settings/cheateditor.hpp b/bsnes/ui_qt/settings/cheateditor.hpp new file mode 100755 index 0000000..053f50c --- /dev/null +++ b/bsnes/ui_qt/settings/cheateditor.hpp @@ -0,0 +1,28 @@ +class CheatEditorWindow : public QObject { + Q_OBJECT + +public: + QWidget *panel; + QVBoxLayout *layout; + QLabel *title; + QTreeWidget *list; + QHBoxLayout *controls; + QPushButton *addCode; + QPushButton *editCode; + QPushButton *deleteCode; + + void setup(); + void syncUi(); + void reloadList(); + void updateList(); + +public slots: + void itemChanged(QTreeWidgetItem *item); + void listChanged(); + void addNewCode(); + void editSelectedCode(); + void deleteSelectedCode(); + +private: + array listItem; +} *winCheatEditor; diff --git a/bsnes/ui_qt/settings/input.cpp b/bsnes/ui_qt/settings/input.cpp new file mode 100755 index 0000000..0220bdf --- /dev/null +++ b/bsnes/ui_qt/settings/input.cpp @@ -0,0 +1,174 @@ +void InputSettingsWindow::setup() { + panel = new QWidget; + + layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + + title = new QLabel("Input Configuration Editor"); + title->setProperty("class", "title"); + layout->addWidget(title); + + selection = new QHBoxLayout; { + port = new QComboBox; + port->addItem("Controller Port 1"); + port->addItem("Controller Port 2"); + port->addItem("User Interface"); + selection->addWidget(port); + + device = new QComboBox; + selection->addWidget(device); + } + selection->setSpacing(Style::WidgetSpacing); + layout->addLayout(selection); + layout->addSpacing(Style::WidgetSpacing); + + list = new QTreeWidget; + list->setColumnCount(3); + list->setHeaderLabels(QStringList() << "Hidden" << "Name" << "Assignment"); + list->hideColumn(0); //used for default sorting + hides child expansion box + layout->addWidget(list); + layout->addSpacing(Style::WidgetSpacing); + + controls = new QHBoxLayout; { + assign = new QPushButton("Assign ..."); + controls->addWidget(assign); + + assignAll = new QPushButton("Assign All ..."); + controls->addWidget(assignAll); + + unassign = new QPushButton("Unassign"); + controls->addWidget(unassign); + } + controls->setSpacing(Style::WidgetSpacing); + layout->addLayout(controls); + + panel->setLayout(layout); + connect(port, SIGNAL(currentIndexChanged(int)), this, SLOT(portChanged())); + connect(device, SIGNAL(currentIndexChanged(int)), this, SLOT(reloadList())); + connect(list, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(assignKey())); + connect(list, SIGNAL(itemSelectionChanged()), this, SLOT(listChanged())); + connect(assign, SIGNAL(released()), this, SLOT(assignKey())); + connect(assignAll, SIGNAL(released()), this, SLOT(assignAllKeys())); + connect(unassign, SIGNAL(released()), this, SLOT(unassignKey())); + + portChanged(); +} + +void InputSettingsWindow::syncUi() { + QList itemList = list->selectedItems(); + assign->setEnabled(itemList.count() == 1); + //allow rapid assign for controllers from both ports, but not for UI shortcuts + assignAll->setEnabled(port->currentIndex() < 2); + unassign->setEnabled(itemList.count() == 1); +} + +//when port combobox item is changed, device list needs to be repopulated +void InputSettingsWindow::portChanged() { + disconnect(device, SIGNAL(currentIndexChanged(int)), this, SLOT(reloadList())); + + device->clear(); + deviceItem.reset(); + + int index = port->currentIndex(); + if(index < 2) { + //this is a controller port + for(unsigned i = 0; i < inputPool.size(); i++) { + //only add devices for selected port + if(inputPool[i]->port == index) { + device->addItem(inputPool[i]->name); + deviceItem.add(inputPool[i]); + } + } + } else { + //user interface controls + for(unsigned i = 0; i < inputUiPool.size(); i++) { + device->addItem(inputUiPool[i]->name); + deviceItem.add(inputUiPool[i]); + } + } + + reloadList(); + connect(device, SIGNAL(currentIndexChanged(int)), this, SLOT(reloadList())); +} + +//when device combobox item is changed, object list needs to be repopulated +void InputSettingsWindow::reloadList() { + list->clear(); + list->setSortingEnabled(false); + listItem.reset(); + + int index = device->currentIndex(); + if(index < deviceItem.size()) { + InputGroup &group = *deviceItem[index]; + for(unsigned i = 0; i < group.size(); i++) { + QTreeWidgetItem *item = new QTreeWidgetItem(list); + item->setText(0, utf8() << (int)(1000000 + i)); + item->setText(1, group[i]->name); + item->setText(2, (const char*)group[i]->id); + listItem.add(item); + } + } + + list->setSortingEnabled(true); + list->sortByColumn(0, Qt::AscendingOrder); //set default sorting on list change, overriding user setting + list->resizeColumnToContents(1); //shrink name column + syncUi(); +} + +void InputSettingsWindow::listChanged() { + syncUi(); +} + +//InputCaptureWindow calls this after a successful key assignment change: +//need to update list of values to show new key assignment value. +void InputSettingsWindow::updateList() { + int index = device->currentIndex(); + if(index < deviceItem.size()) { + InputGroup &group = *deviceItem[index]; + + for(unsigned i = 0; i < listItem.size(); i++) { + listItem[i]->setText(2, (const char*)group[i]->id); + } + } +} + +void InputSettingsWindow::assignKey() { + int index = device->currentIndex(); + if(index < deviceItem.size()) { + InputGroup &group = *deviceItem[index]; + + QTreeWidgetItem *item = list->currentItem(); + if(item && item->isSelected()) { + signed i = listItem.find(item); + if(i >= 0) winInputCapture->activate(group[i]); + } + } +} + +void InputSettingsWindow::assignAllKeys() { + int index = port->currentIndex(); + if(index < 2) { + index = device->currentIndex(); + if(index < deviceItem.size()) { + winInputCapture->activate(deviceItem[index]); + } + } +} + +void InputSettingsWindow::unassignKey() { + int index = device->currentIndex(); + if(index < deviceItem.size()) { + InputGroup &group = *deviceItem[index]; + + QTreeWidgetItem *item = list->currentItem(); + if(item && item->isSelected()) { + signed i = listItem.find(item); + if(i >= 0) { + group[i]->id = "none"; + inputManager.bind(); //update key bindings to reflect object ID change above + item->setText(2, (const char*)group[i]->id); + } + } + } +} diff --git a/bsnes/ui_qt/settings/input.hpp b/bsnes/ui_qt/settings/input.hpp new file mode 100755 index 0000000..97583be --- /dev/null +++ b/bsnes/ui_qt/settings/input.hpp @@ -0,0 +1,32 @@ +class InputSettingsWindow : public QObject { + Q_OBJECT + +public: + QWidget *panel; + QVBoxLayout *layout; + QLabel *title; + QHBoxLayout *selection; + QComboBox *port; + QComboBox *device; + QTreeWidget *list; + QHBoxLayout *controls; + QPushButton *assign; + QPushButton *assignAll; + QPushButton *unassign; + + void setup(); + void syncUi(); + +public slots: + void portChanged(); + void reloadList(); + void listChanged(); + void updateList(); + void assignKey(); + void assignAllKeys(); + void unassignKey(); + +private: + array deviceItem; + array listItem; +} *winInputSettings; diff --git a/bsnes/ui_qt/settings/paths.cpp b/bsnes/ui_qt/settings/paths.cpp new file mode 100755 index 0000000..2b4b0cb --- /dev/null +++ b/bsnes/ui_qt/settings/paths.cpp @@ -0,0 +1,200 @@ +void PathSettingsWindow::setup() { + panel = new QWidget; + + layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + + title = new QLabel("Default Folder Paths"); + title->setProperty("class", "title"); + layout->addWidget(title); + + gameLabel = new QLabel("Games:"); + layout->addWidget(gameLabel); + + games = new QHBoxLayout; { + games->setMargin(0); + + gamePath = new QLineEdit; + gamePath->setReadOnly(true); + games->addWidget(gamePath); + + gameSelect = new QPushButton("Select ..."); + games->addWidget(gameSelect); + + gameDefault = new QPushButton("Default"); + games->addWidget(gameDefault); + } + games->setSpacing(Style::WidgetSpacing); + layout->addLayout(games); + layout->addSpacing(Style::WidgetSpacing); + + saveLabel = new QLabel("Save RAM:"); + layout->addWidget(saveLabel); + + saves = new QHBoxLayout; { + saves->setMargin(0); + + savePath = new QLineEdit; + savePath->setReadOnly(true); + saves->addWidget(savePath); + + saveSelect = new QPushButton("Select ..."); + saves->addWidget(saveSelect); + + saveDefault = new QPushButton("Default"); + saves->addWidget(saveDefault); + } + saves->setSpacing(Style::WidgetSpacing); + layout->addLayout(saves); + layout->addSpacing(Style::WidgetSpacing); + + patchLabel = new QLabel("UPS patches:"); + layout->addWidget(patchLabel); + + patches = new QHBoxLayout; { + patches->setMargin(0); + + patchPath = new QLineEdit; + patchPath->setReadOnly(true); + patches->addWidget(patchPath); + + patchSelect = new QPushButton("Select ..."); + patches->addWidget(patchSelect); + + patchDefault = new QPushButton("Default"); + patches->addWidget(patchDefault); + } + patches->setSpacing(Style::WidgetSpacing); + layout->addLayout(patches); + layout->addSpacing(Style::WidgetSpacing); + + cheatLabel = new QLabel("Cheat codes:"); + layout->addWidget(cheatLabel); + + cheats = new QHBoxLayout; { + cheats->setMargin(0); + + cheatPath = new QLineEdit; + cheatPath->setReadOnly(true); + cheats->addWidget(cheatPath); + + cheatSelect = new QPushButton("Select ..."); + cheats->addWidget(cheatSelect); + + cheatDefault = new QPushButton("Default"); + cheats->addWidget(cheatDefault); + } + cheats->setSpacing(Style::WidgetSpacing); + layout->addLayout(cheats); + layout->addSpacing(Style::WidgetSpacing); + + dataLabel = new QLabel("Export data:"); + layout->addWidget(dataLabel); + + data = new QHBoxLayout; + data->setMargin(0); + data->setSpacing(Style::WidgetSpacing); { + dataPath = new QLineEdit; + dataPath->setReadOnly(true); + data->addWidget(dataPath); + + dataSelect = new QPushButton("Select ..."); + data->addWidget(dataSelect); + + dataDefault = new QPushButton("Default"); + data->addWidget(dataDefault); + } + layout->addLayout(data); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(spacer); + + syncUi(); + + panel->setLayout(layout); + connect(gameSelect, SIGNAL(released()), this, SLOT(selectGamePath())); + connect(gameDefault, SIGNAL(released()), this, SLOT(defaultGamePath())); + connect(saveSelect, SIGNAL(released()), this, SLOT(selectSavePath())); + connect(saveDefault, SIGNAL(released()), this, SLOT(defaultSavePath())); + connect(patchSelect, SIGNAL(released()), this, SLOT(selectPatchPath())); + connect(patchDefault, SIGNAL(released()), this, SLOT(defaultPatchPath())); + connect(cheatSelect, SIGNAL(released()), this, SLOT(selectCheatPath())); + connect(cheatDefault, SIGNAL(released()), this, SLOT(defaultCheatPath())); + connect(dataSelect, SIGNAL(released()), this, SLOT(selectDataPath())); + connect(dataDefault, SIGNAL(released()), this, SLOT(defaultDataPath())); +} + +void PathSettingsWindow::syncUi() { + gamePath->setText (snes.config.path.rom == "" ? "" : (const char*)snes.config.path.rom); + savePath->setText (snes.config.path.save == "" ? "" : (const char*)snes.config.path.save); + patchPath->setText(snes.config.path.patch == "" ? "" : (const char*)snes.config.path.patch); + cheatPath->setText(snes.config.path.cheat == "" ? "" : (const char*)snes.config.path.cheat); + dataPath->setText (snes.config.path.data == "" ? "" : (const char*)snes.config.path.data); +} + +void PathSettingsWindow::selectGamePath() { + string path = utility.selectFolder("Default Game Path"); + if(path.length() > 0) { + snes.config.path.rom = path; + syncUi(); + } +} + +void PathSettingsWindow::defaultGamePath() { + snes.config.path.rom = ""; + syncUi(); +} + +void PathSettingsWindow::selectSavePath() { + string path = utility.selectFolder("Default Save RAM Path"); + if(path.length() > 0) { + snes.config.path.save = path; + syncUi(); + } +} + +void PathSettingsWindow::defaultSavePath() { + snes.config.path.save = ""; + syncUi(); +} + +void PathSettingsWindow::selectPatchPath() { + string path = utility.selectFolder("Default UPS Patch Path"); + if(path.length() > 0) { + snes.config.path.patch = path; + syncUi(); + } +} + +void PathSettingsWindow::defaultPatchPath() { + snes.config.path.patch = ""; + syncUi(); +} + +void PathSettingsWindow::selectCheatPath() { + string path = utility.selectFolder("Default Cheat File Path"); + if(path.length() > 0) { + snes.config.path.cheat = path; + syncUi(); + } +} + +void PathSettingsWindow::defaultCheatPath() { + snes.config.path.cheat = ""; + syncUi(); +} + +void PathSettingsWindow::selectDataPath() { + string path = utility.selectFolder("Default Export Data Path"); + if(path.length() > 0) { + snes.config.path.data = path; + syncUi(); + } +} + +void PathSettingsWindow::defaultDataPath() { + snes.config.path.data = ""; + syncUi(); +} diff --git a/bsnes/ui_qt/settings/paths.hpp b/bsnes/ui_qt/settings/paths.hpp new file mode 100755 index 0000000..a0ca502 --- /dev/null +++ b/bsnes/ui_qt/settings/paths.hpp @@ -0,0 +1,49 @@ +class PathSettingsWindow : public QObject { + Q_OBJECT + +public: + QWidget *panel; + QVBoxLayout *layout; + QLabel *title; + QLabel *gameLabel; + QHBoxLayout *games; + QLineEdit *gamePath; + QPushButton *gameSelect; + QPushButton *gameDefault; + QLabel *saveLabel; + QHBoxLayout *saves; + QLineEdit *savePath; + QPushButton *saveSelect; + QPushButton *saveDefault; + QLabel *patchLabel; + QHBoxLayout *patches; + QLineEdit *patchPath; + QPushButton *patchSelect; + QPushButton *patchDefault; + QLabel *cheatLabel; + QHBoxLayout *cheats; + QLineEdit *cheatPath; + QPushButton *cheatSelect; + QPushButton *cheatDefault; + QLabel *dataLabel; + QHBoxLayout *data; + QLineEdit *dataPath; + QPushButton *dataSelect; + QPushButton *dataDefault; + QWidget *spacer; + + void setup(); + void syncUi(); + +public slots: + void selectGamePath(); + void defaultGamePath(); + void selectSavePath(); + void defaultSavePath(); + void selectPatchPath(); + void defaultPatchPath(); + void selectCheatPath(); + void defaultCheatPath(); + void selectDataPath(); + void defaultDataPath(); +} *winPathSettings; diff --git a/bsnes/ui_qt/settings/settings.cpp b/bsnes/ui_qt/settings/settings.cpp new file mode 100755 index 0000000..e2268e7 --- /dev/null +++ b/bsnes/ui_qt/settings/settings.cpp @@ -0,0 +1,84 @@ +#include "video.cpp" +#include "audio.cpp" +#include "input.cpp" +#include "paths.cpp" +#include "cheateditor.cpp" +#include "advanced.cpp" + +#include "utility/inputcapture.cpp" +#include "utility/codeeditor.cpp" + +void SettingsWindow::setup() { + window = new QWidget; + window->setObjectName("settings-window"); + window->setWindowTitle("Configuration Settings"); + + list = new QListWidget; + video = new QListWidgetItem("Video"); + audio = new QListWidgetItem("Audio"); + input = new QListWidgetItem("Input"); + paths = new QListWidgetItem("Paths"); + cheatcodes = new QListWidgetItem("Cheat Codes"); + advanced = new QListWidgetItem("Advanced"); + list->addItem(video); + list->addItem(audio); + list->addItem(input); + list->addItem(paths); + list->addItem(cheatcodes); + list->addItem(advanced); + list->setCurrentItem(input); //select most frequently used panel by default + list->setFixedWidth(135); + list->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + + panel = new QWidget; + panel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + layout = new QHBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + layout->addWidget(list); + layout->addWidget(panel); + window->setLayout(layout); + + winVideoSettings = new VideoSettingsWindow; + winAudioSettings = new AudioSettingsWindow; + winInputSettings = new InputSettingsWindow; + winPathSettings = new PathSettingsWindow; + winCheatEditor = new CheatEditorWindow; + winAdvancedSettings = new AdvancedSettingsWindow; + winInputCapture = new InputCaptureWindow; + winCodeEditor = new CodeEditorWindow; + + winVideoSettings->setup(); + winAudioSettings->setup(); + winInputSettings->setup(); + winPathSettings->setup(); + winCheatEditor->setup(); + winAdvancedSettings->setup(); + winInputCapture->setup(); + winCodeEditor->setup(); + + panelLayout = new QStackedLayout(panel); + panelLayout->addWidget(winVideoSettings->panel); + panelLayout->addWidget(winAudioSettings->panel); + panelLayout->addWidget(winInputSettings->panel); + panelLayout->addWidget(winPathSettings->panel); + panelLayout->addWidget(winCheatEditor->panel); + panelLayout->addWidget(winAdvancedSettings->panel); + panel->setLayout(panelLayout); + + connect(list, SIGNAL(currentRowChanged(int)), this, SLOT(listChanged())); + + listChanged(); + window->setMinimumSize(600, 360); +} + +void SettingsWindow::listChanged() { + QListWidgetItem *item = list->currentItem(); + if(item == video) panelLayout->setCurrentWidget(winVideoSettings->panel); + if(item == audio) panelLayout->setCurrentWidget(winAudioSettings->panel); + if(item == input) panelLayout->setCurrentWidget(winInputSettings->panel); + if(item == paths) panelLayout->setCurrentWidget(winPathSettings->panel); + if(item == cheatcodes) panelLayout->setCurrentWidget(winCheatEditor->panel); + if(item == advanced) panelLayout->setCurrentWidget(winAdvancedSettings->panel); +} diff --git a/bsnes/ui_qt/settings/settings.hpp b/bsnes/ui_qt/settings/settings.hpp new file mode 100755 index 0000000..8e97014 --- /dev/null +++ b/bsnes/ui_qt/settings/settings.hpp @@ -0,0 +1,31 @@ +#include "video.moc" +#include "audio.moc" +#include "input.moc" +#include "paths.moc" +#include "cheateditor.moc" +#include "advanced.moc" + +#include "utility/inputcapture.moc" +#include "utility/codeeditor.moc" + +class SettingsWindow : public QObject { + Q_OBJECT + +public: + QWidget *window; + QHBoxLayout *layout; + QListWidget *list; + QListWidgetItem *video; + QListWidgetItem *audio; + QListWidgetItem *input; + QListWidgetItem *paths; + QListWidgetItem *cheatcodes; + QListWidgetItem *advanced; + QWidget *panel; + QStackedLayout *panelLayout; + + void setup(); + +public slots: + void listChanged(); +} *winSettings; diff --git a/bsnes/ui_qt/settings/utility/codeeditor.cpp b/bsnes/ui_qt/settings/utility/codeeditor.cpp new file mode 100755 index 0000000..f5d7c7f --- /dev/null +++ b/bsnes/ui_qt/settings/utility/codeeditor.cpp @@ -0,0 +1,180 @@ +void CodeEditorWindow::setup() { + window = new QWidget; + window->setObjectName("code-editor-window"); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(0); + + descLabel = new QLabel("Description:"); + layout->addWidget(descLabel); + + description = new QTextEdit; + layout->addWidget(description); + layout->addSpacing(Style::WidgetSpacing); + + codeLabel = new QLabel("Cheat code(s):"); + layout->addWidget(codeLabel); + + codeLayout = new QHBoxLayout; { + codeLayout->setMargin(0); + + codeList = new QListWidget; + codeLayout->addWidget(codeList); + codeLayout->addSpacing(Style::WidgetSpacing); + + controls = new QVBoxLayout; { + controls->setMargin(0); + + codeValue = new QLineEdit; + controls->addWidget(codeValue); + + codeAdd = new QPushButton("Add Code"); + controls->addWidget(codeAdd); + + codeDelete = new QPushButton("Delete Code"); + controls->addWidget(codeDelete); + + codeDeleteAll = new QPushButton("Delete All"); + controls->addWidget(codeDeleteAll); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + controls->addWidget(spacer); + } + codeLayout->addLayout(controls); + } + layout->addLayout(codeLayout); + layout->addSpacing(Style::WidgetSpacing); + + enabled = new QCheckBox("Enable this cheat code"); + layout->addWidget(enabled); + + finishControls = new QHBoxLayout; { + okButton = new QPushButton("Ok"); + finishControls->addWidget(okButton); + + cancelButton = new QPushButton("Cancel"); + finishControls->addWidget(cancelButton); + } + finishControls->setSpacing(Style::WidgetSpacing); + layout->addLayout(finishControls); + + window->setLayout(layout); + window->setMinimumSize(400, 375); + + connect(codeList, SIGNAL(itemSelectionChanged()), this, SLOT(listChanged())); + connect(codeValue, SIGNAL(textChanged(const QString&)), this, SLOT(codeChanged())); + connect(codeAdd, SIGNAL(released()), this, SLOT(addCodeToList())); + connect(codeDelete, SIGNAL(released()), this, SLOT(deleteCodeFromList())); + connect(codeDeleteAll, SIGNAL(released()), this, SLOT(deleteAllCodesFromList())); + connect(okButton, SIGNAL(released()), this, SLOT(accept())); + connect(cancelButton, SIGNAL(released()), this, SLOT(dismiss())); +} + +void CodeEditorWindow::syncUi() { + //only activate add button when code is valid + string code = codeValue->text().toUtf8().data(); + Cheat::cheat_t temp; + bool valid = cheat.decode(code, temp); + codeAdd->setEnabled(valid); + + //only activate delete button when a code is selected + QListWidgetItem *item = codeList->currentItem(); + codeDelete->setEnabled(item && item->isSelected()); + + //only activate delete all / ok buttons when there are one or more codes entered + codeDeleteAll->setEnabled(codeList->count() > 0); + okButton->setEnabled(codeList->count() > 0); +} + +void CodeEditorWindow::listChanged() { syncUi(); } +void CodeEditorWindow::codeChanged() { syncUi(); } + +void CodeEditorWindow::addCodeToList() { + string code = codeValue->text().toUtf8().data(); + Cheat::cheat_t temp; + if(cheat.decode(code, temp) == true) codeList->addItem(utf8() << code); + syncUi(); +} + +void CodeEditorWindow::deleteCodeFromList() { + int index = codeList->currentRow(); + if(index >= 0) { + QListWidgetItem *item = codeList->takeItem(index); + delete item; + } + syncUi(); +} + +void CodeEditorWindow::deleteAllCodesFromList() { + codeList->clear(); + syncUi(); +} + +void CodeEditorWindow::accept() { + string desc = description->toPlainText().toUtf8().data(); + string code; + for(unsigned i = 0; i < codeList->count(); i++) { + code << (codeList->item(i)->text().toUtf8().data()); + if(i != codeList->count() - 1) code << "+"; + } + + if(activeCode == -1) { + //adding a new code + cheat.add(enabled->isChecked(), code, desc); + winCheatEditor->reloadList(); + } else if(codeList->count() > 0) { + //editing an existing code + cheat.edit(activeCode, enabled->isChecked(), code, desc); + winCheatEditor->updateList(); + } else { + //deleting an existing code + cheat.remove(activeCode); + winCheatEditor->reloadList(); + } + + dismiss(); +} + +void CodeEditorWindow::dismiss() { + activeCode = -1; + window->hide(); +} + +void CodeEditorWindow::addCode() { + activeCode = -1; + description->setPlainText(""); + codeList->clear(); + codeValue->setText(""); + enabled->setCheckState(Qt::Unchecked); + showWindow("Add New Cheat Code"); +} + +void CodeEditorWindow::editCode(unsigned code) { + activeCode = code; + codeList->clear(); + codeValue->setText(""); + + Cheat::cheat_t item; + cheat.get(activeCode, item); + + description->setPlainText(utf8() << item.desc); + + lstring part; + part.split("+", item.code); + + for(unsigned i = 0; i < item.count; i++) codeList->addItem(utf8() << part[i]); + enabled->setCheckState(item.enabled ? Qt::Checked : Qt::Unchecked); + showWindow("Edit Existing Cheat Code"); +} + +void CodeEditorWindow::showWindow(const char *title) { + syncUi(); + window->setWindowTitle(title); + utility.showCentered(window); +} + +CodeEditorWindow::CodeEditorWindow() { + activeCode = -1; +} diff --git a/bsnes/ui_qt/settings/utility/codeeditor.hpp b/bsnes/ui_qt/settings/utility/codeeditor.hpp new file mode 100755 index 0000000..dc19466 --- /dev/null +++ b/bsnes/ui_qt/settings/utility/codeeditor.hpp @@ -0,0 +1,43 @@ +class CodeEditorWindow : public QObject { + Q_OBJECT + +public: + QWidget *window; + QVBoxLayout *layout; + QLabel *descLabel; + QTextEdit *description; + QLabel *codeLabel; + QHBoxLayout *codeLayout; + QListWidget *codeList; + QVBoxLayout *controls; + QLineEdit *codeValue; + QPushButton *codeAdd; + QPushButton *codeDelete; + QPushButton *codeDeleteAll; + QWidget *spacer; + QCheckBox *enabled; + QHBoxLayout *finishControls; + QPushButton *okButton; + QPushButton *cancelButton; + + void setup(); + void syncUi(); + void addCode(); + void editCode(unsigned code); + CodeEditorWindow(); + +public slots: + void listChanged(); + void codeChanged(); + void addCodeToList(); + void deleteCodeFromList(); + void deleteAllCodesFromList(); + void accept(); + void dismiss(); + +private: + signed activeCode; + void showWindow(const char *title); + + friend class CheatEditorWindow; +} *winCodeEditor; diff --git a/bsnes/ui_qt/settings/utility/inputcapture.cpp b/bsnes/ui_qt/settings/utility/inputcapture.cpp new file mode 100755 index 0000000..c954367 --- /dev/null +++ b/bsnes/ui_qt/settings/utility/inputcapture.cpp @@ -0,0 +1,454 @@ +void InputCaptureWindow::setup() { + window = new Window; + window->setObjectName("input-capture-window"); + window->setWindowTitle("Input Capture"); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(0); + + hlayout = new QHBoxLayout; + hlayout->setSpacing(Style::WidgetSpacing); { + title = new QLabel; + hlayout->addWidget(title, 0, Qt::AlignTop); + + mouseAxes = new QPushButton(" Assign Mouse Axis "); + mouseAxes->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + hlayout->addWidget(mouseAxes, 0, Qt::AlignTop); + + mouseButtons = new QPushButton(" Assign Mouse Button "); + mouseButtons->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + hlayout->addWidget(mouseButtons, 0, Qt::AlignTop); + } + layout->addLayout(hlayout); + + imageSpacer = new QWidget; + imageSpacer->setFixedSize(Style::WidgetSpacing, Style::WidgetSpacing); + layout->addWidget(imageSpacer); + + imageWidget = new ImageWidget; + layout->addWidget(imageWidget, 0, Qt::AlignHCenter); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(spacer); + + window->setLayout(layout); + connect(mouseAxes, SIGNAL(released()), this, SLOT(assignMouseAxis())); + connect(mouseButtons, SIGNAL(released()), this, SLOT(assignMouseButton())); + + winInputMouseCaptureWindow = new InputMouseCaptureWindow; + winInputMouseCaptureWindow->setup(); + + winInputCalibration = new InputCalibrationWindow; + winInputCalibration->setup(); +} + +void InputCaptureWindow::activate(InputObject *object) { + if(!activeGroup) window->hide(); + + utf8 info; + info << "ID: "; + if(object->parent) { + InputDevice *device = dynamic_cast(object->parent); + if(device) info << "Controller port " << (int)(device->port + 1) << " :: "; + else info << "User interface :: "; + info << object->parent->name << " :: "; + } + info << object->name << "
"; + + activeObject = object; + if(activeObject->type == InputObject::Button) { + mouseAxes->hide(); + mouseButtons->show(); + + info << "Press any key or button to assign to this ID."; + } else /*(activeObject->type == InputObject::Axis)*/ { + mouseAxes->show(); + mouseButtons->hide(); + + info << "Move any axis to assign to this ID."; + } + + if(dynamic_cast(activeObject->parent)) { + imageSpacer->show(); + imageWidget->setFixedSize(480, 210); + imageWidget->show(); + } else { + imageSpacer->hide(); + imageWidget->hide(); + } + + title->setText(info); + utility.showCentered(window); +} + +void InputCaptureWindow::activate(InputGroup *group) { + activeGroup = group; + groupIndex = 0; + window->hide(); + activate((*activeGroup)[groupIndex]); +} + +void InputCaptureWindow::inputEvent(uint16_t code, bool forceAssign /* = false */) { + if(!activeObject || inputLock == true) return; + + //input polling is global, need to block mouse actions that may be UI interactions. + //custom controls on window allow mouse assignment instead. + if(forceAssign == false) { + if(winInputMouseCaptureWindow->window->isActiveWindow()) { + winInputMouseCaptureWindow->inputEvent(code); + return; + } + if(!window->isActiveWindow()) return; + + //get as much info as possible about this code + InputCode::type_t type = InputCode::type(code); + InputCode::axistype_t axisType = InputCode::axisType(code); + int joypadNumber = InputCode::joypadNumber(code); + int16_t state = inputManager.state(code); + unsigned distance = abs(state - inputManager.lastState(code)); + + if(type == InputCode::JoypadHat) { + //hats can be in any of nine clock-wise states (4x direct, 4x angled and centered.) + //only map when hat is moved to an explicit direction. + if(state != joypad<>::hat_up && state != joypad<>::hat_down + && state != joypad<>::hat_left && state != joypad<>::hat_right) return; + } + + if(type == InputCode::JoypadAxis) { + //require a degree of resistance to prevent accidental mapping + if(axisType == InputCode::Stick) { + //require axis to be pressed almost completely toward a specific direction + if(state > -28672 && state < +28672) return; + } else if(axisType == InputCode::Trigger) { + //require trigger to be at least 75% pressed down + if(state > -16384) return; + } else { + //invalid axis type: most likely the controller has yet to be calibrated + if(joypadNumber < 0) return; //should never occur + + //some analog triggers report phantom motion even when user does not touch gamepad + if(distance < 64) return; //require some degree of force to trigger calibration + if(state > -8192 && state < +8192) return; //ignore center motion + + if(inputManager.calibrated(joypadNumber) == false) { + winInputCalibration->activate(joypadNumber); + } + + //block assignment until controller is fully calibrated + return; + } + } + + if(activeObject->type == InputObject::Axis) { + if(type == InputCode::KeyboardButton + || type == InputCode::MouseAxis + || type == InputCode::MouseButton + || type == InputCode::JoypadHat + || type == InputCode::JoypadButton + ) return; + + //uni-directional trigger cannot map to bi-directional axis + if(type == InputCode::JoypadAxis && axisType == InputCode::Trigger) return; + } + + if(activeObject->type == InputObject::Button) { + if(type == InputCode::MouseAxis + || type == InputCode::MouseButton + ) return; + + //only capture on button press, not release + if(type != InputCode::JoypadAxis && state == false) return; + } + + //if assigning a complete controller set, ensure requested key has not been assigned + //to a previous entry in the group already. this prevents slow motion of joypad axes + //from assigning the same value to multiple entries in rapid succession. + if(activeGroup && groupIndex > 0) { + for(unsigned i = 0; i < groupIndex; i++) { + if(code == (*activeGroup)[i]->code) { + //joypad hats and axes have multiple states, and are differentiated by modifier. + //allow mapping only if requested modifier is unique. + if(type == InputCode::JoypadHat) { + if(state == joypad<>::hat_up && (*activeGroup)[i]->modifier == InputObject::Up ) return; + if(state == joypad<>::hat_down && (*activeGroup)[i]->modifier == InputObject::Down ) return; + if(state == joypad<>::hat_left && (*activeGroup)[i]->modifier == InputObject::Left ) return; + if(state == joypad<>::hat_right && (*activeGroup)[i]->modifier == InputObject::Right) return; + } else if(type == InputCode::JoypadAxis) { + if(axisType == InputCode::Stick) { + if(state < 0 && (*activeGroup)[i]->modifier == InputObject::Lo) return; + if(state >= 0 && (*activeGroup)[i]->modifier == InputObject::Hi) return; + } else if(axisType == InputCode::Trigger) { + if((*activeGroup)[i]->modifier == InputObject::Trigger) return; + } + } else { + //this code has already been used, do not map it + return; + } + } + } + } + } + + //bind code and update GUI input assignment list + activeObject->bind(code); + winInputSettings->updateList(); + activeObject = 0; + + //ignore multiple simultaneous state changes. + //this helps with joypads that only activate + //analog inputs after the first button press. + inputLock = true; + for(unsigned i = 0; i < 2; i++) inputManager.refresh(); + inputLock = false; + + if(!activeGroup) { + window->hide(); + winInputMouseCaptureWindow->window->hide(); + } else { + //try and map the next code in this input group + groupIndex++; + if(groupIndex < activeGroup->size()) { + activate((*activeGroup)[groupIndex]); + } else { + //all group codes mapped + window->hide(); + winInputMouseCaptureWindow->window->hide(); + activeGroup = 0; + } + } +} + +void InputCaptureWindow::assignMouseAxis() { + //refresh input state so that mouse release event (from SIGNAL(released()) + //is not sent immediately after window is visible. + inputManager.refresh(); + winInputMouseCaptureWindow->activate(InputMouseCaptureWindow::AxisMode); +} + +void InputCaptureWindow::assignMouseButton() { + inputManager.refresh(); + winInputMouseCaptureWindow->activate(InputMouseCaptureWindow::ButtonMode); +} + +InputCaptureWindow::InputCaptureWindow() { + activeObject = 0; + activeGroup = 0; + groupIndex = 0; + inputLock = false; +} + +void InputCaptureWindow::Window::closeEvent(QCloseEvent*) { + //window closed by user, cancel key assignment + winInputCapture->activeObject = 0; + winInputCapture->activeGroup = 0; +} + +void InputCaptureWindow::ImageWidget::paintEvent(QPaintEvent*) { + //currently, there is only an image available for the joypad. + //in the future, this routine should determine which type of + //image to draw via activeObject->parent's derived class type. + QPainter painter(this); + QPixmap pixmap(":/joypad.png"); + painter.drawPixmap(0, 0, pixmap); +} + +//======================= +//InputMouseCaptureWindow +//======================= + +void InputMouseCaptureWindow::setup() { + window = new QWidget; + window->setObjectName("input-mouse-capture-window"); + window->setWindowTitle("Mouse Input Capture"); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(0); + + info = new QLabel; + layout->addWidget(info); + layout->addSpacing(Style::WidgetSpacing); + + captureBox = new QLabel("[ capture box ]"); + captureBox->setObjectName("mouse-capture-box"); + captureBox->setAlignment(Qt::AlignCenter); + captureBox->setFixedHeight(120); + layout->addWidget(captureBox); + + buttonLayout = new QHBoxLayout; + buttonLayout->setSpacing(Style::WidgetSpacing); { + xAxis = new QPushButton("X-axis"); + buttonLayout->addWidget(xAxis); + + yAxis = new QPushButton("Y-axis"); + buttonLayout->addWidget(yAxis); + } + layout->addLayout(buttonLayout); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(spacer); + + window->setLayout(layout); + connect(xAxis, SIGNAL(released()), this, SLOT(assignAxisX())); + connect(yAxis, SIGNAL(released()), this, SLOT(assignAxisY())); +} + +void InputMouseCaptureWindow::activate(InputMouseCaptureWindow::Mode mode_) { + window->hide(); + activeMode = mode_; + + if(activeMode == AxisMode) { + captureBox->hide(); + xAxis->show(); + yAxis->show(); + + info->setText(utf8() + << "To assign a mouse axis to this ID, please click the desired axis button below,
" + << "using the mouse that you want the axis to be assigned to." + ); + + activeMouse = -1; + } else /*(activeMode == ButtonMode) */ { + captureBox->show(); + xAxis->hide(); + yAxis->hide(); + + info->setText(utf8() + << "To assign a mouse button to this ID, please place the mouse you wish to map
" + << "over the capture box below, and then click any mouse button to set assignment." + ); + } + + utility.showCentered(window); +} + +//this is only called when isActiveWindow() == true +void InputMouseCaptureWindow::inputEvent(uint16_t code) { + InputCode::type_t type = InputCode::type(code); + int16_t state = inputManager.state(code); + + if(activeMode == AxisMode) { + //when pressing down mouse button (eg to select "X-axis" or "Y-axis"), + //record mouse index for later assignment + if(type == InputCode::MouseButton && state == true) { + activeMouse = InputCode::mouseNumber(code); + return; + } + } else if(activeMode == ButtonMode) { + //if this is a mouse button that is being released ... + if(type == InputCode::MouseButton && state == false) { + //ensure button was clicked inside active capture box + QRect windowRect = window->geometry(); + QRect widgetRect = captureBox->geometry(); + unsigned wx = windowRect.left() + widgetRect.left(); + unsigned wy = windowRect.top() + widgetRect.top(); + unsigned px = QCursor::pos().x(); + unsigned py = QCursor::pos().y(); + + if(px < wx || px >= wx + widgetRect.size().width() ) return; + if(py < wy || py >= wy + widgetRect.size().height()) return; + + winInputCapture->inputEvent(code, true); + return; + } + } +} + +void InputMouseCaptureWindow::assignAxisX() { + if(activeMouse >= 0) { + winInputCapture->inputEvent(mouse<>::index(activeMouse, mouse<>::x), true); + } +} + +void InputMouseCaptureWindow::assignAxisY() { + if(activeMouse >= 0) { + winInputCapture->inputEvent(mouse<>::index(activeMouse, mouse<>::y), true); + } +} + +//==================== +//InputCalibrateWindow +//==================== + +//background: +//=========== +//HID devices work by sending device state *changes* only. what this means is that when an application is started, +//it does not know the current state of said device. the keyboard and mouse are exceptions, as the OS globally +//tracks their states. but this does apply to joypads. once a button is pressed, or an axis is moved, the entire +//joypad state will be sent in a message, that APIs such as DirectInput and SDL will buffer. +// +//to complicate matters, recent joypads have added pressure-sensitive buttons (triggers), in addition to the +//existing analog sticks. but this functionality was not extended to the USB HID state or to various platform +//input APIs. instead, they are treated exactly like axes. +// +//however, an application needs to treat these two input types distinctly: +//a stick is a bi-directional input. the stick starts off centered, and can be moved left or right, or up or down. +//a trigger is a uni-directional input. it can only be pushed down. +// +//a stick's default, unpressed state is centered (0), whereas a trigger's default state is fully depressed (+32767.) +//but because the default state is not available until the user presses a button on a joypad, it is not possible to +//calibrate a joypad on startup. all axes will report a value of 0, even if buttons are depressed. +// +//thusly, this class is needed. it will spawn a window upon the first attempt to map a joypad axis after startup. +//by the point this window appears, an axis must have been moved, so the joypad state is now valid. but since it's +//not possible to tell which button was pressed or which axis was moved, it's possible the axis that we're trying to +//map was moved. so querying its state now might result in improper mapping. so instead, this window is shown, and +//the user is asked not to press any buttons or move any axes. after hitting okay to confirm the joypad is idle, +//the joypad can finally be calibrated properly. +// +//upon assignment, the calibration data is appended to the input assignment value (eg "joypad00.axis00::trigger"), +//so that calibration is not necessary every time the emulator is run -- only when modifying input mapping on an axis. + +void InputCalibrationWindow::activate(unsigned joy) { + //do not override an already active calibration window + if(window->isVisible()) return; + + activeJoypad = joy; + info->setText(utf8() + << "Joypad #" << joy << " needs to be calibrated before it can be mapped. " + << "Please ensure that
no buttons are pressed, " + << "and all axes are centered before pressing okay." + ); + + utility.showCentered(window); + ok->setFocus(); +} + +void InputCalibrationWindow::setup() { + window = new Window; + window->setObjectName("input-calibrate-window"); + window->setWindowTitle("Joypad Calibration"); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(0); + + info = new QLabel; + layout->addWidget(info); + layout->addSpacing(Style::WidgetSpacing); + + ok = new QPushButton("Ok"); + layout->addWidget(ok); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(spacer); + + window->setLayout(layout); + connect(ok, SIGNAL(released()), this, SLOT(dismiss())); +} + +void InputCalibrationWindow::dismiss() { + window->hide(); + if(activeJoypad != -1) { + inputManager.calibrate(activeJoypad); + activeJoypad = -1; + } +} + +void InputCalibrationWindow::Window::closeEvent(QCloseEvent*) { + winInputCalibration->dismiss(); +} diff --git a/bsnes/ui_qt/settings/utility/inputcapture.hpp b/bsnes/ui_qt/settings/utility/inputcapture.hpp new file mode 100755 index 0000000..f197204 --- /dev/null +++ b/bsnes/ui_qt/settings/utility/inputcapture.hpp @@ -0,0 +1,88 @@ +class InputCaptureWindow : public QObject { + Q_OBJECT + +public: + struct Window : public QWidget { + void closeEvent(QCloseEvent*); + } *window; + QVBoxLayout *layout; + QHBoxLayout *hlayout; + QLabel *title; + QPushButton *mouseAxes; + QPushButton *mouseButtons; + QWidget *imageSpacer; + struct ImageWidget : public QWidget { + void paintEvent(QPaintEvent*); + } *imageWidget; + QWidget *spacer; + + void setup(); + void activate(InputObject *object); + void activate(InputGroup *group); + void inputEvent(uint16_t code, bool forceAssign = false); + InputCaptureWindow(); + +public slots: + void assignMouseAxis(); + void assignMouseButton(); + +private: + InputObject *activeObject; + InputGroup *activeGroup; + unsigned groupIndex; + bool inputLock; + + friend class InputCaptureWindow::Window; +} *winInputCapture; + +class InputMouseCaptureWindow : public QObject { + Q_OBJECT + +public: + enum Mode { AxisMode, ButtonMode }; + + QWidget *window; + QVBoxLayout *layout; + QLabel *info; + QLabel *captureBox; + QHBoxLayout *buttonLayout; + QPushButton *xAxis; + QPushButton *yAxis; + QWidget *spacer; + + void setup(); + void activate(Mode); + void inputEvent(uint16_t code); + +public slots: + void assignAxisX(); + void assignAxisY(); + +private: + Mode activeMode; + signed activeMouse; +} *winInputMouseCaptureWindow; + +class InputCalibrationWindow : public QObject { + Q_OBJECT + +public: + struct Window : public QWidget { + void closeEvent(QCloseEvent*); + } *window; + QVBoxLayout *layout; + QLabel *info; + QPushButton *ok; + QWidget *spacer; + + void setup(); + void activate(unsigned joy); + +public slots: + void dismiss(); + +private: + int activeJoypad; + + friend class InputCalibrationWindow::Window; +} *winInputCalibration; diff --git a/bsnes/ui_qt/settings/video.cpp b/bsnes/ui_qt/settings/video.cpp new file mode 100755 index 0000000..627e8eb --- /dev/null +++ b/bsnes/ui_qt/settings/video.cpp @@ -0,0 +1,119 @@ +void VideoSettingsWindow::setup() { + panel = new QWidget; + + layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + + title = new QLabel("Video Settings"); + title->setProperty("class", "title"); + layout->addWidget(title); + + sliders = new QGridLayout; { + lcontrast = new QLabel("Contrast adjust: +100%"); + sliders->addWidget(lcontrast, 0, 0); + + contrast = new QSlider(Qt::Horizontal); + contrast->setMinimum(-95); + contrast->setMaximum(+95); + sliders->addWidget(contrast, 0, 1); + + lbrightness = new QLabel("Brightness adjust: +100%"); + sliders->addWidget(lbrightness, 1, 0); + + brightness = new QSlider(Qt::Horizontal); + brightness->setMinimum(-95); + brightness->setMaximum(+95); + sliders->addWidget(brightness, 1, 1); + + lgamma = new QLabel("Gamma adjust: +100%"); + sliders->addWidget(lgamma, 2, 0); + + gamma = new QSlider(Qt::Horizontal); + gamma->setMinimum(-95); + gamma->setMaximum(+95); + sliders->addWidget(gamma, 2, 1); + } + sliders->setSpacing(Style::WidgetSpacing); + layout->addLayout(sliders); + layout->addSpacing(Style::WidgetSpacing); + + options = new QHBoxLayout; { + options->setMargin(0); + + enableGammaRamp = new QCheckBox("Simulate NTSC TV gamma ramp"); + enableGammaRamp->setToolTip("Lower monitor gamma to more accurately match a CRT television"); + options->addWidget(enableGammaRamp); + + enableNtscMergeFields = new QCheckBox("Merge scan fields for NTSC filter"); + enableNtscMergeFields->setToolTip( + "NTSC filter requires 60hz w/video sync to simulate alternating field effect.\n" + "If this is not the case, this option should be enabled to prevent excessive video shimmering." + ); + options->addWidget(enableNtscMergeFields); + } + options->setSpacing(Style::WidgetSpacing); + layout->addLayout(options); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(spacer); + + panel->setLayout(layout); + connect(contrast, SIGNAL(valueChanged(int)), this, SLOT(contrastAdjust(int))); + connect(brightness, SIGNAL(valueChanged(int)), this, SLOT(brightnessAdjust(int))); + connect(gamma, SIGNAL(valueChanged(int)), this, SLOT(gammaAdjust(int))); + connect(enableGammaRamp, SIGNAL(stateChanged(int)), this, SLOT(gammaRampToggle(int))); + connect(enableNtscMergeFields, SIGNAL(stateChanged(int)), this, SLOT(ntscFieldsToggle(int))); + + syncUi(); +} + +void VideoSettingsWindow::syncUi() { + int n; + + n = config.video.contrastAdjust; + lcontrast->setText(utf8() << "Contrast adjust: " << (n > 0 ? "+" : "") << n << "%"); + contrast->setSliderPosition(n); + + n = config.video.brightnessAdjust; + lbrightness->setText(utf8() << "Brightness adjust: " << (n > 0 ? "+" : "") << n << "%"); + brightness->setSliderPosition(n); + + n = config.video.gammaAdjust; + lgamma->setText(utf8() << "Gamma adjust: " << (n > 0 ? "+" : "") << n << "%"); + gamma->setSliderPosition(n); + + enableGammaRamp->setChecked(config.video.enableGammaRamp); + enableNtscMergeFields->setChecked(config.video.enableNtscMergeFields); +} + +void VideoSettingsWindow::gammaRampToggle(int state) { + config.video.enableGammaRamp = (state == Qt::Checked); + syncUi(); + utility.updateColorFilter(); +} + +void VideoSettingsWindow::ntscFieldsToggle(int state) { + config.video.enableNtscMergeFields = (state == Qt::Checked); + syncUi(); + utility.updateSoftwareFilter(); +} + +void VideoSettingsWindow::contrastAdjust(int value) { + config.video.contrastAdjust = value; + syncUi(); + utility.updateColorFilter(); +} + +void VideoSettingsWindow::brightnessAdjust(int value) { + config.video.brightnessAdjust = value; + syncUi(); + utility.updateColorFilter(); +} + +void VideoSettingsWindow::gammaAdjust(int value) { + config.video.gammaAdjust = value; + syncUi(); + utility.updateColorFilter(); +} diff --git a/bsnes/ui_qt/settings/video.hpp b/bsnes/ui_qt/settings/video.hpp new file mode 100755 index 0000000..7c5f3ce --- /dev/null +++ b/bsnes/ui_qt/settings/video.hpp @@ -0,0 +1,29 @@ +class VideoSettingsWindow : public QObject { + Q_OBJECT + +public: + QWidget *panel; + QVBoxLayout *layout; + QLabel *title; + QGridLayout *sliders; + QLabel *lcontrast; + QSlider *contrast; + QLabel *lbrightness; + QSlider *brightness; + QLabel *lgamma; + QSlider *gamma; + QHBoxLayout *options; + QCheckBox *enableGammaRamp; + QCheckBox *enableNtscMergeFields; + QWidget *spacer; + + void setup(); + void syncUi(); + +public slots: + void gammaRampToggle(int); + void ntscFieldsToggle(int); + void contrastAdjust(int); + void brightnessAdjust(int); + void gammaAdjust(int); +} *winVideoSettings; diff --git a/bsnes/ui_qt/ui.cpp b/bsnes/ui_qt/ui.cpp new file mode 100755 index 0000000..9c9a232 --- /dev/null +++ b/bsnes/ui_qt/ui.cpp @@ -0,0 +1,122 @@ +#include "base/main.moc" +#include "base/loader.moc" +#include "base/htmlviewer.moc" +#include "base/about.moc" +#include "settings/settings.moc" + +#include "base/main.cpp" +#include "base/loader.cpp" +#include "base/htmlviewer.cpp" +#include "base/about.cpp" +#include "settings/settings.cpp" + +void Application::init() { + if(config.system.crashedOnLastRun == true) { + //emulator crashed on last run, disable all drivers + QMessageBox::warning(0, "bsnes Crash Notification", utf8() << + "

Warning:
bsnes crashed while attempting to initialize device " + "drivers the last time it was run.

" + "

To prevent this from occurring again, all drivers have been disabled. Please " + "go to Settings->Configuration->Advanced and choose new driver settings, and then " + "restart the emulator for the changes to take effect. Video, audio and input " + "will not work until you do this!

" + "

Settings that caused failure on last run:
" + << "Video driver: " << config.system.video << "
" + << "Audio driver: " << config.system.audio << "
" + << "Input driver: " << config.system.input << "

" + ); + + config.system.video = "None"; + config.system.audio = "None"; + config.system.input = "None"; + } + + if(config.system.video == "") config.system.video = video.default_driver(); + if(config.system.audio == "") config.system.audio = audio.default_driver(); + if(config.system.input == "") config.system.input = input.default_driver(); + + winMain = new MainWindow; + winMain->setup(); + + winLoader = new LoaderWindow; + winLoader->setup(); + + winHtmlViewer = new HtmlViewerWindow; + winHtmlViewer->setup(); + + winAbout = new AboutWindow; + winAbout->setup(); + + //window must be onscreen and visible before initializing video interface + utility.updateSystemState(); + utility.resizeMainWindow(); + utility.updateFullscreenState(); + application.processEvents(); + + winSettings = new SettingsWindow; + winSettings->setup(); + + //if emulator crashes while initializing drivers, next run will disable them all. + //this will allow user to choose different driver settings. + config.system.crashedOnLastRun = true; + config.save(configFilename); + + video.driver(config.system.video); + video.set(Video::Handle, (uintptr_t)winMain->canvas->winId()); + if(video.init() == false) { + QMessageBox::warning(0, "bsnes", utf8() << + "

Warning: " << config.system.video << " video driver failed to initialize. " + "Video driver has been disabled.

" + "

Please go to Settings->Configuration->Advanced and choose a different driver, and " + "then restart the emulator for the changes to take effect.

" + ); + video.driver("None"); + video.init(); + } + + audio.driver(config.system.audio); + audio.set(Audio::Handle, (uintptr_t)winMain->canvas->winId()); + audio.set(Audio::Frequency, config.audio.outputFrequency); + audio.set(Audio::Latency, config.audio.latency); + audio.set(Audio::Volume, config.audio.volume); + if(audio.init() == false) { + QMessageBox::warning(0, "bsnes", utf8() << + "

Warning: " << config.system.audio << " audio driver failed to initialize. " + "Audio driver has been disabled.

" + "

Please go to Settings->Configuration->Advanced and choose a different driver, and " + "then restart the emulator for the changes to take effect.

" + ); + audio.driver("None"); + audio.init(); + } + + input.driver(config.system.input); + input.set(Input::Handle, (uintptr_t)winMain->canvas->winId()); + if(input.init() == false) { + QMessageBox::warning(0, "bsnes", utf8() << + "

Warning: " << config.system.input << " input driver failed to initialize. " + "Input driver has been disabled.

" + "

Please go to Settings->Configuration->Advanced and choose a different driver, and " + "then restart the emulator for the changes to take effect.

" + ); + input.driver("None"); + input.init(); + } + + //didn't crash, note this in the config file now in case a different kind of crash occurs later + config.system.crashedOnLastRun = false; + config.save(configFilename); + + inputManager.bind(); + inputManager.refresh(); + inputManager.refresh(); + inputManager.onInput = bind(&Utility::inputEvent, &utility); + + utility.updateAvSync(); + utility.updateVideoMode(); + utility.updateColorFilter(); + utility.updateHardwareFilter(); + utility.updateSoftwareFilter(); + utility.updateEmulationSpeed(); + utility.updateControllers(); +} diff --git a/bsnes/ui_qt/utility/cartridge.cpp b/bsnes/ui_qt/utility/cartridge.cpp new file mode 100755 index 0000000..294fcd4 --- /dev/null +++ b/bsnes/ui_qt/utility/cartridge.cpp @@ -0,0 +1,163 @@ +string Utility::selectCartridge() { + audio.clear(); + QString filename = QFileDialog::getOpenFileName(0, + "Load Cartridge", + utf8() << (snes.config.path.rom != "" ? snes.config.path.rom : snes.config.path.current), + "SNES images (*.smc *.sfc *.swc *.fig *.bs *.st" + #if defined(GZIP_SUPPORT) + " *.zip *.gz" + #endif + #if defined(JMA_SUPPORT) + " *.jma" + #endif + ");;" + "All files (*)" + ); + return string() << filename.toUtf8().constData(); +} + +string Utility::selectFolder(const char *title) { + audio.clear(); + QString pathname = QFileDialog::getExistingDirectory(0, + title, utf8() << snes.config.path.current, + QFileDialog::ShowDirsOnly); + return string() << pathname.toUtf8().constData(); +} + +void Utility::loadCartridge(const char *filename) { + switch(cartridge.detect_image_type(filename)) { + case Cartridge::TypeNormal: loadCartridgeNormal(filename); break; + case Cartridge::TypeBsxSlotted: winLoader->loadBsxSlottedCartridge(filename, ""); break; + case Cartridge::TypeBsxBios: winLoader->loadBsxCartridge(filename, ""); break; + case Cartridge::TypeBsx: winLoader->loadBsxCartridge(snes.config.path.bsx, filename); break; + case Cartridge::TypeSufamiTurboBios: winLoader->loadSufamiTurboCartridge(filename, "", ""); break; + case Cartridge::TypeSufamiTurbo: winLoader->loadSufamiTurboCartridge(snes.config.path.st, filename, ""); break; + } +} + +bool Utility::loadCartridgeNormal(const char *base) { + if(!*base) return false; + unloadCartridge(); + cartridge.load_normal(base); + modifySystemState(LoadCartridge); + return true; +} + +bool Utility::loadCartridgeBsxSlotted(const char *base, const char *slot) { + if(!*base) return false; + unloadCartridge(); + cartridge.load_bsx_slotted(base, slot); + modifySystemState(LoadCartridge); + return true; +} + +bool Utility::loadCartridgeBsx(const char *base, const char *slot) { + if(!*base) return false; + unloadCartridge(); + cartridge.load_bsx(base, slot); + modifySystemState(LoadCartridge); + return true; +} + +bool Utility::loadCartridgeSufamiTurbo(const char *base, const char *slotA, const char *slotB) { + if(!*base) return false; + unloadCartridge(); + cartridge.load_sufami_turbo(base, slotA, slotB); + modifySystemState(LoadCartridge); + return true; +} + +void Utility::unloadCartridge() { + if(cartridge.loaded()) { + cartridge.unload(); + modifySystemState(UnloadCartridge); + } +} + +void Utility::modifySystemState(system_state_t state) { + video.clear(); + audio.clear(); + + switch(state) { + case LoadCartridge: { + //must call cartridge.load_cart_...() before calling modifySystemState(LoadCartridge) + if(cartridge.loaded() == false) break; + + application.power = true; + application.pause = false; + snes.power(); + + //warn if unsupported hardware detected + string chip; + if(cartridge.has_superfx()) chip = "SuperFX"; + else if(cartridge.has_sa1()) chip = "SA-1"; + else if(cartridge.has_st011()) chip = "ST011"; + else if(cartridge.has_st018()) chip = "ST018"; + else if(cartridge.has_dsp3()) chip = "DSP-3"; + if(chip != "") { + QMessageBox::warning(winMain->window, "Warning", utf8() + << "

Warning:
Unsupported " << chip << " chip detected. " + << "It is unlikely that this title will work properly.

"); + } + + showMessage(utf8() + << "Loaded " << cartridge.name() + << (cartridge.patched() ? ", and applied UPS patch." : ".")); + winMain->window->setWindowTitle(utf8() << BSNES_TITLE << " - " << cartridge.name()); + } break; + + case UnloadCartridge: { + if(cartridge.loaded() == false) break; //no cart to unload? + cartridge.unload(); + + application.power = false; + application.pause = true; + + showMessage(utf8() << "Unloaded " << cartridge.name() << "."); + winMain->window->setWindowTitle(utf8() << BSNES_TITLE); + } break; + + case PowerOn: { + if(cartridge.loaded() == false || application.power == true) break; + + application.power = true; + application.pause = false; + snes.power(); + + showMessage("Power on."); + } break; + + case PowerOff: { + if(cartridge.loaded() == false || application.power == false) break; + + application.power = false; + application.pause = true; + + showMessage("Power off."); + } break; + + case PowerCycle: { + if(cartridge.loaded() == false) break; + + application.power = true; + application.pause = false; + snes.power(); + + showMessage("System power was cycled."); + } break; + + case Reset: { + if(cartridge.loaded() == false || application.power == false) break; + + application.pause = false; + snes.reset(); + + showMessage("System was reset."); + } break; + } + + winMain->syncUi(); + winCodeEditor->dismiss(); + winCheatEditor->reloadList(); + winCheatEditor->syncUi(); +} diff --git a/bsnes/ui_qt/utility/utility.cpp b/bsnes/ui_qt/utility/utility.cpp new file mode 100755 index 0000000..945f549 --- /dev/null +++ b/bsnes/ui_qt/utility/utility.cpp @@ -0,0 +1,218 @@ +#include "cartridge.cpp" +#include "window.cpp" + +//returns true if requested code is a button, and it has just been pressed down +bool Utility::isButtonDown(uint16_t inputCode, InputObject &object) { + if(inputCode != object.code) return false; + + if(object.codetype != InputCode::KeyboardButton + && object.codetype != InputCode::MouseButton + && object.codetype != InputCode::JoypadHat + && object.codetype != InputCode::JoypadAxis + && object.codetype != InputCode::JoypadButton) return false; + + int16_t state = inputManager.state(object.code); + int16_t lastState = inputManager.lastState(object.code); + + if(object.codetype == InputCode::JoypadHat) { + switch(object.modifier) { + case InputObject::Up: return (state & joypad<>::hat_up ) && !(lastState & joypad<>::hat_up ); + case InputObject::Down: return (state & joypad<>::hat_down ) && !(lastState & joypad<>::hat_down ); + case InputObject::Left: return (state & joypad<>::hat_left ) && !(lastState & joypad<>::hat_left ); + case InputObject::Right: return (state & joypad<>::hat_right) && !(lastState & joypad<>::hat_right); + } + } else if(object.codetype == InputCode::JoypadAxis) { + switch(object.modifier) { + case InputObject::Lo: return (state < -16384) && !(lastState < -16384); + case InputObject::Hi: return (state > +16384) && !(lastState > +16384); + case InputObject::Trigger: return (state < 0) && !(lastState < 0); + } + } else { + return (state == 1) && !(lastState == 1); + } + + return false; //fall-through for modifier-less hats / axes +} + +void Utility::inputEvent(uint16_t code) { + //forward key-press event + //(in case window is currently active and capturing a new button / axis assignment) + winInputCapture->inputEvent(code); + + //if escape key is pressed on *any* keyboard; release the mouse if it is acquired + for(unsigned i = 0; i < keyboard<>::count; i++) { + if(code == keyboard<>::index(i, keyboard<>::escape) && inputManager.state(code) && input.acquired()) { + input.unacquire(); + return; //do not trigger other UI actions that may be bound to escape key + } + } + + if(winMain->window->isActiveWindow()) { + bool resizeWindow = false; + + if(isButtonDown(code, inputUiGeneral.loadCartridge)) { + string filename = selectCartridge(); + if(filename.length() > 0) loadCartridge(filename); + } + + if(isButtonDown(code, inputUiGeneral.pauseEmulation)) { + application.pause = !application.pause; + } + + if(isButtonDown(code, inputUiGeneral.resetSystem)) { + modifySystemState(Reset); + } + + if(isButtonDown(code, inputUiGeneral.powerCycleSystem)) { + modifySystemState(PowerCycle); + } + + if(isButtonDown(code, inputUiGeneral.lowerSpeed)) { + config.system.speed--; + updateEmulationSpeed(); + winMain->syncUi(); + } + + if(isButtonDown(code, inputUiGeneral.raiseSpeed)) { + config.system.speed++; + updateEmulationSpeed(); + winMain->syncUi(); + } + + if(isButtonDown(code, inputUiGeneral.toggleCheatSystem)) { + if(cheat.enabled() == false) { + cheat.enable(); + showMessage("Cheat system enabled."); + } else { + cheat.disable(); + showMessage("Cheat system disabled."); + } + } + + if(isButtonDown(code, inputUiGeneral.toggleFullscreen)) { + config.video.isFullscreen = !config.video.isFullscreen; + updateFullscreenState(); + winMain->syncUi(); + } + + if(isButtonDown(code, inputUiGeneral.toggleMenu)) { + winMain->window->menuBar()->setVisible(!winMain->window->menuBar()->isVisibleTo(winMain->window)); + resizeWindow = true; + } + + if(isButtonDown(code, inputUiGeneral.toggleStatus)) { + winMain->window->statusBar()->setVisible(!winMain->window->statusBar()->isVisibleTo(winMain->window)); + resizeWindow = true; + } + + //prevent calling twice when toggleMenu == toggleStatus + if(resizeWindow == true) { + resizeMainWindow(); + } + + if(isButtonDown(code, inputUiGeneral.exitEmulator)) { + application.terminate = true; + } + } +} + +//display message in main window statusbar area for three seconds +void Utility::showMessage(const char *message) { + winMain->window->statusBar()->showMessage(utf8() << message, 3000); +} + +//updates system state text at bottom-right of main window statusbar +void Utility::updateSystemState() { + string text; + + if(cartridge.loaded() == false) { + text = "No cartridge loaded"; + } else if(application.power == false) { + text = "Power off"; + } else if(application.pause == true || application.autopause == true) { + text = "Paused"; + } else if(ppu.status.frames_updated == true) { + ppu.status.frames_updated = false; + text << (int)ppu.status.frames_executed; + text << " fps"; + } else { + //nothing to update + return; + } + + winMain->systemState->setText(utf8() << text); +} + +void Utility::acquireMouse() { + if(cartridge.loaded()) { + if(snes.config.controller_port1 == SNES::Input::DeviceMouse + || snes.config.controller_port2 == SNES::Input::DeviceMouse + || snes.config.controller_port2 == SNES::Input::DeviceSuperScope + || snes.config.controller_port2 == SNES::Input::DeviceJustifier + || snes.config.controller_port2 == SNES::Input::DeviceJustifiers + ) input.acquire(); + } +} + +void Utility::unacquireMouse() { + input.unacquire(); +} + +void Utility::updateAvSync() { + video.set(Video::Synchronize, config.video.synchronize); + audio.set(Audio::Synchronize, config.audio.synchronize); +} + +void Utility::updateVideoMode() { + if(config.video.context->region == 0) { + snes.video.set_mode(SNES::Video::ModeNTSC); + } else { + snes.video.set_mode(SNES::Video::ModePAL); + } +} + +void Utility::updateColorFilter() { + libfilter::colortable.set_format(libfilter::Colortable::RGB888); + libfilter::colortable.set_contrast(config.video.contrastAdjust); + libfilter::colortable.set_brightness(config.video.brightnessAdjust); + libfilter::colortable.set_gamma(100 + config.video.gammaAdjust); + libfilter::colortable.enable_gamma_ramp(config.video.enableGammaRamp); + libfilter::colortable.update(); +} + +void Utility::updateHardwareFilter() { + video.set(Video::Filter, config.video.context->hwFilter); +} + +void Utility::updateSoftwareFilter() { + libfilter::FilterInterface::FilterType type; + switch(config.video.context->swFilter) { default: + case 0: type = libfilter::FilterInterface::Direct; break; + case 1: type = libfilter::FilterInterface::Scanline; break; + case 2: type = libfilter::FilterInterface::Scale2x; break; + case 3: type = libfilter::FilterInterface::HQ2x; break; + case 4: type = libfilter::FilterInterface::NTSC; break; + } + libfilter::filter.set(type); + + if(type == libfilter::FilterInterface::NTSC) { + libfilter::filter_ntsc.adjust(0, 0, 0, 0, 0, config.video.enableNtscMergeFields); + } +} + +void Utility::updateEmulationSpeed() { + config.system.speed = max(0, min(4, (signed)config.system.speed)); + + double scale[] = { 0.50, 0.75, 1.00, 1.50, 2.00 }; + unsigned outfreq = config.audio.outputFrequency; + unsigned infreq = config.audio.inputFrequency * scale[config.system.speed] + 0.5; + + audio.set(Audio::Resample, outfreq != infreq); //only resample when necessary + audio.set(Audio::ResampleOutputFrequency, outfreq); + audio.set(Audio::ResampleInputFrequency, infreq); +} + +void Utility::updateControllers() { + snes.input.port_set_device(0, snes.config.controller_port1); + snes.input.port_set_device(1, snes.config.controller_port2); +} diff --git a/bsnes/ui_qt/utility/utility.hpp b/bsnes/ui_qt/utility/utility.hpp new file mode 100755 index 0000000..afbaf57 --- /dev/null +++ b/bsnes/ui_qt/utility/utility.hpp @@ -0,0 +1,37 @@ +class Utility { +public: + //utility.cpp + bool isButtonDown(uint16_t inputCode, InputObject &object); + void inputEvent(uint16_t code); + void showMessage(const char *message); + void updateSystemState(); + void acquireMouse(); + void unacquireMouse(); + + void updateAvSync(); + void updateVideoMode(); + void updateColorFilter(); + void updateHardwareFilter(); + void updateSoftwareFilter(); + void updateEmulationSpeed(); + void updateControllers(); + + //cartridge.cpp + string selectCartridge(); + string selectFolder(const char *title); + void loadCartridge(const char*); + bool loadCartridgeNormal(const char*); + bool loadCartridgeBsxSlotted(const char*, const char*); + bool loadCartridgeBsx(const char*, const char*); + bool loadCartridgeSufamiTurbo(const char*, const char *, const char*); + void unloadCartridge(); + + enum system_state_t { LoadCartridge, UnloadCartridge, PowerOn, PowerOff, PowerCycle, Reset }; + void modifySystemState(system_state_t state); + + //window.cpp + void showCentered(QWidget *window); + void updateFullscreenState(); + void constrainSize(unsigned &x, unsigned &y, unsigned max); + void resizeMainWindow(); +} utility; diff --git a/bsnes/ui_qt/utility/window.cpp b/bsnes/ui_qt/utility/window.cpp new file mode 100755 index 0000000..cb8b144 --- /dev/null +++ b/bsnes/ui_qt/utility/window.cpp @@ -0,0 +1,123 @@ +//show specified window in the center of the screen. +void Utility::showCentered(QWidget *window) { + QRect deskRect = QApplication::desktop()->availableGeometry(window); + + //place window offscreen, so that it can be shown to get proper frameSize() + window->move(std::numeric_limits::max(), std::numeric_limits::max()); + window->showNormal(); + + //force-resize window to be as small as minimumSize() will allow, and then center it + window->resize(0, 0); + window->move( + deskRect.center().x() - (window->frameSize().width() / 2), + deskRect.center().y() - (window->frameSize().height() / 2) + ); + + #ifndef _WIN32 + //Xlib frame size (excluding inside) is QSize(0, 0) at first, wait for it to be valid + for(unsigned counter = 0; counter < 4096; counter++) { + if(window->frameSize() != window->size()) break; + application.processEvents(); + } + #endif + + //in case frame size changed, recenter window + window->move( + deskRect.center().x() - (window->frameSize().width() / 2), + deskRect.center().y() - (window->frameSize().height() / 2) + ); + + //ensure window has focus + application.processEvents(); + window->activateWindow(); + window->raise(); +} + +void Utility::updateFullscreenState() { + video.clear(); + + if(config.video.isFullscreen == false) { + config.video.context = &config.video.windowed; + winMain->window->showNormal(); + } else { + config.video.context = &config.video.fullscreen; + winMain->window->showFullScreen(); + } + + //Xlib requires time to propogate fullscreen state change message to window manager; + //if window is resized before this occurs, canvas may not resize correctly + application.processEvents(); + usleep(50000); + + //refresh options that are unique to each video context + resizeMainWindow(); + updateVideoMode(); + updateHardwareFilter(); + updateSoftwareFilter(); + winMain->syncUi(); +} + +//if max exceeds x: x is set to max, and y is scaled down to keep proportion to x +void Utility::constrainSize(unsigned &x, unsigned &y, unsigned max) { + if(x > max) { + double scalar = (double)max / (double)x; + y = (unsigned)((double)y * (double)scalar); + x = max; + } +} + +void Utility::resizeMainWindow() { + for(unsigned i = 0; i < 8; i++) { + unsigned multiplier = config.video.context->multiplier; + unsigned width = 256 * config.video.context->multiplier; + unsigned height = (config.video.context->region == 0 ? 224 : 239) * config.video.context->multiplier; + + if(config.video.context->correctAspectRatio) { + if(config.video.context->region == 0) { + width = (double)width * config.video.ntscAspectRatio + 0.5; //NTSC adjust + } else { + width = (double)width * config.video.palAspectRatio + 0.5; //PAL adjust + } + } + + if(config.video.isFullscreen == false) { + //get effective desktop work area region (ignore Windows taskbar, OS X doc, etc.) + QRect deskRect = QApplication::desktop()->availableGeometry(winMain->window); + + //calculate frame geometry (window border + menubar + statusbar) + unsigned frameWidth = winMain->window->frameSize().width() - winMain->canvasContainer->size().width(); + unsigned frameHeight = winMain->window->frameSize().height() - winMain->canvasContainer->size().height(); + + //ensure window size will not be larger than viewable desktop area + constrainSize(height, width, deskRect.height() - frameHeight); + constrainSize(width, height, deskRect.width() - frameWidth ); + + //resize window such that it is as small as possible to hold canvas of size (width, height) + winMain->canvas->setFixedSize(width, height); + winMain->window->resize(width, height); + + //center window onscreen + winMain->window->move( + deskRect.center().x() - (winMain->window->frameSize().width() / 2), + deskRect.center().y() - (winMain->window->frameSize().height() / 2) + ); + } else { + constrainSize(height, width, winMain->canvasContainer->size().height()); + constrainSize(width, height, winMain->canvasContainer->size().width()); + + //center canvas onscreen; ensure it is not larger than viewable area + winMain->canvas->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + winMain->canvas->setMaximumSize(width, height); + } + + application.processEvents(); + usleep(2000); + } + + //work around for Qt/Xlib bug: + //if window resize occurs with cursor over it, Qt shows Qt::Size*DiagCursor; + //so force it to show Qt::ArrowCursor, as expected + winMain->window->setCursor(Qt::ArrowCursor); + winMain->canvasContainer->setCursor(Qt::ArrowCursor); + winMain->canvas->setCursor(Qt::ArrowCursor); +} diff --git a/project.tmproj b/project.tmproj new file mode 100644 index 0000000..2da4c15 --- /dev/null +++ b/project.tmproj @@ -0,0 +1,307 @@ + + + + + currentDocument + zsnes/docs/srcinfo.txt + documents + + + expanded + + name + snesram + regexFolderFilter + !.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$ + sourceDirectory + + + + fileHierarchyDrawerWidth + 200 + metaData + + zsnes/docs/README.LINUX + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 0 + + zsnes/docs/README.SVN + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 0 + + zsnes/docs/authors.txt + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 0 + + zsnes/docs/install.txt + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 132 + + zsnes/docs/license.txt + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 0 + + zsnes/docs/opengl.txt + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 0 + + zsnes/docs/srcinfo.txt + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 119 + + zsnes/src/chips/dsp4emu.c + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 113 + + zsnes/src/chips/fxtable.asm + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 0 + + zsnes/src/chips/seta10.c + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 453 + + zsnes/src/cpu/dsp.asm + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 32 + + zsnes/src/cpu/execute.asm + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 246 + + zsnes/src/macros.mac + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 0 + + zsnes/src/mmlib/osx.c + + caret + + column + 0 + line + 0 + + firstVisibleColumn + 0 + firstVisibleLine + 1639 + + + openDocuments + + zsnes/src/macros.mac + zsnes/src/cpu/dsp.asm + zsnes/src/cpu/execute.asm + zsnes/src/mmlib/osx.c + zsnes/src/chips/fxtable.asm + zsnes/src/chips/seta10.c + zsnes/src/chips/dsp4emu.c + zsnes/docs/authors.txt + zsnes/docs/install.txt + zsnes/docs/license.txt + zsnes/docs/opengl.txt + zsnes/docs/README.LINUX + zsnes/docs/README.SVN + zsnes/docs/srcinfo.txt + + showFileHierarchyDrawer + + showFileHierarchyPanel + + treeState + + snesram + + isExpanded + + subItems + + pyusb + + isExpanded + + subItems + + + zsnes + + isExpanded + + subItems + + docs + + isExpanded + + subItems + + + src + + isExpanded + + subItems + + cpu + + isExpanded + + subItems + + + mmlib + + isExpanded + + subItems + + + tools + + isExpanded + + subItems + + + + + + + + + + windowFrame + {{2, 60}, {738, 818}} + + diff --git a/pyusb/PKG-INFO b/pyusb/PKG-INFO new file mode 100644 index 0000000..eef9352 --- /dev/null +++ b/pyusb/PKG-INFO @@ -0,0 +1,14 @@ +Metadata-Version: 1.0 +Name: pyusb +Version: 0.4.1 +Summary: USB access extension module +Home-page: http://pyusb.berlios.de +Author: Wander Lairson Costa +Author-email: wander.lairson@gmail.com +License: BSD +Description: + PyUSB provides easy USB access to python. + The module contains classes and methods to + support the most USB operations. + +Platform: UNKNOWN diff --git a/pyusb/README b/pyusb/README new file mode 100644 index 0000000..ee85b8f --- /dev/null +++ b/pyusb/README @@ -0,0 +1,144 @@ +PyUSB - USB Access from Python +============================== + +The PyUSB module provides Python with easy access to the host +machine's Universal Serial Bus (USB) system. Although not yet +complete, PyUSB does provide a core set of functionality around which +useful applications can be developed. + +As with most Python modules, PyUSB's documentation is based on Python +doc strings and can therefore be manipulated by tools such as pydoc. + +PyUSB was developed and tested on the Slackware GNU/Linux +distribution and Windows XP Professional. Some testing has been done on Ubuntu, +Mac OS X and FreeBSD. + +If you have any question about PyUSB, you can contact the author at +wander (dot) lairson (at) gmail (dot) com. + +Installing PyUSB on GNU/Linux Systems +===================================== + +These instructions are for Debian-based systems. Instructions for +other flavors of GNU/Linux should be similar. + +You will first need to install the following packages: + +1) python (PyUSB is useless without it) +2) gcc (the compiler, linker, etc.) +3) python-dev (includes header files needed to compile PyUSB) +4) libusb-dev (C library upon which PyUSB is based) + +For example, the command + +sudo apt-get install python gcc python-dev libusb-dev + +should install all these packages on most Debian-based systems with +access to the proper package repositories. + +Once the above packages are installed, you can build and install PyUSB +with the command + +python setup.py install + +run as root from within the same directory as this README file. + +Installing PyUSB on Windows +=========================== + +These instructions are for installing PyUSB in a Cygwin environment. +See the end of this section for notes on installing in a Visual C++ +environment. + +You will first need to install the following software: + +1) Cygwin: A Linux-like environment for Windows available from +http://www.cygwin.com. Be sure to include GCC and Python with the +Cygwin installation. + +2) libusb-win32: a Windows version of the libusb C library available +from http://libusb-win32.sourceforge.net. + +From within a Cygwin terminal, copy the libusb.a file from the +libusb-win32 lib/ directory to $(CYGWINDIR)/usr/lib/, and copy the +usb.h file from the libusb-win32 include/ directory to +$(CYGWINDIR)/usr/include/. You can build and install PyUSB with the +command + +python setup.py install + +run from within the same directory as this README file. + +To build PyUSB using Visual C++, use Python's distutils tool, which +might request that .NET SDK Framework be installed first. +Alternatively, you can use the Visual C++ .NET 2005 project solution +included in the same directory as this README file. In this case, you +will need to manually change the project settings to include the +Python and libusb-win32 headers and libraries in the Visual C++ path. + +USAGE +===== + +This Python program is an example of how PyUSB can be used. + +# +# Open a device, look at the alternate interfaces, use the device, and +# then close the connection. +# + +import usb # import the usb module + +bus = usb.busses() # get a list of all available busses + +dev = bus[0].devices[0] # choose the first device on the first bus + +handle = dev.open() # open the device + +for alt in dev.configurations[0].interfaces[0]: + print alt # look at the alternate settings. + +handle.setConfiguration(0) # choose the first configuration + +handle.claimInterface(0) # choose the first interface + +### Use the device here. ### + +handle.releaseInterface() # also called automatically on __del__ + + +TODO/ROADMAP +============ + +- more tests +- more samples +- better documentation +- utility functions to find devices + + +ACKNOWLEDGEMENTS +================ + +Thanks to the following people for their help and contributions to the +PyUSB project. Where possible, there contributions are noted in the +source code. + +- Damian Staniforth +- Brenno Diegoli +- Israel Florentino +- Xiaofan Chen +- Mario Olimpio de Menezes +- Renato Manzan +- Ray Schumacher +- Mark Rages +- James Barabas +- Andrew Rogers +- Juha Torkkel +- Josh Lifton +- David Merrill +- Srikanth Jandhyala +- Alexander Krause +- Yael Maguire +- Lucio Torre +- Simeon Miteff + +PS: this great README file was written by Josh Lifton... :-) diff --git a/pyusb/license.txt b/pyusb/license.txt new file mode 100644 index 0000000..abeb1c7 --- /dev/null +++ b/pyusb/license.txt @@ -0,0 +1,28 @@ +Copyright (C) 2005 - 2007 Wander Lairson Costa + +The following terms apply to all files associated +with the software unless explicitly disclaimed in individual files. + +The authors hereby grant permission to use, copy, modify, distribute, +and license this software and its documentation for any purpose, provided +that existing copyright notices are retained in all copies and that this +notice is included verbatim in any distributions. No written agreement, +license, or royalty fee is required for any of the authorized uses. +Modifications to this software may be copyrighted by their authors +and need not follow the licensing terms described here, provided that +the new terms are clearly indicated on the first page of each file where +they apply. + +IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +MODIFICATIONS. + diff --git a/pyusb/pyusb.c b/pyusb/pyusb.c new file mode 100644 index 0000000..794e5ec --- /dev/null +++ b/pyusb/pyusb.c @@ -0,0 +1,2149 @@ +/* + * PyUSB - Python module for USB Access + * + * Copyright 2005 - 2007 Wander Lairson Costa + */ + +#if _MSC_VER >= 1400 /* Visual C++ 8.00 */ +#define _CRT_SECURE_NO_DEPRECATE +#endif /* _MSC_VER */ + +#include "pyusb.h" +#include +#include +#define DEFAULT_TIMEOUT 100 + +/* + * Necessary to compile successfully in python 2.3 + * Thanks to Mark Rages for the patch + */ +#ifndef Py_RETURN_NONE +#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None +#endif + +// PYUSB_STATIC char cvsid[] = "$Id: pyusb.c,v 1.24 2007/05/15 21:20:55 wander Exp $"; + +/* + * USBError + */ +PYUSB_STATIC PyObject *PyExc_USBError; + +#define PyUSB_Error() PyErr_SetString(PyExc_USBError, usb_strerror()) + +#define SUPPORT_NUMBER_PROTOCOL(_Arg) \ + (PyNumber_Check(_Arg) || PyString_Check(_Arg) || PyUnicode_Check(_Arg)) + +#if !defined(NDEBUG) +#define DUMP_PARAMS 1 +#else +#define DUMP_PARAMS 0 +#endif /* NDEBUG */ + +#ifndef NDEBUG + +/* + * Print a buffer of data + */ +PYUSB_STATIC void printBuffer( + const char *buffer, + int size + ) +{ + int i; + + if (!buffer) { + fputs("NULL\n", stderr); + return; + } + + for (i = 0; i < size; ++i) { + fprintf(stderr, "%2x ", buffer[i]); + if (i && !(i % 20)) fputc('\n', stderr); + } + + fputc('\n', stderr); +} + +#endif /* NDEBUG */ + +/* + * Converts a object with number procotol to int type + */ +PYUSB_STATIC int py_NumberAsInt( + PyObject *obj + ) +{ + PyObject *number = PyNumber_Int(obj); + if (number) { + int ret = PyInt_AS_LONG(number); + Py_DECREF(number); + return ret; + } else { + return 0; + } +} + +/* + * Gets a byte from a PyObject + * If the obj is a number, returns the number casted to a byte + * If it is a sequence, returns the first byte representation of the sequence + * If it is a mapping, returns the first byte representation of the first obj.values() + */ +PYUSB_STATIC u_int8_t getByte( + PyObject *obj + ) +{ + u_int8_t byte; + + if (PyNumber_Check(obj)) { + byte = (u_int8_t) py_NumberAsInt(obj); + } else if (PyString_Check(obj) || PyUnicode_Check(obj)) { + return (u_int8_t) PyString_AsString(obj)[0]; + } else if (PySequence_Check(obj)) { + PyObject *el0; + el0 = PySequence_GetItem(obj, 0); + if (!el0) return 0; + byte = getByte(el0); + Py_DECREF(el0); + } else if (PyMapping_Check(obj)) { + PyObject *vals; + vals = PyMapping_Values(obj); + if (!vals) return 0; + byte = getByte(vals); + Py_DECREF(vals); + } else { + byte = 0; + PyErr_BadArgument(); + } + + return byte; +} + +/* + * Gets a unsigned byte * representation of the obj + * If the obj is a sequence, returns the elements as a c byte array representation + * If it is a mapping, returns the c byte array representations of the obj.values() + */ +PYUSB_STATIC char *getBuffer( + PyObject *obj, + int *size + ) +{ + char *p = NULL; + + /* + * When the obj is a sequence type, we take the first byte from + * the each element of the sequence + */ + + /* ok, string is a sequence too, but let us optimize it */ + if (PyString_Check(obj) || PyUnicode_Check(obj)) { + char *tmp; + + if (-1 != PyString_AsStringAndSize(obj, &tmp, size)) { + p = (char *) PyMem_Malloc(*size); + if (p) memcpy(p, tmp, *size); + } + } else if (PySequence_Check(obj)) { + u_int32_t i, sz; + PyObject *el; + + sz = PySequence_Size(obj); + p = (char *) PyMem_Malloc(sz); + + for (i = 0; i < sz; ++i) { + el = PySequence_GetItem(obj, i); + p[i] = getByte(el); + Py_DECREF(el); + + if (!p[i] && PyErr_Occurred()) { + PyMem_Free(p); + return NULL; + } + } + + *size = sz; + } else if (PyMapping_Check(obj)) { + PyObject *values; + + values = PyMapping_Values(obj); + if (!values) return NULL; + + p = getBuffer(values, size); + Py_DECREF(values); + } else if (obj == Py_None) { + *size = 0; + return NULL; + } else { + PyErr_BadArgument(); + return NULL; + } + + return p; +} + +/* + * Build a numeric tuple from the buffer values + */ +PYUSB_STATIC PyObject *buildTuple( + char *buffer, + int size + ) +{ + PyObject *ret; + int i; + + ret = PyTuple_New(size); + + if (ret) { + for (i = 0; i < size; ++i) { + PyTuple_SET_ITEM(ret, i, PyInt_FromLong((u_int8_t) buffer[i])); + } + } + + return ret; +} + +/* + * Add a numeric constant to the dictionary + */ +PYUSB_STATIC void addConstant( + PyObject *dict, + const char *name, + long value) +{ + PyObject *val; + + val = PyInt_FromLong(value); + + if (val) { + PyDict_SetItemString(dict, name, val); + Py_DECREF(val); + } +} + +/* + * Add the module constants to the module dictionary + */ +PYUSB_STATIC void installModuleConstants( + PyObject *module + ) +{ + PyObject *dict; + + dict = PyModule_GetDict(module); + + addConstant(dict, "CLASS_PER_INTERFACE", USB_CLASS_PER_INTERFACE); + addConstant(dict, "CLASS_AUDIO", USB_CLASS_AUDIO); + addConstant(dict, "CLASS_COMM", USB_CLASS_COMM); + addConstant(dict, "CLASS_HID", USB_CLASS_HID); + addConstant(dict, "CLASS_PRINTER", USB_CLASS_PRINTER); + addConstant(dict, "CLASS_MASS_STORAGE", USB_CLASS_MASS_STORAGE); + addConstant(dict, "CLASS_HUB", USB_CLASS_HUB); + addConstant(dict, "CLASS_DATA", USB_CLASS_DATA); + addConstant(dict, "CLASS_VENDOR_SPEC", USB_CLASS_VENDOR_SPEC); + addConstant(dict, "DT_DEVICE", USB_DT_DEVICE); + addConstant(dict, "DT_CONFIG", USB_DT_CONFIG); + addConstant(dict, "DT_STRING", USB_DT_STRING); + addConstant(dict, "DT_INTERFACE", USB_DT_INTERFACE); + addConstant(dict, "DT_ENDPOINT", USB_DT_ENDPOINT); + addConstant(dict, "DT_HID", USB_DT_HID); + addConstant(dict, "DT_REPORT", USB_DT_REPORT); + addConstant(dict, "DT_PHYSICAL", USB_DT_PHYSICAL); + addConstant(dict, "DT_HUB", USB_DT_HUB); + addConstant(dict, "DT_DEVICE_SIZE", USB_DT_DEVICE_SIZE); + addConstant(dict, "DT_CONFIG_SIZE", USB_DT_CONFIG_SIZE); + addConstant(dict, "DT_INTERFACE_SIZE", USB_DT_INTERFACE_SIZE); + addConstant(dict, "DT_ENDPOINT_SIZE", USB_DT_ENDPOINT_SIZE); + addConstant(dict, "DT_ENDPOINT_AUDIO_SIZE", USB_DT_ENDPOINT_AUDIO_SIZE); + addConstant(dict, "DT_HUB_NONVAR_SIZE", USB_DT_HUB_NONVAR_SIZE); + addConstant(dict, "MAXENDPOINTS", USB_MAXENDPOINTS); + addConstant(dict, "ENDPOINT_ADDRESS_MASK", USB_ENDPOINT_ADDRESS_MASK); + addConstant(dict, "ENDPOINT_DIR_MASK", USB_ENDPOINT_DIR_MASK); + addConstant(dict, "ENDPOINT_TYPE_MASK", USB_ENDPOINT_TYPE_MASK); + addConstant(dict, "ENDPOINT_TYPE_CONTROL", USB_ENDPOINT_TYPE_CONTROL); + addConstant(dict, "ENDPOINT_TYPE_ISOCHRONOUS", USB_ENDPOINT_TYPE_ISOCHRONOUS); + addConstant(dict, "ENDPOINT_TYPE_BULK", USB_ENDPOINT_TYPE_BULK); + addConstant(dict, "ENDPOINT_TYPE_INTERRUPT", USB_ENDPOINT_TYPE_INTERRUPT); + addConstant(dict, "MAXINTERFACES", USB_MAXINTERFACES); + addConstant(dict, "MAXALTSETTING", USB_MAXALTSETTING); + addConstant(dict, "MAXCONFIG", USB_MAXCONFIG); + addConstant(dict, "REQ_GET_STATUS", USB_REQ_GET_STATUS); + addConstant(dict, "REQ_CLEAR_FEATURE", USB_REQ_CLEAR_FEATURE); + addConstant(dict, "REQ_SET_FEATURE", USB_REQ_SET_FEATURE); + addConstant(dict, "REQ_SET_ADDRESS", USB_REQ_SET_ADDRESS); + addConstant(dict, "REQ_GET_DESCRIPTOR", USB_REQ_GET_DESCRIPTOR); + addConstant(dict, "REQ_SET_DESCRIPTOR", USB_REQ_SET_DESCRIPTOR); + addConstant(dict, "REQ_GET_CONFIGURATION", USB_REQ_GET_CONFIGURATION); + addConstant(dict, "REQ_SET_CONFIGURATION", USB_REQ_SET_CONFIGURATION); + addConstant(dict, "REQ_GET_INTERFACE", USB_REQ_GET_INTERFACE); + addConstant(dict, "REQ_SET_INTERFACE", USB_REQ_SET_INTERFACE); + addConstant(dict, "REQ_SYNCH_FRAME", USB_REQ_SYNCH_FRAME); + addConstant(dict, "TYPE_STANDARD", USB_TYPE_STANDARD); + addConstant(dict, "TYPE_CLASS", USB_TYPE_CLASS); + addConstant(dict, "TYPE_VENDOR", USB_TYPE_VENDOR); + addConstant(dict, "TYPE_RESERVED", USB_TYPE_RESERVED); + addConstant(dict, "RECIP_DEVICE", USB_RECIP_DEVICE); + addConstant(dict, "RECIP_INTERFACE", USB_RECIP_INTERFACE); + addConstant(dict, "RECIP_ENDPOINT", USB_RECIP_ENDPOINT); + addConstant(dict, "RECIP_OTHER", USB_RECIP_OTHER); + addConstant(dict, "ENDPOINT_IN", USB_ENDPOINT_IN); + addConstant(dict, "ENDPOINT_OUT", USB_ENDPOINT_OUT); + addConstant(dict, "ERROR_BEGIN", USB_ERROR_BEGIN); +} + +/* + * Earlier versions of the PyUSB separate direction bit and + * endpoint address with direction and address fields... + * Windows version of the libusb does need direction + * bit in endpoint address to works fine. + * Thanks to Ray Schumacher. + */ +PYUSB_STATIC PyMemberDef Py_usb_Endpoint_Members[] = { + {"address", + T_UBYTE, + offsetof(Py_usb_Endpoint, address), + READONLY, + "Contains the endpoint address."}, + + {"type", + T_UBYTE, + offsetof(Py_usb_Endpoint, type), + READONLY, + "It contains one of the following values, \n" + "indicating the endpoint transfer type:\n" + "\tENDPOINT_TYPE_CONTROL\n" + "\tENDPOINT_TYPE_ISOCHRONOUS\n" + "\tENDPOINT_TYPE_BULK\n" + "\tENDPOINT_TYPE_INTERRUPT\n"}, + + {"maxPacketSize", + T_USHORT, + offsetof(Py_usb_Endpoint, maxPacketSize), + READONLY, + "The maximum number of data bytes the endpoint\n" + "can transfer in a transaction."}, + + {"interval", + T_UBYTE, + offsetof(Py_usb_Endpoint, interval), + READONLY, + "The maximum latency for polling interrupt endpoints, or\n" + "the interval for polling isochronous endpoints, or the maximum NAK\n" + "rate for high-speed bulk OUT or control endpoints."}, + + {NULL} +}; + +PYUSB_STATIC PyMethodDef Py_usb_Endpoint_Methods[] = { + {NULL, NULL} +}; + +PYUSB_STATIC PyTypeObject Py_usb_Endpoint_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "usb.Endpoint", /*tp_name*/ + sizeof(Py_usb_Endpoint), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "Endpoint descriptor object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Py_usb_Endpoint_Methods, /* tp_methods */ + Py_usb_Endpoint_Members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, /* destructor */ +}; + +PYUSB_STATIC void set_Endpoint_fields( + Py_usb_Endpoint *endpoint, + struct usb_endpoint_descriptor *ep + ) +{ + endpoint->address = ep->bEndpointAddress; + endpoint->type = ep->bmAttributes & 3; + endpoint->maxPacketSize = ep->wMaxPacketSize; + endpoint->interval = ep->bInterval; + endpoint->refresh = ep->bRefresh; + // endpoint->synchAddress - ep->bSynchAddress; +} + +PYUSB_STATIC Py_usb_Endpoint *new_Endpoint( + struct usb_endpoint_descriptor *ep + ) +{ + Py_usb_Endpoint *endpoint; + + endpoint = PyObject_New(Py_usb_Endpoint, + &Py_usb_Endpoint_Type); + + if (endpoint && ep) + set_Endpoint_fields(endpoint, ep); + + return endpoint; +} + +PYUSB_STATIC PyMemberDef Py_usb_Interface_Members[] = { + {"interfaceNumber", + T_UBYTE, + offsetof(Py_usb_Interface, interfaceNumber), + READONLY, + "Identifies the interface."}, + + {"alternateSetting", + T_UBYTE, + offsetof(Py_usb_Interface, alternateSetting), + READONLY, + "Alternate setting number."}, + + {"interfaceClass", + T_UBYTE, + offsetof(Py_usb_Interface, interfaceClass), + READONLY, + "Similar to DeviceClass in the device descriptor, but\n" + "for devices with a class specified by the interface."}, + + {"interfaceSubClass", + T_UBYTE, + offsetof(Py_usb_Interface, interfaceSubClass), + READONLY, + "Similar to bDeviceSubClass in the device\n" + "descriptor, but for devices with a class defined by the interface."}, + + {"interfaceProtocol", + T_UBYTE, + offsetof(Py_usb_Interface, interfaceProtocol), + READONLY, + "Similar to bDeviceProtocol in the device\n" + "descriptor, but for devices whose class is defined by the interface."}, + + {"endpoints", + T_OBJECT, + offsetof(Py_usb_Interface, endpoints), + READONLY, + "Tuple with interface endpoints."}, + + {NULL} +}; + +PYUSB_STATIC void Py_usb_Interface_del( + PyObject *self + ) +{ + Py_XDECREF((PyObject *) ((Py_usb_Interface *) self)->endpoints); + PyObject_Del(self); +} + +PYUSB_STATIC PyMethodDef Py_usb_Interface_Methods[] = { + {NULL} +}; + +PYUSB_STATIC PyTypeObject Py_usb_Interface_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "usb.Interface", /*tp_name*/ + sizeof(Py_usb_Interface), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + Py_usb_Interface_del, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "Interface descriptor object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Py_usb_Interface_Methods, /* tp_methods */ + Py_usb_Interface_Members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 /* destructor */ +}; + +PYUSB_STATIC void set_Interface_fields( + Py_usb_Interface *interface, + struct usb_interface_descriptor *i + ) +{ + u_int8_t index; + + interface->interfaceNumber = i->bInterfaceNumber; + interface->alternateSetting = i->bAlternateSetting; + interface->interfaceClass = i->bInterfaceClass; + interface->interfaceSubClass = i->bInterfaceSubClass; + interface->interfaceProtocol = i->bInterfaceProtocol; + interface->iInterface = i->iInterface; + + interface->endpoints = PyTuple_New(i->bNumEndpoints); + + if (!interface->endpoints) { + return; + } + + for (index = 0; index < i->bNumEndpoints; ++index) + PyTuple_SET_ITEM(interface->endpoints, index, + (PyObject *) new_Endpoint(i->endpoint+index)); +} + +PYUSB_STATIC Py_usb_Interface *new_Interface( + struct usb_interface_descriptor *i + ) +{ + Py_usb_Interface *interface; + + interface = PyObject_NEW(Py_usb_Interface, &Py_usb_Interface_Type); + + if (interface) { + set_Interface_fields(interface, i); + + if (PyErr_Occurred()) { + Py_XDECREF((PyObject *) interface); + return NULL; + } + } + + return interface; +} + +PYUSB_STATIC PyMemberDef Py_usb_Configuration_Members[] = { + {"totalLength", + T_USHORT, + offsetof(Py_usb_Configuration, totalLength), + READONLY, + "The number of data bytes that the device returns,\n" + "including the bytes for all of the configuration's interfaces and\n" + "endpoints."}, + + {"value", + T_UBYTE, + offsetof(Py_usb_Configuration, value), + READONLY, + "Identifies the configuration."}, + + {"selfPowered", + T_UBYTE, + offsetof(Py_usb_Configuration, selfPowered), + READONLY, + "True if the device is self powered."}, + + {"remoteWakeup", + T_UBYTE, + offsetof(Py_usb_Configuration, remoteWakeup), + READONLY, + "True if the device supports remote wakeup feature."}, + + {"maxPower", + T_UBYTE, + offsetof(Py_usb_Configuration, maxPower), + READONLY, + "Specifies the device current. This is the absolute value,\n" + "already multiplied by 2"}, + + {"interfaces", + T_OBJECT, + offsetof(Py_usb_Configuration, interfaces), + READONLY, + "Tuple with a tuple of the configuration interfaces.\n" + "Each element represents a sequence of the\n" + "alternate settings for each interface."}, + + {"iConfiguration", + T_UBYTE, + offsetof(Py_usb_Configuration, iConfiguration), + RO, + "Index to a string that describes the\n" + "configuration."}, + + {NULL} +}; + +PYUSB_STATIC PyMethodDef Py_usb_Configuration_Methods[] = { + {NULL, NULL} +}; + +PYUSB_STATIC void Py_usb_Configuration_del( + PyObject *self + ) +{ + Py_XDECREF((PyObject *) ((Py_usb_Configuration *) self)->interfaces); + PyObject_Del(self);; +} + +PYUSB_STATIC PyTypeObject Py_usb_Configuration_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "usb.Configuration", /*tp_name*/ + sizeof(Py_usb_Configuration), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + Py_usb_Configuration_del, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "Configuration descriptor object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Py_usb_Configuration_Methods, /* tp_methods */ + Py_usb_Configuration_Members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 /* destructor */ +}; + +PYUSB_STATIC void set_Configuration_fields( + Py_usb_Configuration *configuration, + struct usb_config_descriptor *config + ) +{ + u_int8_t i, j, k; + PyObject *t1; + + configuration->totalLength = config->wTotalLength; + configuration->value = config->bConfigurationValue; + configuration->iConfiguration = config->iConfiguration; + configuration->selfPowered = (config->bmAttributes >> 6) & 1; + configuration->remoteWakeup = (config->bmAttributes >> 5) & 1; + configuration->maxPower = config->MaxPower << 2; + + configuration->interfaces = PyTuple_New(config->bNumInterfaces); + + if (!configuration->interfaces) return; + + for (i = 0; i < config->bNumInterfaces; ++i) { + k = config->interface[i].num_altsetting; + t1 = PyTuple_New(k); + + if (!t1) return; + + for (j = 0; j < k; ++j) + PyTuple_SET_ITEM(t1, j, + (PyObject *) new_Interface(config->interface[i].altsetting+j)); + + PyTuple_SET_ITEM(configuration->interfaces, i, t1); + } +} + +PYUSB_STATIC Py_usb_Configuration *new_Configuration( + struct usb_config_descriptor *conf + ) +{ + Py_usb_Configuration *configuration; + + configuration = PyObject_NEW(Py_usb_Configuration, &Py_usb_Configuration_Type); + + if (configuration) { + set_Configuration_fields(configuration, conf); + + if (PyErr_Occurred()) { + Py_DECREF((PyObject *) configuration); + return NULL; + } + } + + return configuration; +} + +PYUSB_STATIC PyMemberDef Py_usb_Device_Members[] = { + {"usbVersion", + T_STRING_INPLACE, + offsetof(Py_usb_Device, usbVersion), + READONLY, + "String containing the USB specification number that\n" + "the device and its descriptors comply with."}, + + {"deviceClass", + T_UBYTE, + offsetof(Py_usb_Device, deviceClass), + READONLY, + "For devices that belong to a class, this field may\n" + "name the class."}, + + {"deviceSubClass", + T_UBYTE, + offsetof(Py_usb_Device, deviceSubClass), + READONLY, + "For devices that belong to a class, this field may\n" + "specify a subclass within the class."}, + + {"deviceProtocol", + T_UBYTE, + offsetof(Py_usb_Device, deviceProtocol), + READONLY, + "This field may specify a protocol defined by the\n" + "selected class or subclass."}, + + {"maxPacketSize", + T_UBYTE, + offsetof(Py_usb_Device, maxPacketSize), + READONLY, + "The maximum packet size for Endpoint 0."}, + + {"idVendor", + T_USHORT, + offsetof(Py_usb_Device, idVendor), + READONLY, + "Unique vendor identifier."}, + + {"idProduct", + T_USHORT, + offsetof(Py_usb_Device, idProduct), + READONLY, + "The manufacturer assigns a Product ID to identify the\n" + "device."}, + + {"deviceVersion", + T_STRING_INPLACE, + offsetof(Py_usb_Device, deviceVersion), + READONLY, + "String containing the device's release number."}, + + {"filename", + T_STRING_INPLACE, + offsetof(Py_usb_Device, filename), + READONLY, + ""}, + + {"configurations", + T_OBJECT, + offsetof(Py_usb_Device, configurations), + READONLY, + "Tuple with the device configurations."}, + + {"iManufacturer", + T_UBYTE, + offsetof(Py_usb_Device, iManufacturer), + RO, + "An index that points to a string describing the\n" + "manufacturer."}, + + {"iProduct", + T_UBYTE, + offsetof(Py_usb_Device, iProduct), + RO, + "An index that points to a string describing the product."}, + + {"iSerialNumber", + T_UBYTE, + offsetof(Py_usb_Device, iSerialNumber), + RO, + "An index that points to a string containing the\n" + "device's serial number."}, + + {NULL} +}; + +PYUSB_STATIC PyObject *Py_usb_Device_open( + PyObject *self, + PyObject *args + ) +{ + return (PyObject *) new_DeviceHandle((Py_usb_Device *) self); +} + +PYUSB_STATIC PyMethodDef Py_usb_Device_Methods[] = { + {"open", + Py_usb_Device_open, + METH_NOARGS, + "open() -> DeviceHandle\n\n" + "Open the device for use.\n" + "Returns a DeviceHandle object."}, + + {NULL, NULL} +}; + +PYUSB_STATIC void Py_usb_Device_del( + PyObject *self + ) +{ + Py_XDECREF(((Py_usb_Device *) self)->configurations); + PyObject_Del(self);; +} + +PYUSB_STATIC PyTypeObject Py_usb_Device_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "usb.Device", /*tp_name*/ + sizeof(Py_usb_Device), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + Py_usb_Device_del, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "Device descriptor object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Py_usb_Device_Methods, /* tp_methods */ + Py_usb_Device_Members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 /* destructor */ +}; + +PYUSB_STATIC void set_Device_fields( + Py_usb_Device *device, + struct usb_device *dev + ) +{ + struct usb_device_descriptor *desc = &dev->descriptor; + u_int8_t i; + + device->usbVersion[0] = ((desc->bcdUSB >> 12) & 0xf) + '0'; + device->usbVersion[1] = ((desc->bcdUSB >> 8) & 0xf) + '0'; + device->usbVersion[2] = '.'; + device->usbVersion[3] = ((desc->bcdUSB >> 4) & 0xf) + '0'; + device->usbVersion[4] = (desc->bcdUSB & 0xf) + '0'; + device->usbVersion[5] = '\0'; + + device->deviceVersion[0] = ((desc->bcdDevice >> 12) & 0xf) + '0'; + device->deviceVersion[1] = ((desc->bcdDevice >> 8) & 0xf) + '0'; + device->deviceVersion[2] = '.'; + device->deviceVersion[3] = ((desc->bcdDevice >> 4) & 0xf) + '0'; + device->deviceVersion[4] = (desc->bcdDevice & 0xf) + '0'; + device->deviceVersion[5] = '\0'; + + device->deviceClass = desc->bDeviceClass; + device->deviceSubClass = desc->bDeviceSubClass; + device->deviceProtocol = desc->bDeviceProtocol; + device->maxPacketSize = desc->bMaxPacketSize0; + device->idVendor = desc->idVendor; + device->idProduct = desc->idProduct; + device->iManufacturer = desc->iManufacturer; + device->iProduct = desc->iProduct; + device->iSerialNumber = desc->iSerialNumber; + strcpy(device->filename, dev->filename); + device->dev = dev; + + if (!dev->config) { + device->configurations = PyTuple_New(0); + return; + } + + device->configurations = PyTuple_New(desc->bNumConfigurations); + + if (!device->configurations) return; + + for (i = 0; i < desc->bNumConfigurations; ++i) + PyTuple_SET_ITEM(device->configurations, i, (PyObject *) new_Configuration(dev->config+i)); +} + +PYUSB_STATIC Py_usb_Device *new_Device( + struct usb_device *dev + ) +{ + Py_usb_Device *device; + + device = PyObject_NEW(Py_usb_Device, &Py_usb_Device_Type); + + if (device) { + set_Device_fields(device, dev); + + if (PyErr_Occurred()) { + Py_DECREF((PyObject *) device); + return NULL; + } + } + + return device; +} + +PYUSB_STATIC PyMemberDef Py_usb_Bus_Members[] = { + {"dirname", + T_STRING_INPLACE, + offsetof(Py_usb_Bus, dirname), + READONLY, + ""}, + + {"location", + T_UINT, + offsetof(Py_usb_Bus, location), + READONLY, + ""}, + + {"devices", + T_OBJECT, + offsetof(Py_usb_Bus, devices), + READONLY, + "Tuple with the bus devices"}, + + {NULL} +}; + +PYUSB_STATIC PyMethodDef Py_usb_Bus_Methods[] = { + {NULL, NULL} +}; + +PYUSB_STATIC void Py_usb_Bus_del( + PyObject *self + ) +{ + Py_XDECREF(((Py_usb_Bus *) self)->devices); + PyObject_Del(self); +} + +PYUSB_STATIC PyTypeObject Py_usb_Bus_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "usb.Bus", /*tp_name*/ + sizeof(Py_usb_Bus), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + Py_usb_Bus_del, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "Bus object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Py_usb_Bus_Methods, /* tp_methods */ + Py_usb_Bus_Members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 /* destructor */ +}; + +PYUSB_STATIC Py_usb_Bus *new_Bus( + struct usb_bus *b + ) +{ + Py_usb_Bus *bus; + u_int32_t i; + struct usb_device *dev; + + bus = PyObject_NEW(Py_usb_Bus, &Py_usb_Bus_Type); + + if (bus) { + bus->location = b->location; + strcpy(bus->dirname, b->dirname); + for(dev = b->devices, i = 0; dev; dev = dev->next) ++i; + bus->devices = PyTuple_New(i); + + if(!bus->devices) { + Py_DECREF((PyObject *) bus); + return NULL; + } + + for(dev = b->devices, i=0; dev; dev = dev->next, ++i) + PyTuple_SET_ITEM(bus->devices, i, (PyObject *) new_Device(dev)); + + if (PyErr_Occurred()) { + Py_DECREF((PyObject *) bus); + bus = NULL; + } + } + + return bus; +} + +PYUSB_STATIC PyMemberDef Py_usb_DeviceHandle_Members[] = { + {NULL} +}; + +/* + * def controlMsg(requestType, request, buffer, value = 0, index = 0, timeout = 100) + */ +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_controlMsg( + PyObject *self, + PyObject *args, + PyObject *kwds + ) +{ + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + int requestType; + int request; + int value = 0; + int index = 0; + char *bytes; + PyObject *data; + int size; + int timeout = DEFAULT_TIMEOUT; + int ret; + int as_read = 0; + + static char *kwlist[] = { + "requestType", + "request", + "buffer", + "value", + "index", + "timeout", + NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args, + kwds, + "iiO|iii", + kwlist, + &requestType, + &request, + &data, + &value, + &index, + &timeout)) { + return NULL; + } + + /* + * If is a number, should be a read operation... + */ + if (PyNumber_Check(data)) { + size = py_NumberAsInt(data); + if (PyErr_Occurred()) return NULL; + bytes = (char *) PyMem_Malloc(size); + if (!bytes) return NULL; + as_read = 1; + } else { + bytes = (char *) getBuffer(data, &size); + if (PyErr_Occurred()) return NULL; + } + + +#if DUMP_PARAMS + + fprintf(stderr, "controlMsg params:\n" + "\trequestType: %d\n" + "\trequest: %d\n" + "\tvalue: %d\n" + "\tindex: %d\n" + "\ttimeout: %d\n", + requestType, + request, + value, + index, + timeout); + + if (as_read) { + fprintf(stderr, "\tbuffer: %d\n", size); + } else { + fprintf(stderr, "controlMsg buffer param:\n"); + printBuffer(bytes, size); + } + +#endif /* DUMP_PARAMS */ + + Py_BEGIN_ALLOW_THREADS + ret = usb_control_msg(_self->deviceHandle, + requestType, + request, + value, + index, + bytes, + size, + timeout); + Py_END_ALLOW_THREADS + + if (ret < 0) { + PyMem_Free(bytes); + PyUSB_Error(); + return NULL; + } else if (as_read) { + PyObject *retObj = buildTuple(bytes, ret); + PyMem_Free(bytes); + return retObj; + } else { + PyMem_Free(bytes); + return PyInt_FromLong(ret); + } +} + +/* + * def setConfiguration(configuration) + */ +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_setConfiguration( + PyObject *self, + PyObject *args + ) +{ + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + int configuration; + int ret; + + if (SUPPORT_NUMBER_PROTOCOL(args)) { + configuration = (int) PyInt_AS_LONG(args); + } else if (PyObject_TypeCheck(args, &Py_usb_Configuration_Type)) { + configuration = ((Py_usb_Configuration *) args)->value; + } else { + PyErr_BadArgument(); + return NULL; + } + +#if DUMP_PARAMS + + fprintf(stderr, + "setConfiguration params:\n\tconfiguration: %d\n", + configuration); + +#endif /* DUMP_PARAMS */ + + Py_BEGIN_ALLOW_THREADS + ret = usb_set_configuration(_self->deviceHandle, configuration); + Py_END_ALLOW_THREADS + + if (ret < 0) { + PyUSB_Error(); + return NULL; + } else { + Py_RETURN_NONE; + } +} + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_claimInterface( + PyObject *self, + PyObject *args + ) +{ + int interfaceNumber; + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + + if (SUPPORT_NUMBER_PROTOCOL(args)) { + interfaceNumber = py_NumberAsInt(args); + if (PyErr_Occurred()) return NULL; + } else if (PyObject_TypeCheck(args, &Py_usb_Interface_Type)) { + interfaceNumber = ((Py_usb_Interface *) args)->interfaceNumber; + } else { + PyErr_BadArgument(); + return NULL; + } + +#if DUMP_PARAMS + + fprintf(stderr, + "claimInterface params:\n\tinterfaceNumber: %d\n", + interfaceNumber); + +#endif /* DUMP_PARAMS */ + + if (usb_claim_interface(_self->deviceHandle, interfaceNumber)) { + PyUSB_Error(); + return NULL; + } else { + _self->interfaceClaimed = interfaceNumber; + } + + Py_RETURN_NONE; +} + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_detachKernelDriver( + PyObject *self, + PyObject *args + ) +{ + int interfaceNumber; + int ret; + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + + if (SUPPORT_NUMBER_PROTOCOL(args)) { + interfaceNumber = py_NumberAsInt(args); + if (PyErr_Occurred()) return NULL; + } else if (PyObject_TypeCheck(args, &Py_usb_Interface_Type)) { + interfaceNumber = ((Py_usb_Interface *) args)->interfaceNumber; + } else { + PyErr_BadArgument(); + return NULL; + } + +#if DUMP_PARAMS + + fprintf(stderr, + "detachKernelDriver params:\n\tinterfaceNumber: %d\n", + interfaceNumber); + +#endif /* DUMP_PARAMS */ + +#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP + Py_BEGIN_ALLOW_THREADS + ret = usb_detach_kernel_driver_np(_self->deviceHandle, interfaceNumber); + Py_END_ALLOW_THREADS + + if (ret) { + PyUSB_Error(); + return NULL; + } +#endif + + Py_RETURN_NONE; +} + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_releaseInterface( + PyObject *self, + PyObject *args + ) +{ + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + + if (-1 != _self->interfaceClaimed) { + int ret; + Py_BEGIN_ALLOW_THREADS + ret = usb_release_interface(_self->deviceHandle, _self->interfaceClaimed); + Py_END_ALLOW_THREADS + + if (ret) { + PyUSB_Error(); + return NULL; + } else { + _self->interfaceClaimed = -1; + } + } else { + PyErr_SetString(PyExc_ValueError, "No interface claimed"); + return NULL; + } + + Py_RETURN_NONE; +} + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_setAltInterface( + PyObject *self, + PyObject *args + ) +{ + int altInterface, ret; + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + + if (SUPPORT_NUMBER_PROTOCOL(args)) { + altInterface = (int) py_NumberAsInt(args); + if (PyErr_Occurred()) return NULL; + } else if (PyObject_TypeCheck(args, &Py_usb_Interface_Type)) { + altInterface = ((Py_usb_Interface *) args)->alternateSetting; + } else { + PyErr_BadArgument(); + return NULL; + } + +#if DUMP_PARAMS + + fprintf(stderr, + "setAltInterface params:\n\taltInterface: %d\n", + altInterface); + +#endif /* DUMP_PARAMS */ + + Py_BEGIN_ALLOW_THREADS + ret = usb_set_altinterface(_self->deviceHandle, altInterface); + Py_END_ALLOW_THREADS + + if (ret < 0) { + PyUSB_Error(); + return NULL; + } else { + Py_RETURN_NONE; + } +} + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_bulkWrite( + PyObject *self, + PyObject *args + ) +{ + int endpoint; + int timeout = DEFAULT_TIMEOUT; + char *data; + int size; + PyObject *bytes; + int ret; + PyObject *retObj; + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + + if (!PyArg_ParseTuple(args, + "iO|i", + &endpoint, + &bytes, + &timeout)) { + return NULL; + } + + data = getBuffer(bytes, &size); + if (PyErr_Occurred()) return NULL; + +#if DUMP_PARAMS + + fprintf(stderr, + "bulkWrite params:\n" + "\tendpoint: %d\n" + "\ttimeout: %d\n", + endpoint, + timeout); + + fprintf(stderr, "bulkWrite buffer param:\n"); + printBuffer(data, size); + +#endif /* DUMP_PARAMS */ + + Py_BEGIN_ALLOW_THREADS + ret = usb_bulk_write(_self->deviceHandle, endpoint, data, size, timeout); + Py_END_ALLOW_THREADS + + PyMem_Free(data); + + if (ret < 0) { + PyUSB_Error(); + return NULL; + } else { + retObj = PyInt_FromLong(ret); + } + + return retObj; +} + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_bulkRead( + PyObject *self, + PyObject *args + ) +{ + int endpoint; + int timeout = DEFAULT_TIMEOUT; + char *buffer; + int size; + PyObject *ret; + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + + if (!PyArg_ParseTuple(args, + "ii|i", + &endpoint, + &size, + &timeout)) { + return NULL; + } + +#if DUMP_PARAMS + + fprintf(stderr, + "bulkRead params:\n" + "\tendpoint: %d\n" + "\tsize: %d\n" + "\ttimeout: %d\n", + endpoint, + size, + timeout); + +#endif /* DUMP_PARAMS */ + + buffer = (char *) PyMem_Malloc(size); + if (!buffer) return NULL; + + Py_BEGIN_ALLOW_THREADS + size = usb_bulk_read(_self->deviceHandle, endpoint, buffer, size, timeout); + Py_END_ALLOW_THREADS + + if (size < 0) { + PyMem_Free(buffer); + PyUSB_Error(); + return NULL; + } else { + ret = buildTuple(buffer, size); + PyMem_Free(buffer); + } + + return ret; +} + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_interruptWrite( + PyObject *self, + PyObject *args + ) +{ + int endpoint; + int timeout = DEFAULT_TIMEOUT; + char *data; + int size; + PyObject *bytes; + int ret; + PyObject *retObj; + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + + if (!PyArg_ParseTuple(args, + "iO|i", + &endpoint, + &bytes, + &timeout)) { + return NULL; + } + + data = getBuffer(bytes, &size); + if (PyErr_Occurred()) return NULL; + +#if DUMP_PARAMS + + fprintf(stderr, + "interruptWrite params:\n" + "\tendpoint: %d\n" + "\ttimeout: %d\n", + endpoint, + timeout); + + fprintf(stderr, "interruptWrite buffer param:\n"); + printBuffer(data, size); + +#endif /* DUMP_PARAMS */ + + Py_BEGIN_ALLOW_THREADS + ret = usb_interrupt_write(_self->deviceHandle, endpoint, data, size, timeout); + Py_END_ALLOW_THREADS + + PyMem_Free(data); + + if (ret < 0) { + PyUSB_Error(); + return NULL; + } else { + retObj = PyInt_FromLong(ret); + } + + return retObj; +} + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_interruptRead( + PyObject *self, + PyObject *args + ) +{ + int endpoint; + int timeout = DEFAULT_TIMEOUT; + char *buffer; + int size; + PyObject *ret; + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + + if (!PyArg_ParseTuple(args, + "ii|i", + &endpoint, + &size, + &timeout)) { + return NULL; + } + +#if DUMP_PARAMS + + fprintf(stderr, + "interruptRead params:\n" + "\tendpoint: %d\n" + "\tsize: %d\n" + "\ttimeout: %d\n", + endpoint, + size, + timeout); + +#endif /* DUMP_PARAMS */ + + buffer = (char *) PyMem_Malloc(size); + if (!buffer) return NULL; + + Py_BEGIN_ALLOW_THREADS + size = usb_interrupt_read(_self->deviceHandle, endpoint, buffer, size, timeout); + Py_END_ALLOW_THREADS + + if (size < 0) { + PyMem_Free(buffer); + PyUSB_Error(); + return NULL; + } else { + ret = buildTuple(buffer, size); + PyMem_Free(buffer); + } + + return ret; +} + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_resetEndpoint( + PyObject *self, + PyObject *args + ) +{ + int endpoint, ret; + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + + endpoint = py_NumberAsInt(args); + if (PyErr_Occurred()) return NULL; + +#if DUMP_PARAMS + + fprintf(stderr, + "resetEndpoint params:\n" + "\tendpoint: %d\n", + endpoint); + +#endif /* DUMP_PARAMS */ + Py_BEGIN_ALLOW_THREADS + ret = usb_resetep(_self->deviceHandle, endpoint); + Py_END_ALLOW_THREADS + + if (ret) { + PyUSB_Error(); + return NULL; + } else { + Py_RETURN_NONE; + } +} + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_reset( + PyObject *self, + PyObject *args + ) +{ + int ret; + + Py_BEGIN_ALLOW_THREADS + ret = usb_reset(((Py_usb_DeviceHandle *) self)->deviceHandle); + Py_END_ALLOW_THREADS + + if (ret) { + PyUSB_Error(); + return NULL; + } else { + Py_RETURN_NONE; + } +} + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_clearHalt( + PyObject *self, + PyObject *args + ) +{ + int endpoint, ret; + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + + endpoint = py_NumberAsInt(args); + if (PyErr_Occurred()) return NULL; + +#if DUMP_PARAMS + + fprintf(stderr, + "clearHalt params:\n" + "\tendpoint: %d\n", + endpoint); + +#endif /* DUMP_PARAMS */ + + Py_BEGIN_ALLOW_THREADS + ret = usb_clear_halt(_self->deviceHandle, endpoint); + Py_END_ALLOW_THREADS + + if (ret) { + PyUSB_Error(); + return NULL; + } else { + Py_RETURN_NONE; + } +} + +/* + * def getString(index, len, langid = -1) + */ +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_getString( + PyObject *self, + PyObject *args + ) +{ + int langid=-1, index; + unsigned long len; + PyObject *retStr; + char *buffer; + int ret; + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + + if (!PyArg_ParseTuple(args, + "ik|i", + &index, + &len, + &langid)) { + return NULL; + } + +#if DUMP_PARAMS + + fprintf(stderr, + "getString params:\n" + "\tindex: %d\n" + "\tlen: %lu\n" + "\tlangid: %d\n", + index, + len, + langid); + +#endif /* DUMP_PARAMS */ + + ++len; /* for NULL termination */ + buffer = (char *) PyMem_Malloc(len); + if (!buffer) return NULL; + + Py_BEGIN_ALLOW_THREADS + + if (-1 == langid) { + ret = usb_get_string_simple(_self->deviceHandle, index, buffer, len); + } else { + ret = usb_get_string(_self->deviceHandle, index, langid, buffer, len); + } + + Py_END_ALLOW_THREADS + + if (ret < 0) { + PyMem_Free(buffer); + PyUSB_Error(); + return NULL; + } + + retStr = PyString_FromStringAndSize(buffer, ret); + PyMem_Free(buffer); + return retStr; +} + +/* + * def getDescriptor(type, index, len, endpoint = -1) + */ +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_getDescriptor( + PyObject *self, + PyObject *args + ) +{ + int endpoint=-1, type, index; + int len; + PyObject *retSeq; + char *buffer; + int ret; + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + + if (!PyArg_ParseTuple(args, + "iii|i", + &type, + &index, + &len, + &endpoint)) { + return NULL; + } + +#if DUMP_PARAMS + + fprintf(stderr, + "getDescriptor params:\n" + "ttype: %d\n" + "\tindex: %d\n" + "\tlen: %d\n" + "\tendpoint: %d\n", + type, + index, + len, + endpoint); + +#endif /* DUMP_PARAMS */ + + buffer = (char *) PyMem_Malloc(len); + if (!buffer) return NULL; + + Py_BEGIN_ALLOW_THREADS + + if (-1 == endpoint) { + ret = usb_get_descriptor(_self->deviceHandle, type, index, buffer, len); + } else { + ret = usb_get_descriptor_by_endpoint(_self->deviceHandle, endpoint, type, index, buffer, len); + } + + Py_END_ALLOW_THREADS + + if (ret < 0) { + PyMem_Free(buffer); + PyUSB_Error(); + return NULL; + } + + retSeq = buildTuple(buffer, ret); + PyMem_Free(buffer); + return retSeq; +} + +PYUSB_STATIC PyMethodDef Py_usb_DeviceHandle_Methods[] = { + {"controlMsg", + (PyCFunction) Py_usb_DeviceHandle_controlMsg, + METH_VARARGS | METH_KEYWORDS, + "controlMsg(requestType, request, buffer, value=0, index=0, timeout=100) -> bytesWritten|buffer\n\n" + "Performs a control request to the default control pipe on a device.\n" + "Arguments:\n" + "\trequestType: specifies the direction of data flow, the type\n" + "\t of request, and the recipient.\n" + "\trequest: specifies the request.\n" + "\tbuffer: if the transfer is a write transfer, buffer is a sequence \n" + "\t with the transfer data, otherwise, buffer is the number of\n" + "\t bytes to read.\n" + "\tvalue: specific information to pass to the device. (default: 0)\n" + "\tindex: specific information to pass to the device. (default: 0)\n" + "\ttimeout: operation timeout in miliseconds. (default: 100)\n" + "Returns the number of bytes written."}, + + {"setConfiguration", + Py_usb_DeviceHandle_setConfiguration, + METH_O, + "setConfiguration(configuration) -> None\n\n" + "Sets the active configuration of a device.\n" + "Arguments:\n" + "\tconfiguration: a configuration value or a Configuration object."}, + + {"claimInterface", + Py_usb_DeviceHandle_claimInterface, + METH_O, + "claimInterface(interface) -> None\n\n" + "Claims the interface with the Operating System.\n" + "Arguments:\n" + "\tinterface: interface number or an Interface object."}, + + {"detachKernelDriver", + Py_usb_DeviceHandle_detachKernelDriver, + METH_O, + "detachKernelDriver(interface) -> None\n\n" + "Detaches a kernel driver from the interface (if one is attached,\n" + "we have permission and the operation is supported by the OS)\n" + "Arguments:\n" + "\tinterface: interface number or an Interface object."}, + + {"releaseInterface", + Py_usb_DeviceHandle_releaseInterface, + METH_NOARGS, + "releaseInterface() -> None\n\n" + "Releases an interface previously claimed with claimInterface."}, + + {"setAltInterface", + Py_usb_DeviceHandle_setAltInterface, + METH_O, + "setAltInterface(alternate) -> None\n\n" + "Sets the active alternate setting of the current interface.\n" + "Arguments:\n" + "\talternate: an alternate setting number or an Interface object."}, + + {"bulkWrite", + Py_usb_DeviceHandle_bulkWrite, + METH_VARARGS, + "bulkWrite(endpoint, buffer, timeout=100) -> bytesWritten\n\n" + "Performs a bulk write request to the endpoint specified.\n" + "Arguments:\n" + "\tendpoint: endpoint number.\n" + "\tbuffer: sequence data buffer to write.\n" + "\t This parameter can be any sequence type\n" + "\ttimeout: operation timeout in miliseconds. (default: 100)\n" + "Returns the number of bytes written."}, + + {"bulkRead", + Py_usb_DeviceHandle_bulkRead, + METH_VARARGS, + "bulkRead(endpoint, size, timeout=100) -> buffer\n\n" + "Performs a bulk read request to the endpoint specified.\n" + "Arguments:\n" + "\tendpoint: endpoint number.\n" + "\tsize: number of bytes to read.\n" + "\ttimeout: operation timeout in miliseconds. (default: 100)\n" + "Returns a tuple with the data read."}, + + {"interruptWrite", + Py_usb_DeviceHandle_interruptWrite, + METH_VARARGS, + "interruptWrite(endpoint, buffer, timeout=100) -> bytesWritten\n\n" + "Performs a interrupt write request to the endpoint specified.\n" + "Arguments:\n" + "\tendpoint: endpoint number.\n" + "\tbuffer: sequence data buffer to write.\n" + "\t This parameter can be any sequence type\n" + "\ttimeout: operation timeout in miliseconds. (default: 100)\n" + "Returns the number of bytes written."}, + + {"interruptRead", + Py_usb_DeviceHandle_interruptRead, + METH_VARARGS, + "interruptRead(endpoint, size, timeout=100) -> buffer\n\n" + "Performs a interrupt read request to the endpoint specified.\n" + "Arguments:\n" + "\tendpoint: endpoint number.\n" + "\tsize: number of bytes to read.\n" + "\ttimeout: operation timeout in miliseconds. (default: 100)\n" + "Returns a tuple with the data read."}, + + {"resetEndpoint", + Py_usb_DeviceHandle_resetEndpoint, + METH_O, + "resetEndpoint(endpoint) -> None\n\n" + "Resets all state (like toggles) for the specified endpoint.\n" + "Arguments:\n" + "\tendpoint: endpoint number.\n"}, + + {"reset", + Py_usb_DeviceHandle_reset, + METH_NOARGS, + "reset() -> None\n\n" + "Resets the specified device by sending a RESET\n" + "down the port it is connected to.\n"}, + + {"clearHalt", + Py_usb_DeviceHandle_clearHalt, + METH_O, + "clearHalt(endpoint) -> None\n\n" + "Clears any halt status on the specified endpoint.\n" + "Arguments:\n" + "\tendpoint: endpoint number.\n"}, + + {"getString", + Py_usb_DeviceHandle_getString, + METH_VARARGS, + "getString(index, len, langid = -1) -> string\n\n" + "Retrieves the string descriptor specified by index\n" + "and langid from a device.\n" + "Arguments:\n" + "\tindex: index of descriptor in the device.\n" + "\tlen: number of bytes of the string\n" + "\tlangid: Language ID. If it is omittedi, will be\n" + "\t used the first language.\n"}, + + {"getDescriptor", + Py_usb_DeviceHandle_getDescriptor, + METH_VARARGS, + "getDescriptor(type, index, len, endpoint = -1) -> descriptor\n\n" + "Retrieves a descriptor from the device identified by the type\n" + "and index of the descriptor.\n" + "Arguments:\n" + "\ttype: descriptor type.\n" + "\tindex: index of the descriptor.\n" + "\tlen: descriptor length.\n" + "\tendpoint: endpoint number from descriptor is read. If it is\n" + "\t omitted, the descriptor is read from default control pipe.\n"}, + + {NULL, NULL} +}; + +PYUSB_STATIC void Py_usb_DeviceHandle_del( + PyObject *self + ) +{ + Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self; + struct usb_dev_handle *h = _self->deviceHandle; + + if (h) { + if (-1 != _self->interfaceClaimed) { + usb_release_interface(_self->deviceHandle, + _self->interfaceClaimed); + } + + usb_close(_self->deviceHandle); + } + + PyObject_Del(self); +} + +PYUSB_STATIC PyTypeObject Py_usb_DeviceHandle_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "usb.DeviceHandle", /*tp_name*/ + sizeof(Py_usb_DeviceHandle), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + Py_usb_DeviceHandle_del, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "DeviceHandle object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Py_usb_DeviceHandle_Methods, /* tp_methods */ + Py_usb_DeviceHandle_Members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 /* destructor */ +}; + +PYUSB_STATIC Py_usb_DeviceHandle *new_DeviceHandle( + Py_usb_Device *device + ) +{ + Py_usb_DeviceHandle *dh; + struct usb_dev_handle *h; + + dh = PyObject_NEW(Py_usb_DeviceHandle, &Py_usb_DeviceHandle_Type); + + if (dh) { + h = usb_open(device->dev); + + if (!h) { + PyUSB_Error(); + Py_DECREF((PyObject *) dh); + return NULL; + } + + dh->deviceHandle = h; + dh->interfaceClaimed = -1; + } + + return dh; +} + +/* + * Global functions + */ + +PYUSB_STATIC PyObject *busses( + PyObject *self, + PyObject *args + ) +{ + PyObject *tuple; + struct usb_bus *bus, *b; + u_int32_t i; + + if (usb_find_busses() < 0) { + PyUSB_Error(); + return NULL; + } + + if (usb_find_devices() < 0) { + PyUSB_Error(); + return NULL; + } + + bus = usb_get_busses(); + + if (!bus) { + PyUSB_Error(); + return NULL; + } + + for(i=0,b=bus;b;b=b->next) ++i; + tuple = PyTuple_New(i); + if (!tuple) return NULL; + + for(b=bus,i=0;b;++i,b=b->next) + PyTuple_SET_ITEM(tuple, i, (PyObject *) new_Bus(b)); + + if (PyErr_Occurred()) { + Py_DECREF(tuple); + return NULL; + } + + return tuple; +} + +PYUSB_STATIC PyMethodDef usb_Methods[] = { + {"busses", busses, METH_NOARGS, "Returns a tuple with the usb busses"}, + {NULL, NULL} +}; + +#ifndef PyMODINIT_FUNC +#define PyMODINIT_FUNC void +#endif /* PyMODINIT_FUNC */ + +/* + * Entry point for the module + */ +PyMODINIT_FUNC initusb(void) +{ + PyObject *module; + + module = Py_InitModule3("usb", usb_Methods,"USB access module"); + if (!module) return; + + PyExc_USBError = PyErr_NewException("usb.USBError", PyExc_IOError, NULL); + if (!PyExc_USBError) return; + PyModule_AddObject(module, "USBError", PyExc_USBError); + Py_INCREF(PyExc_USBError); + + if (PyType_Ready(&Py_usb_Endpoint_Type) < 0) return; + Py_INCREF(&Py_usb_Endpoint_Type); + PyModule_AddObject(module, "Endpoint", (PyObject *) &Py_usb_Endpoint_Type); + + if (PyType_Ready(&Py_usb_Interface_Type) < 0) return; + Py_INCREF(&Py_usb_Interface_Type); + PyModule_AddObject(module, "Interface", (PyObject *) &Py_usb_Interface_Type); + + if (PyType_Ready(&Py_usb_Configuration_Type) < 0) return; + Py_INCREF(&Py_usb_Configuration_Type); + PyModule_AddObject(module, "Configuration", (PyObject *) &Py_usb_Configuration_Type); + + if (PyType_Ready(&Py_usb_Device_Type) < 0) return; + Py_INCREF(&Py_usb_Device_Type); + PyModule_AddObject(module, "Device", (PyObject *) &Py_usb_Device_Type); + + if (PyType_Ready(&Py_usb_Bus_Type) < 0) return; + Py_INCREF(&Py_usb_Bus_Type); + PyModule_AddObject(module, "Bus", (PyObject *) &Py_usb_Bus_Type); + + if (PyType_Ready(&Py_usb_DeviceHandle_Type) < 0) return; + Py_INCREF(&Py_usb_DeviceHandle_Type); + PyModule_AddObject(module, "DeviceHandle", (PyObject *) &Py_usb_DeviceHandle_Type); + + installModuleConstants(module); + + usb_init(); +} + +/* + * vim:ts=4 + */ diff --git a/pyusb/pyusb.h b/pyusb/pyusb.h new file mode 100644 index 0000000..785198e --- /dev/null +++ b/pyusb/pyusb.h @@ -0,0 +1,248 @@ +#ifndef __pyusb_h__ +#define __pyusb_h__ + +#include +#include +#include +#ifdef unix +#include +#include +#endif /* unix */ +#if 0 /* defined _WIN32 */ +/* + * I were having many problems trying compile with windows.h + * because the Visual C++ .NET language extensions + */ +#include +#endif /* _WIN32 */ + +#define STRING_ARRAY_SIZE 256 + +#define PYUSB_STATIC static + +#if defined _WIN32 && !defined unix + +#ifndef u_int8_t +typedef unsigned char u_int8_t; +#endif /* u_int8_t */ + +#ifndef u_int16_t +typedef unsigned short int u_int16_t; +#endif /* u_int16_t */ + +#ifndef u_int32_t +typedef unsigned long u_int32_t; +#endif /* u_int32_t */ + +#ifndef PATH_MAX +#define PATH_MAX 255 +#endif /* PATH_MAX */ + +#endif /* _WIN32 */ + +/* + * EndpointDescriptor object + */ +typedef struct _Py_usb_Endpoint { + PyObject_HEAD + u_int8_t address; + u_int8_t type; + u_int16_t maxPacketSize; + u_int8_t interval; + u_int8_t refresh; +} Py_usb_Endpoint; + + +/* + * Interface Object + */ +typedef struct _Py_usb_Interface { + PyObject_HEAD + u_int8_t interfaceNumber; + u_int8_t alternateSetting; + u_int8_t interfaceClass; + u_int8_t interfaceSubClass; + u_int8_t interfaceProtocol; + u_int8_t iInterface; + PyObject *endpoints; +} Py_usb_Interface; + +/* + * Configuration object + */ +typedef struct _Py_usb_Configuration { + PyObject_HEAD + u_int16_t totalLength; + u_int8_t value; + u_int8_t iConfiguration; + u_int8_t selfPowered; + u_int8_t remoteWakeup; + u_int16_t maxPower; + PyObject *interfaces; +} Py_usb_Configuration; + +/* + * Device object + */ +typedef struct _Py_usb_Device { + PyObject_HEAD + char usbVersion[STRING_ARRAY_SIZE]; + u_int8_t deviceClass; + u_int8_t deviceSubClass; + u_int8_t deviceProtocol; + u_int8_t maxPacketSize; + u_int16_t idVendor; + u_int16_t idProduct; + char deviceVersion[STRING_ARRAY_SIZE]; + u_int8_t iManufacturer; + u_int8_t iProduct; + u_int8_t iSerialNumber; + char filename[PATH_MAX + 1]; + PyObject *configurations; + struct usb_device *dev; // necessary for usb_open +} Py_usb_Device; + +/* + * Bus Object + */ +typedef struct _Py_usb_Bus { + PyObject_HEAD + char dirname[PATH_MAX + 1]; + u_int32_t location; + PyObject *devices; +} Py_usb_Bus; + +/* + * DeviceHandle object + */ +typedef struct _Py_usb_DeviceHandle { + PyObject_HEAD + usb_dev_handle *deviceHandle; + int interfaceClaimed; +} Py_usb_DeviceHandle; + +/* + * Functions prototypes + */ + +PYUSB_STATIC void set_Endpoint_fields( + Py_usb_Endpoint *endpoint, + struct usb_endpoint_descriptor *ep + ); + +PYUSB_STATIC Py_usb_Endpoint *new_Endpoint( + struct usb_endpoint_descriptor *ep + ); + +PYUSB_STATIC void set_Interface_fields( + Py_usb_Interface *interface, + struct usb_interface_descriptor *i + ); + +PYUSB_STATIC Py_usb_Interface *new_Interface( + struct usb_interface_descriptor *i + ); + +PYUSB_STATIC void set_Configuration_fields( + Py_usb_Configuration *configuration, + struct usb_config_descriptor *config + ); + +PYUSB_STATIC Py_usb_Configuration *new_Configuration( + struct usb_config_descriptor *conf + ); + +PYUSB_STATIC PyObject *Py_usb_Device_open( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC void set_Device_fields( + Py_usb_Device *device, + struct usb_device *dev + ); + +PYUSB_STATIC Py_usb_Device *new_Device( + struct usb_device *dev + ); + +PYUSB_STATIC Py_usb_Bus *new_Bus( + struct usb_bus *b + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_controlMsg( + PyObject *self, + PyObject *args, + PyObject *kwds + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_setConfiguration( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_claimInterface( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_detachKernelDriver( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_releaseInterface( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_setAltInterface( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_bulkWrite( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_bulkRead( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_interruptWrite( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_interruptRead( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_resetEndpoint( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_reset( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC PyObject *Py_usb_DeviceHandle_clearHalt( + PyObject *self, + PyObject *args + ); + +PYUSB_STATIC Py_usb_DeviceHandle *new_DeviceHandle( + Py_usb_Device *device + ); + +PYUSB_STATIC PyObject *busses( + PyObject *self, + PyObject *args + ); + +#endif /* __pyusb_h__ */ diff --git a/pyusb/pyusb.sln b/pyusb/pyusb.sln new file mode 100644 index 0000000..c5ff699 --- /dev/null +++ b/pyusb/pyusb.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pyusb", "pyusb.vcproj", "{D59C735D-D69F-42BD-8664-3CA8BC1CED3C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D59C735D-D69F-42BD-8664-3CA8BC1CED3C}.Debug|Win32.ActiveCfg = Debug|Win32 + {D59C735D-D69F-42BD-8664-3CA8BC1CED3C}.Debug|Win32.Build.0 = Debug|Win32 + {D59C735D-D69F-42BD-8664-3CA8BC1CED3C}.Release|Win32.ActiveCfg = Release|Win32 + {D59C735D-D69F-42BD-8664-3CA8BC1CED3C}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/pyusb/samples/usbenum.py b/pyusb/samples/usbenum.py new file mode 100644 index 0000000..6f3972a --- /dev/null +++ b/pyusb/samples/usbenum.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# +# Enumerate usb devices +# +#Copyright 2005 - 2007 Wander Lairson Costa + +import usb + +busses = usb.busses() + +for bus in busses: + devices = bus.devices + for dev in devices: + print "Device:", dev.filename + print " Device class:",dev.deviceClass + print " Device sub class:",dev.deviceSubClass + print " Device protocol:",dev.deviceProtocol + print " Max packet size:",dev.maxPacketSize + print " idVendor:",dev.idVendor + print " idProduct:",dev.idProduct + print " Device Version:",dev.deviceVersion + for config in dev.configurations: + print " Configuration:", config.value + print " Total length:", config.totalLength + print " selfPowered:", config.selfPowered + print " remoteWakeup:", config.remoteWakeup + print " maxPower:", config.maxPower + for intf in config.interfaces: + print " Interface:",intf[0].interfaceNumber + for alt in intf: + print " Alternate Setting:",alt.alternateSetting + print " Interface class:",alt.interfaceClass + print " Interface sub class:",alt.interfaceSubClass + print " Interface protocol:",alt.interfaceProtocol + for ep in alt.endpoints: + print " Endpoint:",hex(ep.address) + print " Type:",ep.type + print " Max packet size:",ep.maxPacketSize + print " Interval:",ep.interval + diff --git a/pyusb/samples/usbprint.py b/pyusb/samples/usbprint.py new file mode 100644 index 0000000..04ee7c0 --- /dev/null +++ b/pyusb/samples/usbprint.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python + +""" +Provides an interface to USB Printer Class devices. +""" + +import usb +import types + +PRINTER_CLASS = 0x07 +PRINTER_SUBCLASS = 0x01 +UNIDIRECTIONAL_PROTOCOL = 0x01 +BIDIRECTIONAL_PROTOCOL = 0x02 +IEEE1284_4_PROTOCOL = 0x03 +VENDOR_PROTOCOL = 0xff + +class Printer: + def __init__(self, device, configuration, interface): + """ + __init__(device, configuration, interface) -> None + + Initialize the device. + device: printer usb.Device object. + configuration: printer usb.Configuration object of the Device or configuration number. + interface: printer usb.Interface object representing the + interface and altenate setting. + + """ + if PRINTER_CLASS != interface.interfaceClass: + raise TypeError, "Wrong interface class" + + self.__devhandle = device.open() + self.__devhandle.setConfiguration(configuration) + self.__devhandle.claimInterface(interface) + self.__devhandle.setAltInterface(interface) + + self.__intf = interface.interfaceNumber + self.__alt = interface.alternateSetting + + self.__conf = (type(configuration) == types.IntType \ + or type(configuration) == types.LongType) and \ + configuration or \ + configuration.value + + # initialize members + # TODO: automatic endpoints detection + self.__bulkout = 1 + self.__bulkin = 0x82 + + def __del__(self): + try: + self.__devhandle.releaseInterface(self.__intf) + del self.__devhandle + except: + pass + + def getDeviceID(self, maxlen, timeout = 100): + """ + getDeviceID(maxlen, timeout = 100) -> device_id + + Get the device capabilities information. + maxlen: maxlength of the buffer. + timeout: operation timeout. + """ + return self.__devhandle.controlMsg(requestType = 0xa1, + request = 0, + value = self.__conf - 1, + index = self.__alt + (self.__intf << 8), + buffer = maxlen, + timeout = timeout) + + def getPortStatus(self, timeout = 100): + """ + getPortStatus(timeout = 100) -> status + + Get the port status. + timeout: operation timeout. + """ + return self.__devhandle.controlMsg(requestType = 0xa1, + request = 1, + value = 0, + index = self.__intf, + buffer = 1, + timeout = timeout)[0] + + def softReset(self, timeout = 100): + """ + softReset(timeout = 100) -> None + + Request flushes all buffers and resets the Bulk OUT + and Bulk IN pipes to their default states. + timeout: the operation timeout. + """ + self.__devhandle.controlMsg(requestType = 0x21, + request = 2, + value = 0, + index = self.__intf, + buffer = 0) + + + def write(self, buffer, timeout = 100): + """ + write(buffer, timeout = 100) -> written + + Write data to printer. + buffer: data buffer. + timeout: operation timeout. + """ + return self.__devhandle.bulkWrite(self.__bulkout, + buffer, + timeout) + + def read(self, numbytes, timeout = 100): + """ + read(numbytes, timeout = 100) -> data + + Read data from printer. + numbytes: number of bytes to read. + timeout: operation timeout. + """ + return self.__devhandle.bulkRead(self.__bulkin, + numbytes, + timeout) + + diff --git a/pyusb/setup.py b/pyusb/setup.py new file mode 100644 index 0000000..7ea5ce2 --- /dev/null +++ b/pyusb/setup.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# PyUSB setup script +# +# Copyright 2005 - 2007 Wander Lairson Costa + +from distutils.core import setup, Extension +import sys + +extra_link_args = [] +extra_compile_args = [] +platform = sys.platform.lower() +libraries = ["usb"] + +# necessary to work fine in MacOS +# many thanks to Damian Staniforth! :-) +if -1 != platform.find("mac"): + extra_link_args = ['-framework', + 'CoreFoundation', + '-framework', + 'IOKit'] +elif -1 != platform.find("win32"): + libraries = ["libusb"] +# necessary to work fine in darwin +# Many thanks to James Barabas! +elif -1 != platform.find("darwin"): + extra_link_args = ['-framework', + 'CoreFoundation', + '-framework', + 'IOKit', + '-L/opt/local/lib'] + extra_compile_args = ['-I/opt/local/include'] +# Juha Torkkel has reported problems compiling on freebsd +# when libusb is in /usr/local tree. I don't know on freebsd, but +# on Linux the paths to usr/local are in $PATH. +# Thanks Juha... ;) +elif -1 != platform.find("freebsd"): + extra_link_args = ['-L/usr/local/lib'] + extra_compile_args = ['-I/usr/local/include'] + + +usbmodule = Extension(name = 'usb', + libraries = libraries, + sources = ['pyusb.c'], + extra_link_args = extra_link_args, + extra_compile_args = extra_compile_args, + depends = ['pyusb.h']) + +setup(name = 'pyusb', + version = '0.4.1', + description = "USB access extension module", + long_description = + """ + PyUSB provides easy USB access to python. + The module contains classes and methods to + support the most USB operations. + """, + author = 'Wander Lairson Costa', + author_email = 'wander.lairson@gmail.com', + url = 'http://pyusb.berlios.de', + license = 'BSD', + ext_modules = [usbmodule]) + +# vim:ts=4 diff --git a/scripts/rom.py b/scripts/rom.py new file mode 100644 index 0000000..92102b3 --- /dev/null +++ b/scripts/rom.py @@ -0,0 +1,126 @@ +import sqlite3 + +# Detect Mirrord Roms +# Rom Type Mapping +# +# 0 ROM only +# 1 ROM and RAM +# 2 ROM and Save RAM +# 3 ROM and DSP1 chip +# 4 ROM, RAM and DSP1 chip +# 5 ROM, Save RAM and DSP1 chip +# 19 ROM and Super FX chip +#227 ROM, RAM and GameBoy data +#246 ROM and DSP2 chip + + +snes_header_tpl=''' +Super Nintendo Entertainment System/SNES/Super Famicom +SNES Tile Demo +Demo or Beta ROM? +Europe, Oceania and Asia +262144 Bytes (2.0000 Mb) + +Padded: Maybe, 227296 Bytes (1.7341 Mb) +Interleaved/Swapped: No +Backup unit/emulator header: Yes, 512 Bytes +HiROM: No +Internal size: 2 Mb +ROM type: (0) ROM +ROM speed: 200 ns (SlowROM) +SRAM: No +Version: 1.0 +Checksum: Ok, 0x6629 (calculated) == 0x6629 (internal) +Inverse checksum: Ok, 0x99d6 (calculated) == 0x99d6 (internal) +Checksum (CRC32): 0x8e16de1e +''' + +swc_header_tpl=''' +Backup unit header info (SWC) + +00000000 20 00 0c 00 00 00 00 00 aa bb 04 00 00 00 00 00 ............... +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + +[0-1] File size: 262144 Bytes (2.0000 Mb) => Matches +[2:7] Run program in mode: 3 +[2:6] Split: No => Matches +[2:5] SRAM mapping mode: LoROM => Matches +[2:4] DRAM mapping mode: LoROM => Matches +[2:3-2] SRAM size: 0 kB => Matches +[2:1] Run program in mode: 3 +[2:0] External cartridge memory: Disabled +''' + + +snes_header_regex='''(?P.*) +(?P.*) +(?P.*) +(?P.*) +''' + +a = ''' +(?P[\w]+) +(?P[\w]+) +(?P[\w]+) +(?P\d+) Bytes ((?P[\d.]+) Mb) + +Padded: Maybe, 227296 Bytes (1.7341 Mb) +Interleaved/Swapped: No +Backup unit/emulator header: Yes, 512 Bytes +HiROM: No +Internal size: 2 Mb +ROM type: (0) ROM +ROM speed: 200 ns (SlowROM) +SRAM: No +Version: 1.0 +Checksum: Ok, 0x6629 (calculated) == 0x6629 (internal) +Inverse checksum: Ok, 0x99d6 (calculated) == 0x99d6 (internal) +Checksum (CRC32): 0x8e16de1e +''' + + +import os +import re +import string +os.unlink("roms.sqlite3") + +conn = sqlite3.connect('roms.sqlite3') +c = conn.cursor() +c.execute('''create table roms + ( + file_name text, + file_ext text, + file_size integer, + rom_size integer, + rom_mb real, + rom_name text, + rom_vendor text, + rom_region text, + rom_hirom integer, + rom_internalsize integer, + rom_type, integer, + rom_speed integer, + rom_sram integer, + rom_version integer, + rom_chk integer, + swc_size integer, + swc_mode integer, + swc_split text, + swc_sram_mode text, + swc_dram_mode text, + swc_sram_size text + + )''') + +#c.execute("""insert into stocks +# values ('2006-01-05','BUY','RHAT',100,35.14)""") +conn.commit + +c.close() + + +print re.compile(snes_header_regex,re.M).search(snes_header_tpl).groups() + + + diff --git a/scripts/roms.sqlite3 b/scripts/roms.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..7a8384c38ed31f5e959565229e8ba348d9633f2e GIT binary patch literal 3072 zcmeHGyG{c!5VRo(DG~)mly*yy((wZXbX^)CedOf!5{rAuw!-5F==f%CZ4n*Z!)c;x zY}t~%9?j0q+AmKFX%Lb^Wx~K68!^t=Jpf~jSF1kgT#wcFbzEbAL6MOSpEoW;AUF^l z7|eky%IyB;U>ZW=;6QL-7zgP8bCxigryE~WemR{?`1iFD>l9UyXsd!1I*UHWhfMB z7u}%*=u$^X_n60pBECM9!mTWFA8IEnMk~!z09~tGq^@EQ*nE^YJBC%4i18mF9a?&4 z%~rxm^_jg#rCseFotxdOO%8pG`fbzF=MJ3&&WhMQ9xrQ|O7|MO8fkM^WjoG3RD2Xl O6Uw%bxW+9;-~IqgEZyV) literal 0 HcmV?d00001