From 2a90e129996d5de69c6a3338341edac5784e156a Mon Sep 17 00:00:00 2001
From: Tim Allen <screwtape@froup.com>
Date: Sun, 5 Jun 2011 13:45:04 +1000
Subject: [PATCH] Update to v079 release.

byuu says:

This release includes Nintendo Super System DIP switch emulation and
improved PPU rendering accuracy, among other things.

Changelog:
- added Nintendo Super System DIP switch emulation [requires XML setting
  maps]
- emulated Super Game Boy $6001 VRAM offset selection port [ikari_01]
- fixed randomness initialization of S-SMP port registers [fixes
  DBZ:Hyper Dimension and Ninja Warriors]
- mosaic V-countdown caches BGOFS registers (fixes Super Turrican
  2 effect) [reported by zal16]
- non-mosaic BGOFS registers are always cached at H=60 (fixes NHL '94
  and Super Mario World flickering)
- fixed 2xSaI family of renderers on 64-bit systems
- cleaned up SMP source code
- phoenix: fixed a bug when closing bsnes while minimized

Please note that the mosaic BGOFS fix is only for the accuracy profile.
Unfortunately the older scanline-based compatibility renderer's code is
nearly unmaintainable at this point, so I haven't yet been able to
backport the fixes.

Also, I have written a new cycle-accurate SMP core that does not use
libco. The aim is to implement it into Snes9X v1.54. But it would of
course be prudent to test the new core first.

[...then in the next post...]

Decided to keep that Super Mario World part a surprise, so ... surprise!

Realized while working on the Super Turrican 2 mosaic fix, and from
looking at NHL '94 and Dai Kaijuu Monogatari 2's behavior, that BGOFS
registers must be cached between H=0 and H=88 for the entire scanline
... they can't work otherwise, and it'd be stupid for the PPU to re-add
the offset to the position on every pixel anyway. I chose H=60 for now.
Once I am set up with the RGB monitor and the North American cartridge
dumping is completed, I'll set it on getting exact timings for all these
things. It'll probably require a smallish speed hit to allow exact-cycle
timing events for everything in the PPU.
---
 bsnes/data/cheats.xml                         | 517 +++++++++++++-----
 bsnes/nall/directory.hpp                      |   9 +-
 bsnes/nall/file.hpp                           |  71 +--
 bsnes/nall/platform.hpp                       |  15 +-
 bsnes/nall/string/cast.hpp                    |   6 +-
 bsnes/phoenix/core/core.cpp                   |   4 +
 bsnes/phoenix/core/core.hpp                   |  13 +
 bsnes/phoenix/core/layout/vertical-layout.cpp |   4 -
 bsnes/phoenix/core/layout/vertical-layout.hpp |   1 -
 bsnes/phoenix/core/state.hpp                  |  10 +
 bsnes/phoenix/gtk/gtk.cpp                     |  28 +-
 bsnes/phoenix/gtk/gtk.hpp                     |  10 +
 bsnes/phoenix/gtk/timer.cpp                   |  24 +
 bsnes/phoenix/phoenix.cpp                     |   9 +-
 bsnes/phoenix/phoenix.hpp                     |   5 +
 bsnes/phoenix/qt/qt.cpp                       |   1 +
 bsnes/phoenix/qt/qt.moc                       |  63 ++-
 bsnes/phoenix/qt/qt.moc.hpp                   |  17 +
 bsnes/phoenix/qt/timer.cpp                    |  21 +
 bsnes/phoenix/reference/font.cpp              |   4 +
 bsnes/phoenix/reference/reference.cpp         |   2 +
 bsnes/phoenix/reference/reference.hpp         |  23 +
 bsnes/phoenix/reference/timer.cpp             |   8 +
 bsnes/phoenix/reference/widget/canvas.cpp     |   9 +
 bsnes/phoenix/reference/widget/widget.cpp     |   8 +
 bsnes/phoenix/windows/timer.cpp               |  31 ++
 bsnes/phoenix/windows/window.cpp              |   7 +-
 bsnes/phoenix/windows/windows.cpp             |   1 +
 bsnes/phoenix/windows/windows.hpp             |  11 +
 bsnes/snes/ppu/background/background.cpp      |  35 +-
 bsnes/snes/ppu/background/background.hpp      |   9 +-
 bsnes/snes/ppu/background/mode7.cpp           |  10 +-
 bsnes/snes/ppu/ppu.cpp                        |   4 +
 bsnes/snes/ppu/serialization.cpp              |   5 +-
 bsnes/snes/snes.hpp                           |   2 +-
 snesfilter/2xSaI/2xSaI.cpp                    |   2 +-
 snesfilter/2xSaI/Super-2xSaI.cpp              |   2 +-
 snesfilter/2xSaI/Super-Eagle.cpp              |   2 +-
 38 files changed, 784 insertions(+), 219 deletions(-)
 create mode 100755 bsnes/phoenix/gtk/timer.cpp
 create mode 100755 bsnes/phoenix/qt/timer.cpp
 create mode 100755 bsnes/phoenix/reference/timer.cpp
 create mode 100755 bsnes/phoenix/reference/widget/canvas.cpp
 create mode 100755 bsnes/phoenix/windows/timer.cpp

diff --git a/bsnes/data/cheats.xml b/bsnes/data/cheats.xml
index 949d894e..b49542f4 100755
--- a/bsnes/data/cheats.xml
+++ b/bsnes/data/cheats.xml
@@ -1673,6 +1673,10 @@
     </cheat>
     <cheat>
       <description>Have Fezi-copter</description>
+      <code>6DAC-6FDD</code>
+    </cheat>
+    <cheat>
+      <description>Have Fezi-copter (alt)</description>
       <code>7E0064FF</code>
     </cheat>
     <cheat>
@@ -3535,7 +3539,6 @@
     <cheat>
       <description>Invincibility</description>
       <code>2D87-AD04</code>
-      <code>EDA6-AFDD</code>
     </cheat>
     <cheat>
       <description>Infinite lives</description>
@@ -5042,6 +5045,10 @@
   </cartridge>
   <cartridge sha256="de1de85ad549a6aaf0431cceb47cbd07e1f6e81f9e16fd62575305e2c1f06240">
     <name>BioMetal (USA)</name>
+    <cheat>
+      <description>Invincibility (blinking)</description>
+      <code>406D-DDD2</code>
+    </cheat>
     <cheat>
       <description>Infinite lives</description>
       <code>C26E-6D02</code>
@@ -9409,6 +9416,14 @@
   </cartridge>
   <cartridge sha256="7c722f9941957630467c1784d0eb3f92fbfc0a2a1da3c8f5c27f593eca2a5a04">
     <name>Cutthroat Island (USA)</name>
+    <cheat>
+      <description>Stage select menu after character select screen</description>
+      <code>3C6B-479E</code>
+    </cheat>
+    <cheat>
+      <description>Stage select menu after character select screen (alt)</description>
+      <code>878C9DEA</code>
+    </cheat>
     <cheat>
       <description>Infinite health - P1</description>
       <code>7E09F428</code>
@@ -17330,6 +17345,18 @@
       <description>Infinite time</description>
       <code>A2C0-A7D0</code>
     </cheat>
+    <cheat>
+      <description>Hit anywhere</description>
+      <code>3CA4-0FD0</code>
+      <code>40AD-04A0</code>
+      <code>7DA4-0F00</code>
+      <code>89A4-0D60</code>
+      <code>F9A4-0DA0</code>
+    </cheat>
+    <cheat>
+      <description>Enemies never get knocked down, attack until they die</description>
+      <code>7E0D7000</code>
+    </cheat>
     <cheat>
       <description>Slower timer</description>
       <code>D4C9-AFD0</code>
@@ -17338,10 +17365,6 @@
       <description>Faster timer</description>
       <code>DDC9-AFD0</code>
     </cheat>
-    <cheat>
-      <description>Enemies never get knocked down, hit them until they die</description>
-      <code>7E0D7000</code>
-    </cheat>
     <cheat>
       <description>Enemy 1 has no health</description>
       <code>7E101400</code>
@@ -24315,6 +24338,19 @@
       <description>Infinite Arrows</description>
       <code>17E5-22E6</code>
     </cheat>
+    <cheat>
+      <description>Hit anywhere (disable before fighting Ganon, use the Boomerang instead of Sword to hit switches)</description>
+      <code>40E2-6D96</code>
+      <code>6D3E-A7FC</code>
+      <code>403F-DD28</code>
+      <code>40EC-AF26</code>
+      <code>6D34-D7F8</code>
+      <code>4068-A0E6</code>
+    </cheat>
+    <cheat>
+      <description>Get items from anywhere</description>
+      <code>402F-07B6</code>
+    </cheat>
     <cheat>
       <description>Always get Faerie at the Pond Of Happiness (as if you threw in 100 rupees)</description>
       <code>7EF36A64</code>
@@ -24336,7 +24372,7 @@
       <code>DDB5-049E</code>
     </cheat>
     <cheat>
-      <description>Enemies that normally drop items will drop them 100% of the time</description>
+      <description>100% enemy drop rate (from enemies that normally drop items)</description>
       <code>DDE8-142C</code>
     </cheat>
     <cheat>
@@ -30756,6 +30792,10 @@
       <description>One hit kills</description>
       <code>40DE-7FD2</code>
     </cheat>
+    <cheat>
+      <description>Hit anywhere</description>
+      <code>6DD8-7DD2</code>
+    </cheat>
     <cheat>
       <description>Multi-jump</description>
       <code>1DFC-E400</code>
@@ -30805,6 +30845,10 @@
       <description>One hit kills (most enemies)</description>
       <code>6DB5-CD97</code>
     </cheat>
+    <cheat>
+      <description>Hit anywhere</description>
+      <code>40B1-34F4</code>
+    </cheat>
     <cheat>
       <description>Immune to drain attack</description>
       <code>C2AD-4401</code>
@@ -30890,10 +30934,6 @@
       <description>Infinite lives (alt)</description>
       <code>7E1F8009</code>
     </cheat>
-    <cheat>
-      <description>Start with all weapons and all enemies defeated (except Sigma)</description>
-      <code>23BD-3F07</code>
-    </cheat>
     <cheat>
       <description>Infinite weapons once obtained</description>
       <code>C9B3-4769</code>
