David Voswinkel пре 15 година
родитељ
комит
091b82a7ef
100 измењених фајлова са 20439 додато и 0 уклоњено
  1. 283 0
      tools/bsnes/Makefile
  2. 0 0
      tools/bsnes/README
  3. 49 0
      tools/bsnes/base.hpp
  4. BIN
      tools/bsnes/bsnes.lnk
  5. 234 0
      tools/bsnes/cart/cart.cpp
  6. 178 0
      tools/bsnes/cart/cart.hpp
  7. 109 0
      tools/bsnes/cart/cart_file.cpp
  8. 272 0
      tools/bsnes/cart/cart_header.cpp
  9. 244 0
      tools/bsnes/cart/cart_loader.cpp
  10. 3 0
      tools/bsnes/cc.bat
  11. 2 0
      tools/bsnes/cc.sh
  12. 392 0
      tools/bsnes/cheat/cheat.cpp
  13. 69 0
      tools/bsnes/cheat/cheat.hpp
  14. 8 0
      tools/bsnes/chip/bsx/bsx.cpp
  15. 77 0
      tools/bsnes/chip/bsx/bsx.hpp
  16. 137 0
      tools/bsnes/chip/bsx/bsx_base.cpp
  17. 101 0
      tools/bsnes/chip/bsx/bsx_cart.cpp
  18. 113 0
      tools/bsnes/chip/bsx/bsx_flash.cpp
  19. 11 0
      tools/bsnes/chip/chip.hpp
  20. 197 0
      tools/bsnes/chip/cx4/cx4.cpp
  21. 97 0
      tools/bsnes/chip/cx4/cx4.hpp
  22. 187 0
      tools/bsnes/chip/cx4/cx4data.cpp
  23. 246 0
      tools/bsnes/chip/cx4/cx4fn.cpp
  24. 223 0
      tools/bsnes/chip/cx4/cx4oam.cpp
  25. 226 0
      tools/bsnes/chip/cx4/cx4ops.cpp
  26. 59 0
      tools/bsnes/chip/dsp1/dsp1.cpp
  27. 18 0
      tools/bsnes/chip/dsp1/dsp1.hpp
  28. 1625 0
      tools/bsnes/chip/dsp1/dsp1emu.cpp
  29. 127 0
      tools/bsnes/chip/dsp1/dsp1emu.hpp
  30. 136 0
      tools/bsnes/chip/dsp2/dsp2.cpp
  31. 44 0
      tools/bsnes/chip/dsp2/dsp2.hpp
  32. 177 0
      tools/bsnes/chip/dsp2/dsp2_op.cpp
  33. 35 0
      tools/bsnes/chip/dsp3/dsp3.cpp
  34. 12 0
      tools/bsnes/chip/dsp3/dsp3.hpp
  35. 1146 0
      tools/bsnes/chip/dsp3/dsp3emu.c
  36. 55 0
      tools/bsnes/chip/dsp4/dsp4.cpp
  37. 12 0
      tools/bsnes/chip/dsp4/dsp4.hpp
  38. 2150 0
      tools/bsnes/chip/dsp4/dsp4emu.c
  39. 108 0
      tools/bsnes/chip/dsp4/dsp4emu.h
  40. 72 0
      tools/bsnes/chip/obc1/obc1.cpp
  41. 25 0
      tools/bsnes/chip/obc1/obc1.hpp
  42. 158 0
      tools/bsnes/chip/sdd1/sdd1.cpp
  43. 40 0
      tools/bsnes/chip/sdd1/sdd1.hpp
  44. 451 0
      tools/bsnes/chip/sdd1/sdd1emu.cpp
  45. 162 0
      tools/bsnes/chip/sdd1/sdd1emu.hpp
  46. 511 0
      tools/bsnes/chip/spc7110/decomp.cpp
  47. 45 0
      tools/bsnes/chip/spc7110/decomp.hpp
  48. 672 0
      tools/bsnes/chip/spc7110/spc7110.cpp
  49. 133 0
      tools/bsnes/chip/spc7110/spc7110.hpp
  50. 226 0
      tools/bsnes/chip/srtc/srtc.cpp
  51. 22 0
      tools/bsnes/chip/srtc/srtc.hpp
  52. 87 0
      tools/bsnes/chip/st010/st010.cpp
  53. 42 0
      tools/bsnes/chip/st010/st010.hpp
  54. 126 0
      tools/bsnes/chip/st010/st010_data.hpp
  55. 261 0
      tools/bsnes/chip/st010/st010_op.cpp
  56. 1 0
      tools/bsnes/clean.bat
  57. 1 0
      tools/bsnes/clean.sh
  58. 17 0
      tools/bsnes/cpu/cpu.cpp
  59. 74 0
      tools/bsnes/cpu/cpu.hpp
  60. 74 0
      tools/bsnes/cpu/cpuregs.hpp
  61. 483 0
      tools/bsnes/cpu/dcpu.cpp
  62. 4 0
      tools/bsnes/cpu/scpu/core/cc.sh
  63. 1 0
      tools/bsnes/cpu/scpu/core/clean.sh
  64. 90 0
      tools/bsnes/cpu/scpu/core/core.cpp
  65. 54 0
      tools/bsnes/cpu/scpu/core/core.hpp
  66. 298 0
      tools/bsnes/cpu/scpu/core/op_misc.b
  67. 539 0
      tools/bsnes/cpu/scpu/core/op_misc.cpp
  68. 163 0
      tools/bsnes/cpu/scpu/core/op_pc.b
  69. 279 0
      tools/bsnes/cpu/scpu/core/op_pc.cpp
  70. 317 0
      tools/bsnes/cpu/scpu/core/op_read.b
  71. 1654 0
      tools/bsnes/cpu/scpu/core/op_read.cpp
  72. 181 0
      tools/bsnes/cpu/scpu/core/op_rmw.b
  73. 573 0
      tools/bsnes/cpu/scpu/core/op_rmw.cpp
  74. 181 0
      tools/bsnes/cpu/scpu/core/op_write.b
  75. 293 0
      tools/bsnes/cpu/scpu/core/op_write.cpp
  76. 371 0
      tools/bsnes/cpu/scpu/core/opfn.cpp
  77. 12 0
      tools/bsnes/cpu/scpu/core/scpugen.cpp
  78. 271 0
      tools/bsnes/cpu/scpu/dma/dma.cpp
  79. 71 0
      tools/bsnes/cpu/scpu/dma/dma.hpp
  80. 125 0
      tools/bsnes/cpu/scpu/memory/memory.cpp
  81. 35 0
      tools/bsnes/cpu/scpu/memory/memory.hpp
  82. 534 0
      tools/bsnes/cpu/scpu/mmio/mmio.cpp
  83. 71 0
      tools/bsnes/cpu/scpu/mmio/mmio.hpp
  84. 61 0
      tools/bsnes/cpu/scpu/scpu.cpp
  85. 94 0
      tools/bsnes/cpu/scpu/scpu.hpp
  86. 35 0
      tools/bsnes/cpu/scpu/timing/event.cpp
  87. 107 0
      tools/bsnes/cpu/scpu/timing/irq.cpp
  88. 28 0
      tools/bsnes/cpu/scpu/timing/joypad.cpp
  89. 166 0
      tools/bsnes/cpu/scpu/timing/timing.cpp
  90. 41 0
      tools/bsnes/cpu/scpu/timing/timing.hpp
  91. 9 0
      tools/bsnes/data/bsnes.Manifest
  92. 8 0
      tools/bsnes/data/bsnes.desktop
  93. BIN
      tools/bsnes/data/bsnes.ico
  94. BIN
      tools/bsnes/data/bsnes.png
  95. 81 0
      tools/bsnes/data/documentation.html
  96. BIN
      tools/bsnes/data/joypad.png
  97. 87 0
      tools/bsnes/data/license.html
  98. BIN
      tools/bsnes/data/logo.png
  99. 588 0
      tools/bsnes/dsp/adsp/adsp.cpp
  100. 172 0
      tools/bsnes/dsp/adsp/adsp.hpp

+ 283 - 0
tools/bsnes/Makefile

@@ -0,0 +1,283 @@
+include lib/nall/Makefile.string
+prefix = /usr/local
+ui = ui_qt
+
+################
+### compiler ###
+################
+
+platform=x
+compiler=gcc
+enable_gzip=true
+
+ifneq ($(findstring gcc,$(compiler)),) # GCC family
+  flags = -O2 -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 = 
+  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
+  flags += -g -ggdb  -export-symbols
+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)
+	gcc $(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 ""

+ 0 - 0
tools/bsnes/README


+ 49 - 0
tools/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 <libco/libco.h>
+
+#include <nall/algorithm.hpp>
+#include <nall/array.hpp>
+#include <nall/bit.hpp>
+#include <nall/detect.hpp>
+#include <nall/endian.hpp>
+#include <nall/file.hpp>
+#include <nall/moduloarray.hpp>
+#include <nall/new.hpp>
+#include <nall/platform.hpp>
+#include <nall/property.hpp>
+#include <nall/stdint.hpp>
+#include <nall/string.hpp>
+#include <nall/utility.hpp>
+#include <nall/vector.hpp>
+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"

BIN
tools/bsnes/bsnes.lnk


+ 234 - 0
tools/bsnes/cart/cart.cpp

@@ -0,0 +1,234 @@
+#include <../base.hpp>
+#include <../chip/chip.hpp>
+#include <../reader/reader.hpp>
+#define CART_CPP
+
+#include <nall/crc32.hpp>
+#include <nall/ups.hpp>
+
+#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;
+}