@@ -30902,6 +30942,10 @@
       <description>One hit kills (most enemies)</description>
       <code>6DB5-CD97</code>
     </cheat>
+    <cheat>
+      <description>Hit anywhere</description>
+      <code>40B1-34F4</code>
+    </cheat>
     <cheat>
       <description>Have all equipment</description>
       <code>7E1F99FF</code>
@@ -30944,6 +30988,10 @@
       <description>Weapon charges to 1st power level faster</description>
       <code>DDB1-4F61</code>
     </cheat>
+    <cheat>
+      <description>Start with all weapons and all enemies defeated (except Sigma)</description>
+      <code>23BD-3F07</code>
+    </cheat>
     <cheat>
       <description>Start with less health</description>
       <code>D6BE-47AF</code>
@@ -31063,6 +31111,12 @@
       <description>Have sub-tank 4 full</description>
       <code>7E1FB9FF</code>
     </cheat>
+    <cheat>
+      <description>Hit anywhere</description>
+      <code>4028-A73F</code>
+      <code>C222-6734</code>
+      <code>D4E7-DDFB</code>
+    </cheat>
     <cheat>
       <description>One hit kills</description>
       <code>6D2D-AF14</code>
@@ -31084,10 +31138,6 @@
       <description>Infinite health</description>
       <code>C2AD-6FF7</code>
     </cheat>
-    <cheat>
-      <description>Start with max health bar</description>
-      <code>F02A-ADF4</code>
-    </cheat>
     <cheat>
       <description>Infinite lives</description>
       <code>7E1FB409</code>
@@ -31108,14 +31158,14 @@
       <code>82CB-0D2F</code>
       <code>82CA-0F2F</code>
     </cheat>
-    <cheat>
-      <description>Always Super Shot</description>
-      <code>7E0A6702</code>
-    </cheat>
     <cheat>
       <description>Infinite Air Dash</description>
       <code>7E0A3478</code>
     </cheat>
+    <cheat>
+      <description>Always have Super Shot</description>
+      <code>7E0A6702</code>
+    </cheat>
     <cheat>
       <description>Have Zero-Saber</description>
       <code>7E1FB2FC</code>
@@ -31129,8 +31179,10 @@
       <code>7E0CF801</code>
     </cheat>
     <cheat>
-      <description>Skip bosses at start of game and proceed to stage select</description>
-      <code>B933-DF6C</code>
+      <description>Hit anywhere</description>
+      <code>40AC-A7B4</code>
+      <code>6DA0-ADF7</code>
+      <code>6DAF-AD97</code>
     </cheat>
     <cheat>
       <description>Multi-jump</description>
@@ -31150,6 +31202,14 @@
       <description>Ultra mega-jump</description>
       <code>D886-6F26</code>
     </cheat>
+    <cheat>
+      <description>Skip bosses at start of game and proceed to stage select</description>
+      <code>B933-DF6C</code>
+    </cheat>
+    <cheat>
+      <description>Start with max health bar</description>
+      <code>F02A-ADF4</code>
+    </cheat>
   </cartridge>
   <cartridge sha256="d4f2cb6b209db29f7aec62e5a23846681c14665fb007e94d7bcfc7b5611e938b">
     <name>Metal Combat - Falcon's Revenge (USA)</name>
@@ -31984,6 +32044,13 @@
       <description>First strike of any kind wins round</description>
       <code>DDBC-370F</code>
     </cheat>
+    <cheat>
+      <description>Hit anywhere - P1</description>
+      <code>3DBF-3D0F</code>
+      <code>27BF-3D6F</code>
+      <code>F6BF-3DAF</code>
+      <code>74BF-3F0F</code>
+    </cheat>
     <cheat>
       <description>All throws do more damage</description>
       <code>56B9-4DAD</code>
@@ -32507,13 +32574,37 @@
       <code>C239-CD62</code>
     </cheat>
     <cheat>
-      <description>First round / one button fatalities</description>
+      <description>First round and one button fatalities</description>
       <code>7E3AF001</code>
     </cheat>
     <cheat>
       <description>Infinite continues</description>
       <code>A220-3FB6</code>
     </cheat>
+    <cheat>
+      <description>P1 takes all damage</description>
+      <code>DD37-CF62</code>
+    </cheat>
+    <cheat>
+      <description>P2 takes all damage</description>
+      <code>6D37-CF02</code>
+    </cheat>
+    <cheat>
+      <description>Hit anywhere - P1</description>
+      <code>0AE6-3F0E</code>
+      <code>0AE6-340E</code>
+      <code>0AE6-34DE</code>
+      <code>39E6-346E</code>
+      <code>39E6-3FAE</code>
+      <code>3DE6-3DDE</code>
+      <code>6D30-1462</code>
+      <code>C4E6-3F6E</code>
+      <code>D4E6-3D0E</code>
+      <code>DDE6-3D6E</code>
+      <code>EDE6-3DAE</code>
+      <code>D7E6-3FDE</code>
+      <code>EE30-14A2</code>
+    </cheat>
     <cheat>
       <description>Press A on main menu for Sound Test</description>
       <code>D42E-44D8</code>
@@ -32530,14 +32621,6 @@
       <description>Press X on main menu for Scott's Menu</description>
       <code>D42B-1FD8</code>
     </cheat>
-    <cheat>
-      <description>P1 takes all damage</description>
-      <code>DD37-CF62</code>
-    </cheat>
-    <cheat>
-      <description>P2 takes all damage</description>
-      <code>6D37-CF02</code>
-    </cheat>
     <cheat>
       <description>Always fight Kano</description>
       <code>CE8F-3FB7</code>
@@ -32710,6 +32793,10 @@
       <description>Infinite time</description>
       <code>6DC7-1DAA</code>
     </cheat>
+    <cheat>
+      <description>Infinite continues</description>
+      <code>C2C4-47AA</code>
+    </cheat>
     <cheat>
       <description>No health - P1</description>
       <code>DDB1-1FF7</code>
@@ -32719,12 +32806,15 @@
       <code>DDB5-1FF7</code>
     </cheat>
     <cheat>
-      <description>Have 127x more fatality time</description>
-      <code>5EC2-CF02</code>
+      <description>Hit anywhere - P1</description>
+      <code>0DB0-4F97</code>
+      <code>2DB0-4FF7</code>
+      <code>3DB0-4D97</code>
+      <code>DDB0-4DB7</code>
     </cheat>
     <cheat>
-      <description>Infinite continues</description>
-      <code>C2C4-47AA</code>
+      <description>Have 127x more fatality time</description>
+      <code>5EC2-CF02</code>
     </cheat>
     <cheat>
       <description>Disable throws - 2P mode</description>
@@ -44004,6 +44094,13 @@
       <code>DD80-7FAD</code>
       <code>D580-74DD</code>
     </cheat>
+    <cheat>
+      <description>Hit anywhere (except projectiles) - P1</description>
+      <code>3DC7-8D0D</code>
+      <code>6DC7-8D6D</code>
+      <code>BDC7-8FDD</code>
+      <code>DBC7-8DAD</code>
+    </cheat>
   </cartridge>
   <cartridge sha256="2b34161e96ef3f0f48cecd67e531a9bb94310652d8686f301bac426e4ab97e77">
     <name>Street Fighter II (USA)</name>
@@ -44017,6 +44114,13 @@
       <description>Win 1 bout to win the match instead of 2 out of 3 (disable before fighting M. Bison)</description>
       <code>DF80-AD64</code>
     </cheat>
+    <cheat>
+      <description>Hit anywhere (except projectiles) - P1</description>
+      <code>3D29-A4A7</code>
+      <code>8D29-A767</code>
+      <code>DD29-A7D7</code>
+      <code>D329-A707</code>
+    </cheat>
     <cheat>
       <description>Dizziness wears off very quickly</description>
       <code>EDBE-0F09</code>
@@ -44389,6 +44493,13 @@
       <description>Infinite time (alt)</description>
       <code>7E18F399</code>
     </cheat>
+    <cheat>
+      <description>Hit anywhere (except projectiles) - P1</description>
+      <code>3D98-8704</code>
+      <code>8D9A-8DD4</code>
+      <code>DD98-8764</code>
+      <code>D598-87A4</code>
+    </cheat>
     <cheat>
       <description>Select same character - both players</description>
       <code>7E184820</code>
@@ -46655,19 +46766,19 @@
   <cartridge sha256="bcced1be76ef920b562a555696bcb4583d1c8cea4d4b057cab6e0e09be8ef8c4">
     <name>Super Double Dragon (USA)</name>
     <cheat>
-      <description>Invincibility</description>
-      <code>C267-0DD6</code>
-    </cheat>
-    <cheat>
-      <description>Infinite health</description>
+      <description>Invincibility - both players</description>
       <code>1D6F-0766</code>
     </cheat>
     <cheat>
-      <description>Infinite lives</description>
-      <code>C286-6F05</code>
+      <description>Invincibility - P1</description>
+      <code>C267-0DD6</code>
     </cheat>
     <cheat>
       <description>Infinite lives - P1</description>
+      <code>C286-6F05</code>
+    </cheat>
+    <cheat>
+      <description>Infinite lives - P1 (alt)</description>
       <code>4A86-6F05</code>
     </cheat>
     <cheat>
@@ -46706,6 +46817,30 @@
       <description>1 life - 2P game A</description>
       <code>DF88-0D6B</code>
     </cheat>
+    <cheat>
+      <description>Start on Mission 2 (enable on Mode Seclect screen, then disable)</description>
+      <code>7E001C14</code>
+    </cheat>
+    <cheat>
+      <description>Start on Mission 3 (enable on Mode Seclect screen, then disable)</description>
+      <code>7E001C17</code>
+    </cheat>
+    <cheat>
+      <description>Start on Mission 4 (enable on Mode Seclect screen, then disable)</description>
+      <code>7E001C1C</code>
+    </cheat>
+    <cheat>
+      <description>Start on Mission 5 (enable on Mode Seclect screen, then disable)</description>
+      <code>7E001C1D</code>
+    </cheat>
+    <cheat>
+      <description>Start on Mission 6 (enable on Mode Seclect screen, then disable)</description>
+      <code>7E001C1F</code>
+    </cheat>
+    <cheat>
+      <description>Start on Mission 7 (enable on Mode Seclect screen, then disable)</description>
+      <code>7E001C20</code>
+    </cheat>
   </cartridge>
   <cartridge sha256="7468c271d7240cf4e0d08c16e9969a1b1b1caf5adc0e5adc568d93c92651a057">
     <name>Super Ghouls'n Ghosts (USA)</name>