+ 178 - 0
tools/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<bool>   loaded;            //is a base cartridge inserted?
+  property_t<bool>   bsx_flash_loaded;  //is a BS-X flash cart connected?
+  property_t<bool>   patched;           //has a UPS patch been applied?
+  property_t<string> name;              //display name (filename sans path and extension)
+
+  property_t<Mode>             mode;
+  property_t<Region>           region;
+  property_t<MemoryMapper>     mapper;
+  property_t<DSP1MemoryMapper> dsp1_mapper;
+
+  property_t<bool> has_bsx_slot;
+  property_t<bool> has_superfx;
+  property_t<bool> has_sa1;
+  property_t<bool> has_srtc;
+  property_t<bool> has_sdd1;
+  property_t<bool> has_spc7110, has_spc7110rtc;
+  property_t<bool> has_cx4;
+  property_t<bool> has_dsp1, has_dsp2, has_dsp3, has_dsp4;
+  property_t<bool> has_obc1;
+  property_t<bool> 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;

+ 109 - 0
tools/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

+ 272 - 0
tools/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

+ 244 - 0
tools/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

+ 3 - 0
tools/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

+ 2 - 0
tools/bsnes/cc.sh

@@ -0,0 +1,2 @@
+make platform=x compiler=gcc
+#make platform=x compiler=gcc enable_gzip=true enable_jma=true

+ 392 - 0
tools/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;
+}

+ 69 - 0
tools/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<unsigned> addr;
+    array<uint8_t> 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<cheat_t> 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;

+ 8 - 0
tools/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"

+ 77 - 0
tools/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;

+ 137 - 0
tools/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(&regs, 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

+ 101 - 0
tools/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

+ 113 - 0
tools/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

+ 11 - 0
tools/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"

+ 197 - 0
tools/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<count;i++) {
+    write(dest++, bus.read(src++));
+  }
+}
+
+void Cx4::write(unsigned addr, uint8 data) {
+  addr &= 0x1fff;
+
+  if(addr < 0x0c00) {
+  //ram
+    ram[addr] = data;
+    return;
+  }
+
+  if(addr < 0x1f00) {
+  //unmapped
+    return;
+  }
+
+//command register
+  reg[addr & 0xff] = data;
+
+  if(addr == 0x1f47) {
+  //memory transfer
+    transfer_data();
+    return;
+  }
+
+  if(addr == 0x1f4f) {
+  //c4 command
+    if(reg[0x4d] == 0x0e && !(data & 0xc3)) {
+    //c4 test command
+      reg[0x80] = data >> 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);
+}

+ 97 - 0
tools/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;

+ 187 - 0
tools/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

+ 246 - 0
tools/bsnes/chip/cx4/cx4fn.cpp

@@ -0,0 +1,246 @@
+#ifdef CX4_CPP
+
+#include <math.h>
+#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

+ 223 - 0
tools/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

+ 226 - 0
tools/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

+ 59 - 0
tools/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);
+  }
+}

+ 18 - 0
tools/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;

+ 1625 - 0
tools/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<uint8>(mDr>>8);
+      else
+         data = static_cast<uint8>(mDr);
+   }
+   else
+   {
+      if (mSr&DRS)
+      {
+         mDr &= 0x00ff;
+         mDr |= data<<8;
+      }
+      else
+      {
+         mDr &= 0xff00;
+         mDr |= data;
+      }
+   }
+
+
+   switch (mFsmMajorState)
+   {
+      case WAIT_COMMAND:
+         mCommand = static_cast<uint8>(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<int16>(mDr);
+            if (mDataCounter >= mCommandTable[mCommand].reads)
+            {
+               (this->*mCommandTable[mCommand].callback)(mReadBuffer, mWriteBuffer);
+               if (0 != mCommandTable[mCommand].writes)  // any output?
+               {
+                  mDataCounter = 0;
+                  mDr = static_cast<uint16>(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<uint16>(mWriteBuffer[mDataCounter]);
+               }
+               else
+               {
+                  mDr = 0x0080;  // valid command completion
+                  mFsmMajorState = WAIT_COMMAND;
+                  mSr |= DRC;
+               }
+            }
+            else
+            {
+               mDr = static_cast<uint16>(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<int16>(Radius);
+   RadiusHigh = static_cast<int16>(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<E3)?E:E3;
+   refE = (refE<E4)?refE:E4;
+
+   Px=shiftR(Px,E4-refE);    // normalize them to the same exponent
+   Py=shiftR(Py,E-refE);
+   Pz=shiftR(Pz,E3-refE);
+
+   C11=- (Px*shared.Nx>>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<uint16>(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

+ 127 - 0
tools/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
+

+ 136 - 0
tools/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() {}

+ 44 - 0
tools/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;

+ 177 - 0
tools/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

+ 35 - 0
tools/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();
+}

+ 12 - 0
tools/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;

+ 1146 - 0
tools/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

+ 55 - 0
tools/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();
+  }
+}

+ 12 - 0
tools/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;

+ 2150 - 0
tools/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

+ 108 - 0
tools/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

+ 72 - 0
tools/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() {}

+ 25 - 0
tools/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;

+ 158 - 0
tools/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;
+}

+ 40 - 0
tools/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;

+ 451 - 0
tools/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))<<bit_count;
+
+  ++bit_count;
+
+  if (codeword & 0x80) {
+    codeword |= SDD1_read(byte_ptr+1)>>(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<<code_num);
+  }
+
+}
+
+///////////////////////////////////////////////////////
+
+SDD1_BG::SDD1_BG(SDD1_GCD *associatedGCD, uint8 code) :
+  GCD(associatedGCD), code_num(code)
+{
+
+}
+
+///////////////////////////////////////////////
+
+
+void SDD1_BG::prepareDecomp(void) {
+
+  MPScount=0;
+  LPSind=0;
+
+}
+
+//////////////////////////////////////////////
+
+
+uint8 SDD1_BG::getBit(bool8 *endOfRun) {
+
+  uint8 bit;
+
+  if (!(MPScount || LPSind)) GCD->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

+ 162 - 0
tools/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;
+
+};

+ 511 - 0
tools/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

+ 45 - 0
tools/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);
+};

+ 672 - 0
tools/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<time_t>::max() - rtc_time + current_time + 1);  //compensate for overflow
+  if(diff > std::numeric_limits<time_t>::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() {
+}

+ 133 - 0
tools/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;

+ 226 - 0
tools/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<time_t>::max() - rtc_time + current_time + 1);  //compensate for overflow
+  if(diff > std::numeric_limits<time_t>::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() {
+}

+ 22 - 0
tools/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;

+ 87 - 0
tools/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;
+  }
+}

+ 42 - 0
tools/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;

+ 126 - 0
tools/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 }
+};

+ 261 - 0
tools/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

+ 1 - 0
tools/bsnes/clean.bat

@@ -0,0 +1 @@
+@mingw32-make platform=win compiler=mingw32-gcc clean