@@ -46896,6 +47031,11 @@
     <name>Super Mario All-Stars (USA)</name>
     <cheat>
       <description>(SMB) Invincibility</description>
+      <code>62E7-A7D2</code>
+      <code>2DE7-A7A2</code>
+    </cheat>
+    <cheat>
+      <description>(SMB) Invincibility (Starman effect)</description>
       <code>292B-67DE</code>
     </cheat>
     <cheat>
@@ -46923,16 +47063,27 @@
       <code>6D84-DF03</code>
     </cheat>
     <cheat>
-      <description>(SMB) Play as Big Mario</description>
-      <code>7E07AE05</code>
-    </cheat>
-    <cheat>
-      <description>(SMB) Play as Fire Mario</description>
-      <code>23C2BB03</code>
+      <description>(SMB) Always Fiery Mario after first hit</description>
+      <code>CB29-AF0E</code>
+      <code>D429-AF6E</code>
+      <code>CB8B-676A</code>
+      <code>DD8B-67AA</code>
     </cheat>
     <cheat>
       <description>(SMB) Multi-jump</description>
-      <code>23C2BB02</code>
+      <code>2D8E-D7D2</code>
+    </cheat>
+    <cheat>
+      <description>(SMB) Fireballs hit anywhere</description>
+      <code>4028-D4DE</code>
+    </cheat>
+    <cheat>
+      <description>(SMB) Fireballs can kill Bullet Bill</description>
+      <code>4025-07AE</code>
+    </cheat>
+    <cheat>
+      <description>(SMB) Fireballs can kill Buzzy Beetle</description>
+      <code>6D2E-DD6E</code>
     </cheat>
     <cheat>
       <description>(SMB) Run without holding the dash button</description>
@@ -47022,13 +47173,21 @@
       <code>7E00853B</code>
     </cheat>
     <cheat>
-      <description>(SMB2) Always big</description>
+      <description>(SMB2) Infinite hearts</description>
+      <code>DD32-6966</code>
+    </cheat>
+    <cheat>
+      <description>(SMB2) Always big Mario</description>
       <code>7E04C31F</code>
     </cheat>
     <cheat>
-      <description>(SMB2) Always small</description>
+      <description>(SMB2) Always small Mario</description>
       <code>7E04C30F</code>
     </cheat>
+    <cheat>
+      <description>(SMB2) Infinite lives</description>
+      <code>C26E-D5A6</code>
+    </cheat>
     <cheat>
       <description>(SMB2) Multi-jump - all characters</description>
       <code>D966-6166</code>
@@ -47061,10 +47220,6 @@
       <description>(SMB2) 99 lives after continue</description>
       <code>1761-05D0</code>
     </cheat>
-    <cheat>
-      <description>(SMB2) Infinite lives</description>
-      <code>C26E-D5A6</code>
-    </cheat>
     <cheat>
       <description>(SMB2) Continue with 3 hearts instead of 2</description>
       <code>DF6B-A9A1</code>
@@ -47073,10 +47228,6 @@
       <description>(SMB2) Continue with 4 hearts</description>
       <code>D46B-A9A1</code>
     </cheat>
-    <cheat>
-      <description>(SMB2) Never lose hearts</description>
-      <code>DD32-6966</code>
-    </cheat>
     <cheat>
       <description>(SMB2) Jumping in place charges super jump</description>
       <code>7A60-A966</code>
@@ -47097,38 +47248,55 @@
       <description>(SMB3) Invincibility (Starman)</description>
       <code>7E0553FF</code>
     </cheat>
+    <cheat>
+      <description>(SMB3) Infinite lives</description>
+      <code>82BB-0C6D</code>
+    </cheat>
     <cheat>
       <description>(SMB3) Infinite time</description>
       <code>6D3D-6619</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Small Mario</description>
+      <description>(SMB3) Always Small Mario</description>
       <code>7E00BB00</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Big Mario</description>
+      <description>(SMB3) Always Big Mario</description>
       <code>7E00BB01</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Fire Mario</description>
+      <description>(SMB3) Always Fire Mario</description>
       <code>7E00BB02</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Raccoon Mario</description>
+      <description>(SMB3) Always Raccoon Mario</description>
       <code>7E00BB03</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Frog Mario</description>
+      <description>(SMB3) Always Frog Mario</description>
       <code>7E00BB04</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Tanooki Mario</description>
+      <description>(SMB3) Always Tanooki Mario</description>
       <code>7E00BB05</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Hammer Bros. Mario</description>
+      <description>(SMB3) Always Hammer Bros. Mario</description>
       <code>7E00BB06</code>
     </cheat>
+    <cheat>
+      <description>(SMB3) Fireballs hit anywhere</description>
+      <code>4083-D8F3</code>
+      <code>408D-08F3</code>
+    </cheat>
+    <cheat>
+      <description>(SMB3) Tail hits anywhere</description>
+      <code>40C4-6C22</code>
+    </cheat>
+    <cheat>
+      <description>(SMB3) Fireballs can kill most enemies</description>
+      <code>4084-0BB3</code>
+    </cheat>
     <cheat>
       <description>(SMB3) Have Magic Whistle</description>
       <code>7E1D800C</code>
@@ -47146,7 +47314,7 @@
       <code>DDAF-A8A3</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Fly for an unlimited amount of time</description>
+      <description>(SMB3) Infinite flying time</description>
       <code>EEA4-AB63</code>
     </cheat>
     <cheat>
@@ -47216,10 +47384,6 @@
       <description>(SMB3) After getting star, invincible until end of level (may have to disable to jump)</description>
       <code>C23B-680D</code>
     </cheat>
-    <cheat>
-      <description>(SMB3) Infinite lives</description>
-      <code>82BB-0C6D</code>
-    </cheat>
     <cheat>
       <description>(SMB3) 1 life after continue</description>
       <code>DFBB-DBAF</code>
@@ -47289,13 +47453,13 @@
     <name>Super Mario All-Stars + Super Mario World (USA)</name>
     <cheat>
       <description>(SMB) Invincibility</description>
-      <code>292B-67DE</code>
-    </cheat>
-    <cheat>
-      <description>(SMB) Invincibility (alt)</description>
       <code>62E7-A7D2</code>
       <code>2DE7-A7A2</code>
     </cheat>
+    <cheat>
+      <description>(SMB) Invincibility (Starman effect)</description>
+      <code>292B-67DE</code>
+    </cheat>
     <cheat>
       <description>(SMB) Invincibility (Starman)</description>
       <code>7E07AF0F</code>
@@ -47305,21 +47469,28 @@
       <code>7E075A05</code>
     </cheat>
     <cheat>
-      <description>(SMB) Play as Big Mario</description>
-      <code>7E07AE05</code>
-    </cheat>
-    <cheat>
-      <description>(SMB) Play as Big Mario (alt)</description>
-      <code>23C2BB02</code>
-    </cheat>
-    <cheat>
-      <description>(SMB) Play as Fire Mario</description>
-      <code>23C2BB03</code>
+      <description>(SMB) Always Fiery Mario after first hit</description>
+      <code>CB29-AF0E</code>
+      <code>D429-AF6E</code>
+      <code>CB8B-676A</code>
+      <code>DD8B-67AA</code>
     </cheat>
     <cheat>
       <description>(SMB) Multi-jump</description>
       <code>2D8E-D7D2</code>
     </cheat>
+    <cheat>
+      <description>(SMB) Fireballs hit anywhere</description>
+      <code>4028-D4DE</code>
+    </cheat>
+    <cheat>
+      <description>(SMB) Fireballs can kill Bullet Bill</description>
+      <code>4025-07AE</code>
+    </cheat>
+    <cheat>
+      <description>(SMB) Fireballs can kill Buzzy Beetle</description>
+      <code>6D2E-DD6E</code>
+    </cheat>
     <cheat>
       <description>(SMB) Run without holding the dash button</description>
       <code>DD8E-D702</code>
@@ -47341,17 +47512,9 @@
       <code>C26E-D5A6</code>
     </cheat>
     <cheat>
-      <description>(SMB2) Never lose hearts</description>
+      <description>(SMB2) Infinite hearts</description>
       <code>DD32-6966</code>
     </cheat>
-    <cheat>
-      <description>(SMB2) Always Big</description>
-      <code>7E04C31F</code>
-    </cheat>
-    <cheat>
-      <description>(SMB2) Always Small</description>
-      <code>7E04C30F</code>
-    </cheat>
     <cheat>
       <description>(SMB2) Multi-jump - all characters</description>
       <code>D966-6166</code>
@@ -47381,33 +47544,37 @@
       <code>7E0553FF</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Small Mario</description>
+      <description>(SMB3) Always Small Mario</description>
       <code>7E00BB00</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Big Mario</description>
+      <description>(SMB3) Always Big Mario</description>
       <code>7E00BB01</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Fire Mario</description>
+      <description>(SMB3) Always Fiery Mario</description>
       <code>7E00BB02</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Raccoon Mario</description>
+      <description>(SMB3) Always Raccoon Mario</description>
       <code>7E00BB03</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Frog Mario</description>
+      <description>(SMB3) Always Frog Mario</description>
       <code>7E00BB04</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Tanooki Mario</description>
+      <description>(SMB3) Always Tanooki Mario</description>
       <code>7E00BB05</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Play as Hammer Bros Mario</description>
+      <description>(SMB3) Always Hammer Bros Mario</description>
       <code>7E00BB06</code>
     </cheat>
+    <cheat>
+      <description>(SMB3) Infinite flying time</description>
+      <code>23CB26FF</code>
+    </cheat>
     <cheat>
       <description>(SMB3) Have Magic Whistle</description>
       <code>7E1D800C</code>