+ 1 - 0
tools/bsnes/clean.sh

@@ -0,0 +1 @@
+make platform=x compiler=gcc clean

+ 17 - 0
tools/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() {
+}

+ 74 - 0
tools/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();
+};

+ 74 - 0
tools/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) {}
+};

+ 483 - 0
tools/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, "?????? <CPU within opcode>");
+    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

+ 4 - 0
tools/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

+ 1 - 0
tools/bsnes/cpu/scpu/core/clean.sh

@@ -0,0 +1 @@
+rm scpugen

+ 90 - 0
tools/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

+ 54 - 0
tools/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);

+ 298 - 0
tools/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;
+}

+ 539 - 0
tools/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

+ 163 - 0
tools/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;
+}

+ 279 - 0
tools/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

+ 317 - 0
tools/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);
+}

+ 1654 - 0
tools/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

+ 181 - 0
tools/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);
+}

+ 573 - 0
tools/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

+ 181 - 0
tools/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);
+}

+ 293 - 0
tools/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

+ 371 - 0
tools/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

+ 12 - 0
tools/bsnes/cpu/scpu/core/scpugen.cpp

@@ -0,0 +1,12 @@
+#define CLASS_NAME "sCPU"
+#include <tool/opgen_switch.cpp>
+
+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;
+}

+ 271 - 0
tools/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

+ 71 - 0
tools/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();

+ 125 - 0
tools/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

+ 35 - 0
tools/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);

+ 534 - 0
tools/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

+ 71 - 0
tools/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);

+ 61 - 0
tools/bsnes/cpu/scpu/scpu.cpp

@@ -0,0 +1,61 @@
+#include <../base.hpp>
+#define SCPU_CPP
+
+#include <nall/priorityqueue.hpp>
+priority_queue<unsigned> 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() {
+}

+ 94 - 0
tools/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();
+};

+ 35 - 0
tools/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

+ 107 - 0
tools/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

+ 28 - 0
tools/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

+ 166 - 0
tools/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

+ 41 - 0
tools/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

+ 9 - 0
tools/bsnes/data/bsnes.Manifest

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity type="win32" name="bsnes" version="1.0.0.0" processorArchitecture="x86"/>
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*"/>
+    </dependentAssembly>
+  </dependency>
+</assembly>

+ 8 - 0
tools/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;

BIN
tools/bsnes/data/bsnes.ico


BIN
tools/bsnes/data/bsnes.png


+ 81 - 0
tools/bsnes/data/documentation.html

@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body>
+
+<h1>bsnes&trade; Usage Documentation</h1><br>
+
+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.
+<hr>
+
+<h2><u>Modes of Operation</u></h2><br>
+
+bsnes is capable of running both in its default multi-user mode, as well as
+in single-user mode.<br>
+<br>
+
+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".<br>
+<br>
+
+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.
+<hr>
+
+<h2><u>Supported Filetypes</u></h2><br>
+
+<b>SFC, SMC, SWC, FIG:</b> SNES cartridge &mdash; ROM image.<br>
+<b>BS:</b> Satellaview BS-X flash cartridge &mdash; EEPROM image.<br>
+<b>ST:</b> Sufami Turbo cartridge &mdash; ROM image.<br>
+<b>SRM, PSR:</b> non-volatile memory, often used to save game data &mdash; (P)SRAM image.<br>
+<b>RTC:</b> real-time clock non-volatile memory.<br>
+<b>UPS:</b> patch data, used to dynamically modify cartridge of same base filename upon load.<br>
+<b>CHT:</b> plain-text list of "Game Genie" / "Pro Action Replay" codes.
+<hr>
+
+<h2><u>Known Limitations</u></h2><br>
+
+<b>Cartridge co-processors:</b> 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.<br>
+<br>
+
+<b>Satellaview BS-X emulation:</b> this hardware is only partially supported. As a result,
+most BS-X software will not function correctly.<br>
+<br>
+
+<b>Savestates:</b> due to the design of bsnes, it is not plausible to
+implement support for savestate and/or rewind functionality.<br>
+<br>
+
+<b>Netplay:</b> internet multiplay is not currently supported nor planned.
+<hr>
+
+<h2><u>Contributors</u></h2>
+&bull; Andreas Naive<br>
+&bull; anomie<br>
+&bull; Derrick Sobodash<br>
+&bull; DMV27<br>
+&bull; FirebrandX<br>
+&bull; FitzRoy<br>
+&bull; GIGO<br>
+&bull; Jonas Quinn<br>
+&bull; kode54<br>
+&bull; krom<br>
+&bull; Matthew Callis<br>
+&bull; Nach<br>
+&bull; neviksti<br>
+&bull; Overload<br>
+&bull; RedDwarf<br>
+&bull; Richard Bannister<br>
+&bull; Shay Green<br>
+&bull; tetsuo55<br>
+&bull; TRAC<br>
+&bull; zones<br>
+
+</body>
+</html>