@@ -47417,12 +47584,17 @@
       <code>7E056EFF</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Fly at anytime (run meter always full)</description>
-      <code>23CB1F00</code>
+      <description>(SMB3) Fireballs hit anywhere</description>
+      <code>4083-D8F3</code>
+      <code>408D-08F3</code>
     </cheat>
     <cheat>
-      <description>(SMB3) Fly for an unlimited amount of time</description>
-      <code>23CB26FF</code>
+      <description>(SMB3) Tail hits anywhere</description>
+      <code>40C4-6C22</code>
+    </cheat>
+    <cheat>
+      <description>(SMB3) Fireballs can kill most enemies</description>
+      <code>4084-0BB3</code>
     </cheat>
     <cheat>
       <description>(SMW) Invincibility</description>
@@ -47445,9 +47617,13 @@
       <code>7E001902</code>
     </cheat>
     <cheat>
-      <description>(SMW) Always Fire Mario</description>
+      <description>(SMW) Always Fiery Mario</description>
       <code>7E001903</code>
     </cheat>
+    <cheat>
+      <description>(SMW) Always have Yoshi</description>
+      <code>7E0DC101</code>
+    </cheat>
     <cheat>
       <description>(SMW) Infinite time</description>
       <code>7E0F3109</code>
@@ -47458,10 +47634,6 @@
       <description>(SMW) Infinite P-Balloon time</description>
       <code>7E1891FF</code>
     </cheat>
-    <cheat>
-      <description>(SMW) Always have Yoshi</description>
-      <code>7E0DC101</code>
-    </cheat>
     <cheat>
       <description>(SMW) Infinite flying time for Yoshi</description>
       <code>C2EC-0700</code>
@@ -47892,6 +48064,10 @@
       <code>89E4-AFD9</code>
       <code>89C6-D4DB</code>
     </cheat>
+    <cheat>
+      <description>Infinite lives</description>
+      <code>C222-D4DD</code>
+    </cheat>
     <cheat>
       <description>Infinite flying time for Yoshi</description>
       <code>C2EC-0700</code>
@@ -47905,8 +48081,24 @@
       <code>7E1891FF</code>
     </cheat>
     <cheat>
-      <description>Infinite lives</description>
-      <code>C222-D4DD</code>
+      <description>Always Small Mario</description>
+      <code>7E001900</code>
+    </cheat>
+    <cheat>
+      <description>Always Big Mario</description>
+      <code>7E001901</code>
+    </cheat>
+    <cheat>
+      <description>Always Cape Mario</description>
+      <code>7E001902</code>
+    </cheat>
+    <cheat>
+      <description>Always Fiery Mario</description>
+      <code>7E001903</code>
+    </cheat>
+    <cheat>
+      <description>Always have Yoshi</description>
+      <code>7E0DC101</code>
     </cheat>
     <cheat>
       <description>Multi-jump</description>
@@ -47921,6 +48113,31 @@
       <code>F53F-6767</code>
       <code>DD3F-67A7</code>
     </cheat>
+    <cheat>
+      <description>Cape hit anywhere</description>
+      <code>40BE-AD66</code>
+    </cheat>
+    <cheat>
+      <description>Fireballs hits anywhere</description>
+      <code>40C2-D7A6</code>
+    </cheat>
+    <cheat>
+      <description>Fireballs shoot straight</description>
+      <code>DDEA-6F07</code>
+      <code>DDB2-AFD8</code>
+    </cheat>
+    <cheat>
+      <description>Fireballs turn most enemies into Flowers</description>
+      <code>59C4-0466</code>
+    </cheat>
+    <cheat>
+      <description>Fireballs turn most enemies into 1-Up Mushrooms</description>
+      <code>56C4-0466</code>
+    </cheat>
+    <cheat>
+      <description>Fireballs turn most enemies into Starmen</description>
+      <code>51C4-0466</code>
+    </cheat>
     <cheat>
       <description>Low-jump</description>
       <code>D02C-AF6F</code>
@@ -47933,26 +48150,6 @@
       <description>Mega-jump</description>
       <code>DF2C-AF6F</code>
     </cheat>
-    <cheat>
-      <description>Always Small Mario</description>
-      <code>7E001900</code>
-    </cheat>
-    <cheat>
-      <description>Always Big Mario</description>
-      <code>7E001901</code>
-    </cheat>
-    <cheat>
-      <description>Always Cape Mario</description>
-      <code>7E001902</code>
-    </cheat>
-    <cheat>
-      <description>Always Fire Mario</description>
-      <code>7E001903</code>
-    </cheat>
-    <cheat>
-      <description>Always have Yoshi</description>
-      <code>7E0DC101</code>
-    </cheat>
     <cheat>
       <description>Little Yoshi grows after eating 1 enemy instead of 5</description>
       <code>DFCE-64A0</code>
@@ -47969,11 +48166,6 @@
       <description>Little Yoshi grows after eating 4 enemies</description>
       <code>D0CE-64A0</code>
     </cheat>
-    <cheat>
-      <description>Shoot fireballs straight</description>
-      <code>DDEA-6F07</code>
-      <code>DDB2-AFD8</code>
-    </cheat>
     <cheat>
       <description>Activate green blocks (disable before entering the Green Switch Palace)</description>
       <code>7E1F2701</code>
@@ -48477,6 +48669,11 @@
       <description>Super-jumps don't drain energy</description>
       <code>C22A-456D</code>
     </cheat>
+    <cheat>
+      <description>Enemies die on contact (Speed Booster effect)</description>
+      <code>6DC8-4CDF</code>
+      <code>6DCC-4BDF</code>
+    </cheat>
     <cheat>
       <description>Kill most enemies on contact (prevents bomb-bouncing)</description>
       <code>7E0A6E0F</code>
@@ -50434,6 +50631,13 @@
       <description>Instant win - P1</description>
       <code>7E0771FF</code>
     </cheat>
+    <cheat>
+      <description>Hit anywhere (except projectiles) - P1</description>
+      <code>3D1D-5DDF</code>
+      <code>8D1D-5DAF</code>
+      <code>0D1D-5D0F</code>
+      <code>D51D-5D6F</code>
+    </cheat>
     <cheat>
       <description>No charging required for some special moves</description>
       <code>D002-EDD5</code>
@@ -54788,13 +54992,29 @@
       <code>7D7B-FE7C</code>
     </cheat>
     <cheat>
-      <description>First round / one button fatalities</description>
+      <description>First round and one button fatalities</description>
       <code>7E3BE301</code>
     </cheat>
     <cheat>
       <description>Max fatality time</description>
       <code>DF73-2A7C</code>
     </cheat>
+    <cheat>
+      <description>Hit anywhere - P1</description>
+      <code>0AEB-3F97</code>
+      <code>0AEB-34F7</code>
+      <code>31EB-34B7</code>
+      <code>35EB-3F27</code>
+      <code>3DEB-3DF7</code>
+      <code>46EB-3FB7</code>
+      <code>84EB-3497</code>
+      <code>BD3C-C49F</code>
+      <code>D4EB-3D97</code>
+      <code>DDEB-3DB7</code>
+      <code>D7EB-3FF7</code>
+      <code>EDEB-3D27</code>
+      <code>EE3C-C4BF</code>
+    </cheat>
     <cheat>
       <description>Blank versus screen</description>
       <code>EE37-CF02</code>
@@ -55467,6 +55687,21 @@
       <code>7E1B8963</code>
     </cheat>
   </cartridge>
+  <cartridge sha256="8b3ff3a0ca1c85facb71f42886291dfa916ca74245702c79ca8ae635f28bc864">
+    <name>Ushio to Tora (Japan)</name>
+    <cheat>
+      <description>Invincibility</description>
+      <code>7E022226</code>
+    </cheat>
+    <cheat>
+      <description>Infinite health</description>
+      <code>7E021E40</code>
+    </cheat>
+    <cheat>
+      <description>Infinite lives</description>
+      <code>7E060863</code>
+    </cheat>
+  </cartridge>
   <cartridge sha256="2500d6c846c78bcb729f15535bf2b852a120411891cabaaaa6fc407906d0214e">
     <name>Utopia - The Creation of a Nation (USA)</name>
     <cheat>
@@ -57692,15 +57927,15 @@
     </cheat>
   </cartridge>
   <cartridge sha256="25414de02c6805ca62574cfb39c23bf292b3d8c4ff33eb8f212ccdbcd61c5ae3">
-    <name>Zool - Ninja of the Nth Dimension (USA)</name>
+    <name>Zool - Ninja of the 'Nth' Dimension (USA)</name>
+    <cheat>
+      <description>Invincibility</description>
+      <code>7E05DCFF</code>
+    </cheat>
     <cheat>
       <description>Invincibility (blinking)</description>
       <code>7E1D8B49</code>
     </cheat>
-    <cheat>
-      <description>Invincibility (not blinking)</description>
-      <code>7E05DCFF</code>
-    </cheat>
     <cheat>
       <description>Infinite health</description>
       <code>7E1CF1FA</code>
diff --git a/bsnes/nall/directory.hpp b/bsnes/nall/directory.hpp
index c4f94c9a..4d3bcba5 100755
--- a/bsnes/nall/directory.hpp
+++ b/bsnes/nall/directory.hpp
@@ -42,20 +42,21 @@ struct directory {
       if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
         if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
           string name = (const char*)utf8_t(data.cFileName);
-          if(wildcard(name, pattern)) list.append(string(name, "/"));
+          if(wildcard(name, pattern)) list.append(name);
         }
       }
       while(FindNextFile(handle, &data) != false) {
         if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
           if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
             string name = (const char*)utf8_t(data.cFileName);
-            if(wildcard(name, pattern)) list.append(string(name, "/"));
+            if(wildcard(name, pattern)) list.append(name);
           }
         }
       }
       FindClose(handle);
     }
     if(list.size() > 0) sort(&list[0], list.size());
+    foreach(name, list) name.append("/");  //must append after sorting
     return list;
   }
 
@@ -109,14 +110,14 @@ struct directory {
         if(!strcmp(ep->d_name, ".")) continue;
         if(!strcmp(ep->d_name, "..")) continue;
         if(ep->d_type & DT_DIR) {
-          if(wildcard(ep->d_name, pattern)) list.append(string(ep->d_name, "/"));
+          if(wildcard(ep->d_name, pattern)) list.append(ep->d_name);
         }
       }
       closedir(dp);
     }
     if(list.size() > 0) sort(&list[0], list.size());
+    foreach(name, list) name.append("/");  //must append after sorting
     return list;
-
   }
 
   inline lstring directory::files(const string &pathname, const string &pattern) {
diff --git a/bsnes/nall/file.hpp b/bsnes/nall/file.hpp
index 103c7d4a..8c4ef894 100755
--- a/bsnes/nall/file.hpp
+++ b/bsnes/nall/file.hpp
@@ -1,15 +1,7 @@
 #ifndef NALL_FILE_HPP
 #define NALL_FILE_HPP
 
-#include <stdio.h>
-#include <string.h>
-
-#if !defined(_WIN32)
-  #include <unistd.h>
-#else
-  #include <io.h>
-#endif
-
+#include <nall/platform.hpp>
 #include <nall/stdint.hpp>
 #include <nall/string.hpp>
 #include <nall/utf8.hpp>
@@ -28,6 +20,7 @@ namespace nall {
   public:
     enum class mode : unsigned { read, write, readwrite, writeread };
     enum class index : unsigned { absolute, relative };
+    enum class time : unsigned { create, modify, access };
 
     uint8_t read() {
       if(!fp) return 0xff;                       //file not open
@@ -142,52 +135,60 @@ namespace nall {
       return file_offset >= file_size;
     }
 
-    static bool exists(const char *fn) {
+    static bool exists(const char *filename) {
       #if !defined(_WIN32)
-      FILE *fp = fopen(fn, "rb");
+      struct stat64 data;
+      return stat64(filename, &data) == 0;
       #else
-      FILE *fp = _wfopen(utf16_t(fn), L"rb");
+      struct __stat64 data;
+      return _wstat64(utf16_t(filename), &data) == 0;
       #endif
-      if(fp) {
-        fclose(fp);
-        return true;
-      }
-      return false;
     }
 
-    static unsigned size(const char *fn) {
+    static uintmax_t size(const char *filename) {
       #if !defined(_WIN32)
-      FILE *fp = fopen(fn, "rb");
+      struct stat64 data;
+      stat64(filename, &data);
       #else
-      FILE *fp = _wfopen(utf16_t(fn), L"rb");
+      struct __stat64 data;
+      _wstat64(utf16_t(filename), &data);
       #endif
-      unsigned filesize = 0;
-      if(fp) {
-        fseek(fp, 0, SEEK_END);
-        filesize = ftell(fp);
-        fclose(fp);
+      return S_ISREG(data.st_mode) ? data.st_size : 0u;
+    }
+
+    static time_t timestamp(const char *filename, file::time mode = file::time::create) {
+      #if !defined(_WIN32)
+      struct stat64 data;
+      stat64(filename, &data);
+      #else
+      struct __stat64 data;
+      _wstat64(utf16_t(filename), &data);
+      #endif
+      switch(mode) { default:
+        case file::time::create: return data.st_ctime;
+        case file::time::modify: return data.st_mtime;
+        case file::time::access: return data.st_atime;
       }
-      return filesize;
     }
 
     bool open() {
       return fp;
     }
 
-    bool open(const char *fn, mode mode_) {
+    bool open(const char *filename, mode mode_) {
       if(fp) return false;
 
       switch(file_mode = mode_) {
         #if !defined(_WIN32)
-        case mode::read:      fp = fopen(fn, "rb");  break;
-        case mode::write:     fp = fopen(fn, "wb+"); break;  //need read permission for buffering
-        case mode::readwrite: fp = fopen(fn, "rb+"); break;
-        case mode::writeread: fp = fopen(fn, "wb+"); break;
+        case mode::read:      fp = fopen(filename, "rb" ); break;
+        case mode::write:     fp = fopen(filename, "wb+"); break;  //need read permission for buffering
+        case mode::readwrite: fp = fopen(filename, "rb+"); break;
+        case mode::writeread: fp = fopen(filename, "wb+"); break;
         #else
-        case mode::read:      fp = _wfopen(utf16_t(fn), L"rb");  break;
-        case mode::write:     fp = _wfopen(utf16_t(fn), L"wb+"); break;
-        case mode::readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break;
-        case mode::writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break;
+        case mode::read:      fp = _wfopen(utf16_t(filename), L"rb" ); break;
+        case mode::write:     fp = _wfopen(utf16_t(filename), L"wb+"); break;
+        case mode::readwrite: fp = _wfopen(utf16_t(filename), L"rb+"); break;
+        case mode::writeread: fp = _wfopen(utf16_t(filename), L"wb+"); break;
         #endif
       }
       if(!fp) return false;
diff --git a/bsnes/nall/platform.hpp b/bsnes/nall/platform.hpp
index 72eeec09..a7314493 100755
--- a/bsnes/nall/platform.hpp
+++ b/bsnes/nall/platform.hpp
@@ -1,6 +1,12 @@
 #ifndef NALL_PLATFORM_HPP
 #define NALL_PLATFORM_HPP 
+#if defined(_WIN32)
+  //minimum version needed for _wstat64, etc
+  #undef  __MSVCRT_VERSION__
+  #define __MSVCRT_VERSION__ 0x0601
+#endif
+
 #include <nall/utf8.hpp>
 
 //=========================
@@ -18,16 +24,19 @@
 #include <string.h>
 #include <time.h>
 
+#include <sys/types.h>
+#include <sys/stat.h>
+
 #if defined(_WIN32)
   #include <io.h>
   #include <direct.h>
   #include <shlobj.h>
+  #include <wchar.h>
   #undef interface
   #define dllexport __declspec(dllexport)
 #else
   #include <unistd.h>
   #include <pwd.h>
-  #include <sys/stat.h>
   #define dllexport
 #endif
 
@@ -53,11 +62,11 @@
 #if defined(_WIN32)
   #define getcwd      _getcwd
   #define ftruncate   _chsize
-  #define putenv      _putenv
   #define mkdir(n, m) _wmkdir(nall::utf16_t(n))
+  #define putenv      _putenv
   #define rmdir       _rmdir
-  #define vsnprintf   _vsnprintf
   #define usleep(n)   Sleep(n / 1000)
+  #define vsnprintf   _vsnprintf
 #endif
 
 //================
diff --git a/bsnes/nall/string/cast.hpp b/bsnes/nall/string/cast.hpp
index 14f005da..9498392c 100755
--- a/bsnes/nall/string/cast.hpp
+++ b/bsnes/nall/string/cast.hpp
@@ -6,8 +6,10 @@ namespace nall {
 //this is needed, as C++0x does not support explicit template specialization inside classes
 template<> inline const char* to_string<bool>         (bool v)          { return v ? "true" : "false"; }
 template<> inline const char* to_string<signed int>   (signed int v)    { static char temp[256]; snprintf(temp, 255, "%+d", v); return temp; }
-template<> inline const char* to_string<unsigned int> (unsigned int v)  { static char temp[256]; snprintf(temp, 255,  "%u", v); return temp; }
-template<> inline const char* to_string<double>       (double v)        { static char temp[256]; snprintf(temp, 255,  "%f", v); return temp; }
+template<> inline const char* to_string<unsigned int> (unsigned int v)  { static char temp[256]; snprintf(temp, 255, "%u", v); return temp; }
+template<> inline const char* to_string<intmax_t>     (intmax_t v)      { static char temp[256]; snprintf(temp, 255, "%+lld", (long long)v); return temp; }
+template<> inline const char* to_string<uintmax_t>    (uintmax_t v)     { static char temp[256]; snprintf(temp, 255, "%llu", (unsigned long long)v); return temp; }
+template<> inline const char* to_string<double>       (double v)        { static char temp[256]; snprintf(temp, 255, "%f", v); return temp; }
 template<> inline const char* to_string<char*>        (char *v)         { return v; }
 template<> inline const char* to_string<const char*>  (const char *v)   { return v; }
 template<> inline const char* to_string<string>       (string v)        { return v; }
diff --git a/bsnes/phoenix/core/core.cpp b/bsnes/phoenix/core/core.cpp
index ddb868cb..89d26718 100755
--- a/bsnes/phoenix/core/core.cpp
+++ b/bsnes/phoenix/core/core.cpp
@@ -34,6 +34,10 @@ void Font::setSize(unsigned size) { state.size = size; return p.setSize(size); }
 void Font::setUnderline(bool underline) { state.underline = underline; return p.setUnderline(underline); }
 Font::Font() : state(*new State), p(*new pFont(*this)) { p.constructor(); }
 
+void Timer::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); }
+void Timer::setInterval(unsigned milliseconds) { state.milliseconds = milliseconds; return p.setInterval(milliseconds); }
+Timer::Timer() : state(*new State), p(*new pTimer(*this)) { p.constructor(); }
+
 MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::information(parent, text, buttons); }
 MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::question(parent, text, buttons); }
 MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::warning(parent, text, buttons); }
diff --git a/bsnes/phoenix/core/core.hpp b/bsnes/phoenix/core/core.hpp
index 2f087b42..f09e6a2c 100755
--- a/bsnes/phoenix/core/core.hpp
+++ b/bsnes/phoenix/core/core.hpp
@@ -6,6 +6,7 @@ struct Widget;
 
 struct pOS;
 struct pFont;
+struct pTimer;
 struct pWindow;
 struct pAction;
 struct pMenu;
@@ -82,6 +83,18 @@ struct Font : Object {
   pFont &p;
 };
 
+struct Timer : Object {
+  nall::function<void ()> onTimeout;
+
+  void setEnabled(bool enabled = true);
+  void setInterval(unsigned milliseconds);
+
+  Timer();
+  struct State;
+  State &state;
+  pTimer &p;
+};
+
 struct MessageWindow : Object {
   enum class Buttons : unsigned {
     Ok,
diff --git a/bsnes/phoenix/core/layout/vertical-layout.cpp b/bsnes/phoenix/core/layout/vertical-layout.cpp
index e3185001..373c8249 100755
--- a/bsnes/phoenix/core/layout/vertical-layout.cpp
+++ b/bsnes/phoenix/core/layout/vertical-layout.cpp
@@ -69,10 +69,6 @@ Geometry VerticalLayout::minimumLayoutGeometry() {
   return { 0, 0, maximumWidth ? MaximumSize : margin * 2 + width, maximumHeight ? MaximumSize : margin * 2 + height };
 }
 
-void VerticalLayout::reset() {
-  children.reset();
-}
-
 void VerticalLayout::setGeometry(const Geometry &containerGeometry) {
   auto children = this->children;
   foreach(child, children) {
diff --git a/bsnes/phoenix/core/layout/vertical-layout.hpp b/bsnes/phoenix/core/layout/vertical-layout.hpp
index fb78247e..a280c269 100755
--- a/bsnes/phoenix/core/layout/vertical-layout.hpp
+++ b/bsnes/phoenix/core/layout/vertical-layout.hpp
@@ -5,7 +5,6 @@ struct VerticalLayout : public Layout {
   void append(Widget &widget, unsigned width, unsigned height, unsigned spacing = 0);
   Geometry minimumGeometry();
   Geometry minimumLayoutGeometry();
-  void reset();
   void setGeometry(const Geometry &geometry);
   void setMargin(unsigned margin);
   void setParent(Window &parent);
diff --git a/bsnes/phoenix/core/state.hpp b/bsnes/phoenix/core/state.hpp
index a4d99bf7..75df5abb 100755
--- a/bsnes/phoenix/core/state.hpp
+++ b/bsnes/phoenix/core/state.hpp
@@ -13,6 +13,16 @@ struct Font::State {
   }
 };
 
+struct Timer::State {
+  bool enabled;
+  unsigned milliseconds;
+
+  State() {
+    enabled = false;
+    milliseconds = 0;
+  }
+};
+
 struct Window::State {
   bool backgroundColor;
   unsigned backgroundColorRed, backgroundColorGreen, backgroundColorBlue;
diff --git a/bsnes/phoenix/gtk/gtk.cpp b/bsnes/phoenix/gtk/gtk.cpp
index c711d7a2..1c06df83 100755
--- a/bsnes/phoenix/gtk/gtk.cpp
+++ b/bsnes/phoenix/gtk/gtk.cpp
@@ -2,6 +2,7 @@
 
 #include "settings.cpp"
 #include "font.cpp"
+#include "timer.cpp"
 #include "message-window.cpp"
 #include "window.cpp"
 
@@ -31,10 +32,29 @@
 Font pOS::defaultFont;
 
 Geometry pOS::availableGeometry() {
-  //TODO: is there a GTK+ function for this?
-  //should return desktopGeometry() sans panels, toolbars, docks, etc.
-  Geometry geometry = desktopGeometry();
-  return { geometry.x + 64, geometry.y + 64, geometry.width - 128, geometry.height - 128 };
+  Display *display = XOpenDisplay(0);
+  int screen = DefaultScreen(display);
+
+  static Atom atom = X11None;
+  if(atom == X11None) atom = XInternAtom(display, "_NET_WORKAREA", True);
+
+  int format;
+  unsigned char *data = 0;
+  unsigned long items, after;
+  Atom returnAtom;
+
+  int result = XGetWindowProperty(
+    display, RootWindow(display, screen), atom, 0, 4, False, XA_CARDINAL, &returnAtom, &format, &items, &after, &data
+  );
+
+  XCloseDisplay(display);
+
+  if(result == Success && returnAtom == XA_CARDINAL && format == 32 && items == 4) {
+    unsigned long *workarea = (unsigned long*)data;
+    return { (signed)workarea[0], (signed)workarea[1], (unsigned)workarea[2], (unsigned)workarea[3] };
+  }
+
+  return desktopGeometry();
 }
 
 Geometry pOS::desktopGeometry() {
diff --git a/bsnes/phoenix/gtk/gtk.hpp b/bsnes/phoenix/gtk/gtk.hpp
index f6abacce..8a27fb03 100755
--- a/bsnes/phoenix/gtk/gtk.hpp
+++ b/bsnes/phoenix/gtk/gtk.hpp
@@ -57,6 +57,16 @@ struct pFont : public pObject {
   void constructor();
 };
 
+struct pTimer : public pObject {
+  Timer &timer;
+
+  void setEnabled(bool enabled);
+  void setInterval(unsigned milliseconds);
+
+  pTimer(Timer &timer) : timer(timer) {}
+  void constructor();
+};
+
 struct pMessageWindow : public pObject {
   static MessageWindow::Response information(Window &parent, const string &text, MessageWindow::Buttons buttons);
   static MessageWindow::Response question(Window &parent, const string &text, MessageWindow::Buttons buttons);
diff --git a/bsnes/phoenix/gtk/timer.cpp b/bsnes/phoenix/gtk/timer.cpp
new file mode 100755
index 00000000..d04183f8
--- /dev/null
+++ b/bsnes/phoenix/gtk/timer.cpp
@@ -0,0 +1,24 @@
+static guint Timer_trigger(pTimer *self) {
+  //timer may have been disabled prior to triggering, so check state
+  if(self->timer.state.enabled) {
+    if(self->timer.onTimeout) self->timer.onTimeout();
+  }
+  //callback may have disabled timer, so check state again
+  if(self->timer.state.enabled) {
+    g_timeout_add(self->timer.state.milliseconds, (GSourceFunc)Timer_trigger, (gpointer)self);
+  }
+  //kill this timer instance (it is spawned above if needed again)
+  return false;
+}
+
+void pTimer::setEnabled(bool enabled) {
+  if(enabled) {
+    g_timeout_add(timer.state.milliseconds, (GSourceFunc)Timer_trigger, (gpointer)this);
+  }
+}
+
+void pTimer::setInterval(unsigned milliseconds) {
+}
+
+void pTimer::constructor() {
+}
diff --git a/bsnes/phoenix/phoenix.cpp b/bsnes/phoenix/phoenix.cpp
index 28307897..d73c3b6a 100755
--- a/bsnes/phoenix/phoenix.cpp
+++ b/bsnes/phoenix/phoenix.cpp
@@ -1,3 +1,6 @@
+#ifndef PHOENIX_CPP
+#define PHOENIX_CPP
+
 #if defined(PHOENIX_WINDOWS)
   #define UNICODE
   #define WINVER 0x0501
@@ -14,13 +17,15 @@
   #include <QApplication>
   #include <QtGui>
 #elif defined(PHOENIX_GTK)
-  #define None X11None
+  #define None
   #define Window X11Window
+  #define X11None 0L
 
   #include <gtk/gtk.h>
   #include <gdk/gdkx.h>
   #include <cairo.h>
   #include <gdk/gdkkeysyms.h>
+  #include <X11/Xatom.h>
 
   #undef None
   #undef Window
@@ -35,3 +40,5 @@ using namespace nall;
 namespace phoenix {
   #include "core/core.cpp"
 }
+
+#endif
diff --git a/bsnes/phoenix/phoenix.hpp b/bsnes/phoenix/phoenix.hpp
index aa0d94ab..bea075c1 100755
--- a/bsnes/phoenix/phoenix.hpp
+++ b/bsnes/phoenix/phoenix.hpp
@@ -1,3 +1,6 @@
+#ifndef PHOENIX_HPP
+#define PHOENIX_HPP
+
 #include <nall/array.hpp>
 #include <nall/config.hpp>
 #include <nall/foreach.hpp>
@@ -11,3 +14,5 @@
 namespace phoenix {
   #include "core/core.hpp"
 }
+
+#endif
diff --git a/bsnes/phoenix/qt/qt.cpp b/bsnes/phoenix/qt/qt.cpp
index d971d04b..42bd10ee 100755
--- a/bsnes/phoenix/qt/qt.cpp
+++ b/bsnes/phoenix/qt/qt.cpp
@@ -3,6 +3,7 @@
 
 #include "settings.cpp"
 #include "font.cpp"
+#include "timer.cpp"
 #include "message-window.cpp"
 #include "window.cpp"
 
diff --git a/bsnes/phoenix/qt/qt.moc b/bsnes/phoenix/qt/qt.moc
index 5f8602a4..dd08a286 100755
--- a/bsnes/phoenix/qt/qt.moc
+++ b/bsnes/phoenix/qt/qt.moc
@@ -1,7 +1,7 @@
 /****************************************************************************
 ** Meta object code from reading C++ file 'qt.moc.hpp'
 **
-** Created: Wed Mar 23 16:17:23 2011
+** Created: Tue May 24 19:33:16 2011
 **      by: The Qt Meta Object Compiler version 62 (Qt 4.7.0)
 **
 ** WARNING! All changes made in this file will be lost!
@@ -16,6 +16,67 @@
 #endif
 
 QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_pTimer[] = {
+
+ // content:
+       5,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       1,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       0,       // signalCount
+
+ // slots: signature, parameters, type, tag, flags
+       8,    7,    7,    7, 0x0a,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_pTimer[] = {
+    "pTimer\0\0onTimeout()\0"
+};
+
+const QMetaObject pTimer::staticMetaObject = {
+    { &QObject::staticMetaObject, qt_meta_stringdata_pTimer,
+      qt_meta_data_pTimer, 0 }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &pTimer::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *pTimer::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *pTimer::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_pTimer))
+        return static_cast<void*>(const_cast< pTimer*>(this));
+    if (!strcmp(_clname, "pObject"))
+        return static_cast< pObject*>(const_cast< pTimer*>(this));
+    return QObject::qt_metacast(_clname);
+}
+
+int pTimer::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QObject::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        switch (_id) {
+        case 0: onTimeout(); break;
+        default: ;
+        }
+        _id -= 1;
+    }
+    return _id;
+}
 static const uint qt_meta_data_pWindow[] = {
 
  // content:
diff --git a/bsnes/phoenix/qt/qt.moc.hpp b/bsnes/phoenix/qt/qt.moc.hpp
index d7466d6b..ba6ab2ad 100755
--- a/bsnes/phoenix/qt/qt.moc.hpp
+++ b/bsnes/phoenix/qt/qt.moc.hpp
@@ -56,6 +56,23 @@ struct pFont : public pObject {
   void update();
 };
 
+struct pTimer : public QObject, public pObject {
+  Q_OBJECT
+
+public:
+  Timer &timer;
+  QTimer *qtTimer;
+
+  void setEnabled(bool enabled);
+  void setInterval(unsigned milliseconds);
+
+  pTimer(Timer &timer) : timer(timer) {}
+  void constructor();
+
+public slots:
+  void onTimeout();
+};
+
 struct pMessageWindow : public pObject {
   static MessageWindow::Response information(Window &parent, const string &text, MessageWindow::Buttons buttons);
   static MessageWindow::Response question(Window &parent, const string &text, MessageWindow::Buttons buttons);
diff --git a/bsnes/phoenix/qt/timer.cpp b/bsnes/phoenix/qt/timer.cpp
new file mode 100755
index 00000000..eba78b5a
--- /dev/null
+++ b/bsnes/phoenix/qt/timer.cpp
@@ -0,0 +1,21 @@
+void pTimer::setEnabled(bool enabled) {
+  if(enabled) {
+    qtTimer->start();
+  } else {
+    qtTimer->stop();
+  }
+}
+
+void pTimer::setInterval(unsigned milliseconds) {
+  qtTimer->setInterval(milliseconds);
+}
+
+void pTimer::constructor() {
+  qtTimer = new QTimer;
+  qtTimer->setInterval(0);
+  connect(qtTimer, SIGNAL(timeout()), SLOT(onTimeout()));
+}
+
+void pTimer::onTimeout() {
+  if(timer.onTimeout) timer.onTimeout();
+}
diff --git a/bsnes/phoenix/reference/font.cpp b/bsnes/phoenix/reference/font.cpp
index 9690c1d9..77d98f70 100755
--- a/bsnes/phoenix/reference/font.cpp
+++ b/bsnes/phoenix/reference/font.cpp
@@ -1,3 +1,7 @@
+Geometry pFont::geometry(const string &text) {
+  return { 0, 0, 0, 0 };
+}
+
 void pFont::setBold(bool bold) {
 }
 
diff --git a/bsnes/phoenix/reference/reference.cpp b/bsnes/phoenix/reference/reference.cpp
index a70254b6..d2b04b8e 100755
--- a/bsnes/phoenix/reference/reference.cpp
+++ b/bsnes/phoenix/reference/reference.cpp
@@ -1,6 +1,7 @@
 #include "reference.hpp"
 
 #include "font.cpp"
+#include "timer.cpp"
 #include "message-window.cpp"
 #include "window.cpp"
 
@@ -13,6 +14,7 @@
 
 #include "widget/widget.cpp"
 #include "widget/button.cpp"
+#include "widget/canvas.cpp"
 #include "widget/check-box.cpp"
 #include "widget/combo-box.cpp"
 #include "widget/hex-edit.cpp"
diff --git a/bsnes/phoenix/reference/reference.hpp b/bsnes/phoenix/reference/reference.hpp
index 5f8ebf59..6c48b171 100755
--- a/bsnes/phoenix/reference/reference.hpp
+++ b/bsnes/phoenix/reference/reference.hpp
@@ -29,6 +29,7 @@ struct pOS : public pObject {
 struct pFont : public pObject {
   Font &font;
 
+  Geometry geometry(const string &text);
   void setBold(bool bold);
   void setFamily(const string &family);
   void setItalic(bool italic);
@@ -39,6 +40,16 @@ struct pFont : public pObject {
   void constructor();
 };
 
+struct pTimer : public pObject {
+  Timer &timer;
+
+  void setEnabled(bool enabled);
+  void setInterval(unsigned milliseconds);
+
+  pTimer(Timer &timer) : timer(timer) {}
+  void constructor();
+};
+
 struct pMessageWindow : public pObject {
   static MessageWindow::Response information(Window &parent, const string &text, MessageWindow::Buttons buttons);
   static MessageWindow::Response question(Window &parent, const string &text, MessageWindow::Buttons buttons);
@@ -136,6 +147,8 @@ struct pWidget : public pObject {
   Widget &widget;
 
   bool enabled();
+  Font& font();
+  Geometry minimumGeometry();
   void setEnabled(bool enabled);
   void setFocused();
   void setFont(Font &font);
@@ -155,6 +168,16 @@ struct pButton : public pWidget {
   void constructor();
 };
 
+struct pCanvas : public pWidget {
+  Canvas &canvas;
+
+  uint32_t* buffer();
+  void update();
+
+  pCanvas(Canvas &canvas) : pWidget(canvas), canvas(canvas) {}
+  void constructor();
+};
+
 struct pCheckBox : public pWidget {
   CheckBox &checkBox;
 
diff --git a/bsnes/phoenix/reference/timer.cpp b/bsnes/phoenix/reference/timer.cpp
new file mode 100755
index 00000000..6cbe571a
--- /dev/null
+++ b/bsnes/phoenix/reference/timer.cpp
@@ -0,0 +1,8 @@
+void pTimer::setEnabled(bool enabled) {
+}
+
+void pTimer::setInterval(unsigned milliseconds) {
+}
+
+void pTimer::constructor() {
+}
diff --git a/bsnes/phoenix/reference/widget/canvas.cpp b/bsnes/phoenix/reference/widget/canvas.cpp
new file mode 100755
index 00000000..70295166
--- /dev/null
+++ b/bsnes/phoenix/reference/widget/canvas.cpp
@@ -0,0 +1,9 @@
+uint32_t* pCanvas::buffer() {
+  return 0;
+}
+
+void pCanvas::update() {
+}
+
+void pCanvas::constructor() {
+}
diff --git a/bsnes/phoenix/reference/widget/widget.cpp b/bsnes/phoenix/reference/widget/widget.cpp
index 40387671..a7787587 100755
--- a/bsnes/phoenix/reference/widget/widget.cpp
+++ b/bsnes/phoenix/reference/widget/widget.cpp
@@ -2,6 +2,14 @@ bool pWidget::enabled() {
   return false;
 }
 
+Font& pWidget::font() {
+  throw;
+}
+
+Geometry pWidget::minimumGeometry() {
+  return { 0, 0, 0, 0 };
+}
+
 void pWidget::setEnabled(bool enabled) {
 }
 
diff --git a/bsnes/phoenix/windows/timer.cpp b/bsnes/phoenix/windows/timer.cpp
new file mode 100755
index 00000000..8e2b3216
--- /dev/null
+++ b/bsnes/phoenix/windows/timer.cpp
@@ -0,0 +1,31 @@
+static linear_vector<pTimer*> timers;
+
+static void CALLBACK Timer_timeoutProc(HWND hwnd, UINT msg, UINT_PTR timerID, DWORD time) {
+  foreach(timer, timers) {
+    if(timer->htimer == timerID) {
+      if(timer->timer.onTimeout) timer->timer.onTimeout();
+      return;
+    }
+  }
+}
+
+void pTimer::setEnabled(bool enabled) {
+  if(htimer) {
+    KillTimer(NULL, htimer);
+    htimer = 0;
+  }
+
+  if(enabled == true) {
+    htimer = SetTimer(NULL, 0U, timer.state.milliseconds, Timer_timeoutProc);
+  }
+}
+
+void pTimer::setInterval(unsigned milliseconds) {
+  //destroy and recreate timer if interval changed
+  setEnabled(timer.state.enabled);
+}
+
+void pTimer::constructor() {
+  timers.append(this);
+  htimer = 0;
+}
diff --git a/bsnes/phoenix/windows/window.cpp b/bsnes/phoenix/windows/window.cpp
index 1961b410..752bd0c1 100755
--- a/bsnes/phoenix/windows/window.cpp
+++ b/bsnes/phoenix/windows/window.cpp
@@ -36,8 +36,11 @@ Geometry pWindow::frameMargin() {
 
 Geometry pWindow::geometry() {
   Geometry margin = frameMargin();
-  RECT rc;
-  GetWindowRect(hwnd, &rc);
+
+  //note: GetWindowRect returns -32000(x),-32000(y) when window is minimized
+  WINDOWPLACEMENT wp;
+  GetWindowPlacement(hwnd, &wp);
+  RECT rc = wp.rcNormalPosition;
 
   signed x = rc.left + margin.x;
   signed y = rc.top + margin.y;
diff --git a/bsnes/phoenix/windows/windows.cpp b/bsnes/phoenix/windows/windows.cpp
index 4c3596a4..94b1497a 100755
--- a/bsnes/phoenix/windows/windows.cpp
+++ b/bsnes/phoenix/windows/windows.cpp
@@ -2,6 +2,7 @@
 
 #include "object.cpp"
 #include "font.cpp"
+#include "timer.cpp"
 #include "message-window.cpp"
 #include "window.cpp"
 
diff --git a/bsnes/phoenix/windows/windows.hpp b/bsnes/phoenix/windows/windows.hpp
index 70933131..76bace93 100755
--- a/bsnes/phoenix/windows/windows.hpp
+++ b/bsnes/phoenix/windows/windows.hpp
@@ -49,6 +49,17 @@ struct pFont : public pObject {
   void constructor();
 };
 
+struct pTimer : public pObject {
+  Timer &timer;
+  UINT_PTR htimer;
+
+  void setEnabled(bool enabled);
+  void setInterval(unsigned milliseconds);
+
+  pTimer(Timer &timer) : timer(timer) {}
+  void constructor();
+};
+
 struct pMessageWindow : public pObject {
   static MessageWindow::Response information(Window &parent, const string &text, MessageWindow::Buttons buttons);
   static MessageWindow::Response question(Window &parent, const string &text, MessageWindow::Buttons buttons);
diff --git a/bsnes/snes/ppu/background/background.cpp b/bsnes/snes/ppu/background/background.cpp
index d654ceaf..cb3544cb 100755
--- a/bsnes/snes/ppu/background/background.cpp
+++ b/bsnes/snes/ppu/background/background.cpp
@@ -2,10 +2,16 @@
 
 #include "mode7.cpp"
 
+//V = 0, H = 0
 void PPU::Background::frame() {
 }
 
+//H = 0
 void PPU::Background::scanline() {
+}
+
+//H = 60
+void PPU::Background::begin() {
   bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);
   x = -7;
   y = self.vcounter();
@@ -13,20 +19,26 @@ void PPU::Background::scanline() {
   if(y == 1) {
     mosaic.vcounter = regs.mosaic + 1;
     mosaic.voffset = 1;
-    mosaic.vscroll = regs.voffset;
-    mosaic.hscroll = regs.hoffset;
+    cache.hoffset = regs.hoffset;
+    cache.voffset = regs.voffset;
   } else if(--mosaic.vcounter == 0) {
     mosaic.vcounter = regs.mosaic + 1;
     mosaic.voffset += regs.mosaic + 1;
-    mosaic.vscroll = regs.voffset;
-    mosaic.hscroll = regs.hoffset;
+    cache.hoffset = regs.hoffset;
+    cache.voffset = regs.voffset;
   }
 
-  tile_counter = (7 - (mosaic.hscroll & 7)) << hires;
+  tile_counter = (7 - (cache.hoffset & 7)) << hires;
   for(unsigned n = 0; n < 8; n++) data[n] = 0;
 
   mosaic.hcounter = regs.mosaic + 1;
   mosaic.hoffset = 0;
+
+  if(regs.mode == Mode::Mode7) return begin_mode7();
+  if(regs.mosaic == 0) {
+    cache.hoffset = regs.hoffset;
+    cache.voffset = regs.voffset;
+  }
 }
 
 void PPU::Background::get_tile() {
@@ -53,8 +65,8 @@ void PPU::Background::get_tile() {
   unsigned px = x << hires;
   unsigned py = (regs.mosaic == 0 ? y : mosaic.voffset);
 
-  unsigned hscroll = mosaic.hscroll;
-  unsigned vscroll = mosaic.vscroll;
+  unsigned hscroll = cache.hoffset;
+  unsigned vscroll = cache.voffset;
   if(hires) {
     hscroll <<= 1;
     if(self.regs.interlace) py = (py << 1) + self.field();
@@ -67,8 +79,8 @@ void PPU::Background::get_tile() {
     uint16 offset_x = (x + (hscroll & 7));
 
     if(offset_x >= 8) {
-      unsigned hval = self.bg3.get_tile((offset_x - 8) + (self.bg3.regs.hoffset & ~7), self.bg3.regs.voffset + 0);
-      unsigned vval = self.bg3.get_tile((offset_x - 8) + (self.bg3.regs.hoffset & ~7), self.bg3.regs.voffset + 8);
+      unsigned hval = self.bg3.get_tile((offset_x - 8) + (self.bg3.cache.hoffset & ~7), self.bg3.cache.voffset + 0);
+      unsigned vval = self.bg3.get_tile((offset_x - 8) + (self.bg3.cache.hoffset & ~7), self.bg3.cache.voffset + 8);
       unsigned valid_mask = (id == ID::BG1 ? 0x2000 : 0x4000);
 
       if(self.regs.bgmode == 4) {
@@ -205,6 +217,9 @@ void PPU::Background::reset() {
   regs.hoffset = random(0x0000);
   regs.voffset = random(0x0000);
 
+  cache.hoffset = 0;
+  cache.voffset = 0;
+
   output.main.palette = 0;
   output.main.priority = 0;
   output.sub.palette = 0;
@@ -216,10 +231,8 @@ void PPU::Background::reset() {
 
   mosaic.vcounter = 0;
   mosaic.voffset = 0;
-  mosaic.vscroll = 0;
   mosaic.hcounter = 0;
   mosaic.hoffset = 0;
-  mosaic.hscroll = 0;
 
   x = 0;
   y = 0;
diff --git a/bsnes/snes/ppu/background/background.hpp b/bsnes/snes/ppu/background/background.hpp
index 86bd420d..26553af4 100755
--- a/bsnes/snes/ppu/background/background.hpp
+++ b/bsnes/snes/ppu/background/background.hpp
@@ -25,6 +25,11 @@ class Background {
     uint16 voffset;
   } regs;
 
+  struct Cache {
+    uint16 hoffset;
+    uint16 voffset;
+  } cache;
+
   struct Output {
     struct Pixel {
       unsigned priority;  //0 = none (transparent)
@@ -36,10 +41,8 @@ class Background {
   struct Mosaic : Output::Pixel {
     unsigned vcounter;
     unsigned voffset;
-    unsigned vscroll;
     unsigned hcounter;
     unsigned hoffset;
-    unsigned hscroll;
   } mosaic;
 
   struct {
@@ -56,6 +59,7 @@ class Background {
 
   void frame();
   void scanline();
+  void begin();
   void run(bool screen);
   void reset();
 
@@ -63,6 +67,7 @@ class Background {
   unsigned get_tile_color();
   unsigned get_tile(unsigned x, unsigned y);
   signed clip(signed n);
+  void begin_mode7();
   void run_mode7();
 
   void serialize(serializer&);
diff --git a/bsnes/snes/ppu/background/mode7.cpp b/bsnes/snes/ppu/background/mode7.cpp
index 8304f82e..bb630647 100755
--- a/bsnes/snes/ppu/background/mode7.cpp
+++ b/bsnes/snes/ppu/background/mode7.cpp
@@ -5,6 +5,12 @@ signed PPU::Background::clip(signed n) {
   return n & 0x2000 ? (n | ~1023) : (n & 1023);
 }
 
+//H = 60
+void PPU::Background::begin_mode7() {
+  cache.hoffset = self.regs.mode7_hoffset;
+  cache.voffset = self.regs.mode7_voffset;
+}
+
 void PPU::Background::run_mode7() {
   signed a = sclip<16>(self.regs.m7a);
   signed b = sclip<16>(self.regs.m7b);
@@ -13,8 +19,8 @@ void PPU::Background::run_mode7() {
 
   signed cx = sclip<13>(self.regs.m7x);
   signed cy = sclip<13>(self.regs.m7y);
-  signed hoffset = sclip<13>(self.regs.mode7_hoffset);
-  signed voffset = sclip<13>(self.regs.mode7_voffset);
+  signed hoffset = sclip<13>(cache.hoffset);
+  signed voffset = sclip<13>(cache.voffset);
 
   if(Background::x++ & ~255) return;
   unsigned x = mosaic.hoffset;
diff --git a/bsnes/snes/ppu/ppu.cpp b/bsnes/snes/ppu/ppu.cpp
index b4a8de4c..f8a65e15 100755
--- a/bsnes/snes/ppu/ppu.cpp
+++ b/bsnes/snes/ppu/ppu.cpp
@@ -39,6 +39,10 @@ void PPU::enter() {
 
     scanline();
     add_clocks(60);
+    bg1.begin();
+    bg2.begin();
+    bg3.begin();
+    bg4.begin();
 
     if(vcounter() <= 239) {
       for(signed pixel = -7; pixel <= 255; pixel++) {
diff --git a/bsnes/snes/ppu/serialization.cpp b/bsnes/snes/ppu/serialization.cpp
index eaeb28d3..edf90f13 100755
--- a/bsnes/snes/ppu/serialization.cpp
+++ b/bsnes/snes/ppu/serialization.cpp
@@ -109,6 +109,9 @@ void PPU::Background::serialize(serializer &s) {
   s.integer(regs.hoffset);
   s.integer(regs.voffset);
 
+  s.integer(cache.hoffset);
+  s.integer(cache.voffset);
+
   s.integer(output.main.priority);
   s.integer(output.main.palette);
   s.integer(output.main.tile);
@@ -126,10 +129,8 @@ void PPU::Background::serialize(serializer &s) {
 
   s.integer(mosaic.vcounter);
   s.integer(mosaic.voffset);
-  s.integer(mosaic.vscroll);
   s.integer(mosaic.hcounter);
   s.integer(mosaic.hoffset);
-  s.integer(mosaic.hscroll);
 
   s.integer(tile_counter);
   s.integer(tile);
diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp
index 113e28b0..9848141f 100755
--- a/bsnes/snes/snes.hpp
+++ b/bsnes/snes/snes.hpp
@@ -1,7 +1,7 @@
 namespace SNES {
   namespace Info {
     static const char Name[] = "bsnes";
-    static const char Version[] = "078.07";
+    static const char Version[] = "079";
     static const unsigned SerializerVersion = 20;
   }
 }
diff --git a/snesfilter/2xSaI/2xSaI.cpp b/snesfilter/2xSaI/2xSaI.cpp
index 5f5a21f2..d9dc61fa 100755
--- a/snesfilter/2xSaI/2xSaI.cpp
+++ b/snesfilter/2xSaI/2xSaI.cpp
@@ -28,5 +28,5 @@ dllexport void filter_render(
     }
   }
 
-  _2xSaI32((unsigned char*)temp, 1024, 0, (unsigned char*)output, outpitch, width, height);
+  _2xSaI32((unsigned char*)temp, width * sizeof(uint32_t), 0, (unsigned char*)output, outpitch, width, height);
 }
diff --git a/snesfilter/2xSaI/Super-2xSaI.cpp b/snesfilter/2xSaI/Super-2xSaI.cpp
index eea01528..267e11e5 100755
--- a/snesfilter/2xSaI/Super-2xSaI.cpp
+++ b/snesfilter/2xSaI/Super-2xSaI.cpp
@@ -28,5 +28,5 @@ dllexport void filter_render(
     }
   }
 
-  Super2xSaI32((unsigned char*)temp, 1024, 0, (unsigned char*)output, outpitch, width, height);
+  Super2xSaI32((unsigned char*)temp, width * sizeof(uint32_t), 0, (unsigned char*)output, outpitch, width, height);
 }
diff --git a/snesfilter/2xSaI/Super-Eagle.cpp b/snesfilter/2xSaI/Super-Eagle.cpp
index 1e89de1f..c548348c 100755
--- a/snesfilter/2xSaI/Super-Eagle.cpp
+++ b/snesfilter/2xSaI/Super-Eagle.cpp
@@ -28,5 +28,5 @@ dllexport void filter_render(
     }
   }
 
-  SuperEagle32((unsigned char*)temp, 1024, 0, (unsigned char*)output, outpitch, width, height);
+  SuperEagle32((unsigned char*)temp, width * sizeof(uint32_t), 0, (unsigned char*)output, outpitch, width, height);
 }