BIN
tools/bsnes/data/joypad.png


+ 87 - 0
tools/bsnes/data/license.html

@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body>
+
+<h1>bsnes&trade; Reference License</h1><br>
+<b>Copyright &copy; 2004&ndash;2009 byuu<br>
+All rights reserved</b>
+<hr>
+
+<h2><u>1. Definitions</u></h2><br>
+
+The terms "reproduce", "reproduction", "distribute" and "distribution" have the
+same meaning here as under U.S. copyright law.<br><br>
+
+"The software" means this software package as a whole, including, but not
+limited to, this license, binaries, source code, documentation, and data.<br><br>
+
+"You" means the licensee of the software.<br><br>
+
+"The licensor" means the copyright holder of the software, byuu.
+<hr>
+
+<h2><u>2. Grant of Rights</u></h2><br>
+
+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.
+<hr>
+
+<h2><u>3. Limitations</u></h2><br>
+
+This license does not grant you any rights to use the licensor's name, logo or
+trademarks.<br>
+<br>
+
+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.<br>
+<br>
+
+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.
+<hr>
+
+<h2><u>4. Exemptions</u></h2><br>
+
+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.
+<br>
+
+<table border="1" cellpadding="3">
+  <tr><td><b>Name</b></td><td><b>License</b></td><td><b>Author(s)</b></td></tr>
+
+  <tr><td>Cx4 emulator</td><td></td><td>anomie, Kris Bleakley, Nach, zsKnight</td></tr>
+  <tr><td>DSP-1 emulator</td><td></td><td>Andreas Naive, John Weidman, Kris Bleakley, neviksti</td></tr>
+  <tr><td>DSP-2 emulator</td><td></td><td>Kris Bleakley</td></tr>
+  <tr><td>DSP-3 emulator</td><td></td><td>John Weidman, Kris Bleakley, Lancer, z80 gaiden</td></tr>
+  <tr><td>DSP-4 emulator</td><td></td><td>Dreamer Nom, John Weidman, Kris Bleakley, Nach, z80 gaiden</td></tr>
+  <tr><td>S-DD1 decompressor</td><td>Public Domain</td><td>Andreas Naive</td></tr>
+  <tr><td>S-DSP emulator</td><td>LGPL 2.1</td><td>Shay Green</td></tr>
+  <tr><td>SPC7110 decompressor</td><td>Public Domain</td><td>neviksti</td></tr>
+  <tr><td>ST-0010 emulator</td><td></td><td>Feather, John Weidman, Kris Bleakley, Matthew Kendora</td></tr>
+
+  <tr><td>Qt toolkit</td><td>LGPL 2.1</td><td>Nokia</td></tr>
+  <tr><td>HQ2x filter</td><td>LGPL 2.1</td><td>MaxST</td></tr>
+  <tr><td>JMA decompressor</td><td>GPL 2</td><td>NSRT team</td></tr>
+  <tr><td>NTSC filter</td><td>LGPL 2.1</td><td>Shay Green</td></tr>
+  <tr><td>zlib decompressor</td><td>zlib license</td><td>zlib team</td></tr>
+</table>
+
+</body>
+</html>

BIN
tools/bsnes/data/logo.png


+ 588 - 0
tools/bsnes/dsp/adsp/adsp.cpp

@@ -0,0 +1,588 @@
+#include <../base.hpp>
+#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() {}

+ 172 - 0
tools/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();
+};

Неке датотеке нису приказане због велике количине промена