diff --git a/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs b/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs
index 0f5d8f475f..3d70009e06 100644
--- a/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs
+++ b/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs
@@ -1,226 +1,227 @@
-using System;
-using System.Globalization;
-
-namespace BizHawk.Client.ApiHawk
-{
- ///
- /// This class holds a converter for BizHawk SystemId (which is a simple
- /// It allows you to convert it to a value and vice versa
- ///
- /// I made it this way just in case one day we need it for WPF (DependencyProperty binding). Just uncomment :IValueConverter implementation
- /// I didn't implemented it because of mono compatibility
- ///
- public sealed class BizHawkSystemIdToEnumConverter //:IValueConverter
- {
- ///
- /// Convert BizHawk SystemId to value
- ///
- /// you want to convert
- /// The type of the binding target property
- /// The converter parameter to use; null in our case
- /// The culture to use in the converter
- /// A that is equivalent to BizHawk SystemId
- /// Thrown when SystemId hasn't been found
- public object Convert(object value, Type targetType, object parameter, CultureInfo cultureInfo)
- {
- switch ((string)value)
- {
- case "AppleII":
- return CoreSystem.AppleII;
-
- case "A26":
- return CoreSystem.Atari2600;
-
- case "A78":
- return CoreSystem.Atari2600;
-
- case "A7800":
- return CoreSystem.Atari7800;
-
- case "Coleco":
- return CoreSystem.ColecoVision;
-
- case "C64":
- return CoreSystem.Commodore64;
-
- case "DGB":
- return CoreSystem.DualGameBoy;
-
- case "GB":
- return CoreSystem.GameBoy;
-
- case "GBA":
- return CoreSystem.GameBoyAdvance;
-
- case "GEN":
- return CoreSystem.Genesis;
-
- case "INTV":
- return CoreSystem.Intellivision;
-
- case "Libretro":
- return CoreSystem.Libretro;
-
- case "Lynx":
- return CoreSystem.Lynx;
-
- case "SMS":
- return CoreSystem.MasterSystem;
-
- case "NES":
- return CoreSystem.NES;
-
- case "N64":
- return CoreSystem.Nintendo64;
-
- case "NULL":
- return CoreSystem.Null;
-
- case "PCE":
- case "PCECD":
- case "SGX":
- return CoreSystem.PCEngine;
-
- case "PSX":
- return CoreSystem.Playstation;
-
- case "PSP":
- return CoreSystem.PSP;
-
- case "SAT":
- return CoreSystem.Saturn;
-
- case "SNES":
- return CoreSystem.SNES;
-
- case "TI83":
- return CoreSystem.TI83;
-
- case "WSWAN":
- return CoreSystem.WonderSwan;
-
- case "VB":
- case "NGP":
+using System;
+using System.Globalization;
+
+namespace BizHawk.Client.ApiHawk
+{
+ ///
+ /// This class holds a converter for BizHawk SystemId (which is a simple
+ /// It allows you to convert it to a value and vice versa
+ ///
+ /// I made it this way just in case one day we need it for WPF (DependencyProperty binding). Just uncomment :IValueConverter implementation
+ /// I didn't implemented it because of mono compatibility
+ ///
+ public sealed class BizHawkSystemIdToEnumConverter //:IValueConverter
+ {
+ ///
+ /// Convert BizHawk SystemId to value
+ ///
+ /// you want to convert
+ /// The type of the binding target property
+ /// The converter parameter to use; null in our case
+ /// The culture to use in the converter
+ /// A that is equivalent to BizHawk SystemId
+ /// Thrown when SystemId hasn't been found
+ public object Convert(object value, Type targetType, object parameter, CultureInfo cultureInfo)
+ {
+ switch ((string)value)
+ {
+ case "AppleII":
+ return CoreSystem.AppleII;
+
+ case "A26":
+ return CoreSystem.Atari2600;
+
+ case "A78":
+ return CoreSystem.Atari2600;
+
+ case "A7800":
+ return CoreSystem.Atari7800;
+
+ case "Coleco":
+ return CoreSystem.ColecoVision;
+
+ case "C64":
+ return CoreSystem.Commodore64;
+
+ case "DGB":
+ return CoreSystem.DualGameBoy;
+
+ case "GB":
+ return CoreSystem.GameBoy;
+
+ case "GBA":
+ return CoreSystem.GameBoyAdvance;
+
+ case "GEN":
+ return CoreSystem.Genesis;
+
+ case "INTV":
+ return CoreSystem.Intellivision;
+
+ case "Libretro":
+ return CoreSystem.Libretro;
+
+ case "Lynx":
+ return CoreSystem.Lynx;
+
+ case "SMS":
+ return CoreSystem.MasterSystem;
+
+ case "NES":
+ return CoreSystem.NES;
+
+ case "N64":
+ return CoreSystem.Nintendo64;
+
+ case "NULL":
+ return CoreSystem.Null;
+
+ case "PCE":
+ case "PCECD":
+ case "SGX":
+ return CoreSystem.PCEngine;
+
+ case "PSX":
+ return CoreSystem.Playstation;
+
+ case "PSP":
+ return CoreSystem.PSP;
+
+ case "SAT":
+ return CoreSystem.Saturn;
+
+ case "SNES":
+ return CoreSystem.SNES;
+
+ case "TI83":
+ return CoreSystem.TI83;
+
+ case "WSWAN":
+ return CoreSystem.WonderSwan;
+
+ case "VB":
+ case "NGP":
case "DNGP":
case "O2":
- case "SGB":
- return 0; // like I give a shit
-
- default:
- throw new IndexOutOfRangeException(string.Format("{0} is missing in convert list", value));
- }
- }
-
-
- ///
- /// Convert BizHawk SystemId to value
- ///
- /// you want to convert
- /// A that is equivalent to BizHawk SystemId
- /// Thrown when SystemId hasn't been found
- public CoreSystem Convert(string value)
- {
- return (CoreSystem)Convert(value, null, null, CultureInfo.CurrentCulture);
- }
-
-
- ///
- /// Convert a value to BizHawk SystemId
- ///
- /// you want to convert
- /// The type of the binding target property
- /// The converter parameter to use; null in our case
- /// The culture to use in the converter
- /// A that is used by BizHawk SystemId
- /// Thrown when hasn't been found
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo cultureInfo)
- {
- switch ((CoreSystem)value)
- {
- case CoreSystem.AppleII:
- return "AppleII";
-
- case CoreSystem.Atari2600:
- return "A26";
-
- case CoreSystem.Atari7800:
- return "A78";
-
- case CoreSystem.ColecoVision:
- return "Coleco";
-
- case CoreSystem.Commodore64:
- return "C64";
-
- case CoreSystem.DualGameBoy:
- return "DGB";
-
- case CoreSystem.GameBoy:
- return "GB";
-
- case CoreSystem.GameBoyAdvance:
- return "GBA";
-
- case CoreSystem.Genesis:
- return "GEN";
-
- case CoreSystem.Intellivision:
- return "INTV";
-
- case CoreSystem.Libretro:
- return "Libretro";
-
- case CoreSystem.Lynx:
- return "Lynx";
-
- case CoreSystem.MasterSystem:
- return "SMS";
-
- case CoreSystem.NES:
- return "NES";
-
- case CoreSystem.Nintendo64:
- return "N64";
-
- case CoreSystem.Null:
- return "NULL";
-
- case CoreSystem.PCEngine:
- return "PCE";
-
- case CoreSystem.Playstation:
- return "PSX";
-
- case CoreSystem.PSP:
- return "PSP";
-
- case CoreSystem.Saturn:
- return "SAT";
-
- case CoreSystem.SNES:
- return "SNES";
-
- case CoreSystem.TI83:
- return "TI83";
-
- case CoreSystem.WonderSwan:
- return "WSWAN";
-
- default:
- throw new IndexOutOfRangeException(string.Format("{0} is missing in convert list", value.ToString()));
- }
- }
-
-
- ///
- /// Convert a value to BizHawk SystemId
- ///
- /// you want to convert
- /// A that is used by BizHawk SystemId
- /// Thrown when hasn't been found
- public string ConvertBack(CoreSystem value)
- {
- return (string)ConvertBack(value, null, null, CultureInfo.CurrentCulture);
- }
- }
-}
+ case "SGB":
+ case "UZE":
+ return 0; // like I give a shit
+
+ default:
+ throw new IndexOutOfRangeException(string.Format("{0} is missing in convert list", value));
+ }
+ }
+
+
+ ///
+ /// Convert BizHawk SystemId to value
+ ///
+ /// you want to convert
+ /// A that is equivalent to BizHawk SystemId
+ /// Thrown when SystemId hasn't been found
+ public CoreSystem Convert(string value)
+ {
+ return (CoreSystem)Convert(value, null, null, CultureInfo.CurrentCulture);
+ }
+
+
+ ///
+ /// Convert a value to BizHawk SystemId
+ ///
+ /// you want to convert
+ /// The type of the binding target property
+ /// The converter parameter to use; null in our case
+ /// The culture to use in the converter
+ /// A that is used by BizHawk SystemId
+ /// Thrown when hasn't been found
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo cultureInfo)
+ {
+ switch ((CoreSystem)value)
+ {
+ case CoreSystem.AppleII:
+ return "AppleII";
+
+ case CoreSystem.Atari2600:
+ return "A26";
+
+ case CoreSystem.Atari7800:
+ return "A78";
+
+ case CoreSystem.ColecoVision:
+ return "Coleco";
+
+ case CoreSystem.Commodore64:
+ return "C64";
+
+ case CoreSystem.DualGameBoy:
+ return "DGB";
+
+ case CoreSystem.GameBoy:
+ return "GB";
+
+ case CoreSystem.GameBoyAdvance:
+ return "GBA";
+
+ case CoreSystem.Genesis:
+ return "GEN";
+
+ case CoreSystem.Intellivision:
+ return "INTV";
+
+ case CoreSystem.Libretro:
+ return "Libretro";
+
+ case CoreSystem.Lynx:
+ return "Lynx";
+
+ case CoreSystem.MasterSystem:
+ return "SMS";
+
+ case CoreSystem.NES:
+ return "NES";
+
+ case CoreSystem.Nintendo64:
+ return "N64";
+
+ case CoreSystem.Null:
+ return "NULL";
+
+ case CoreSystem.PCEngine:
+ return "PCE";
+
+ case CoreSystem.Playstation:
+ return "PSX";
+
+ case CoreSystem.PSP:
+ return "PSP";
+
+ case CoreSystem.Saturn:
+ return "SAT";
+
+ case CoreSystem.SNES:
+ return "SNES";
+
+ case CoreSystem.TI83:
+ return "TI83";
+
+ case CoreSystem.WonderSwan:
+ return "WSWAN";
+
+ default:
+ throw new IndexOutOfRangeException(string.Format("{0} is missing in convert list", value.ToString()));
+ }
+ }
+
+
+ ///
+ /// Convert a value to BizHawk SystemId
+ ///
+ /// you want to convert
+ /// A that is used by BizHawk SystemId
+ /// Thrown when hasn't been found
+ public string ConvertBack(CoreSystem value)
+ {
+ return (string)ConvertBack(value, null, null, CultureInfo.CurrentCulture);
+ }
+ }
+}
diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs
index 9d79a2b950..579f42e691 100644
--- a/BizHawk.Emulation.Common/Database/Database.cs
+++ b/BizHawk.Emulation.Common/Database/Database.cs
@@ -344,6 +344,10 @@ namespace BizHawk.Emulation.Common
case ".O2":
game.System = "O2";
break;
+
+ case ".UZE":
+ game.System = "UZE";
+ break;
}
game.Name = Path.GetFileNameWithoutExtension(fileName)?.Replace('_', ' ');
diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index e0b3b33ac1..8a0381903f 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -407,6 +407,8 @@
Lynx.cs
+
+
ColecoVision.cs
diff --git a/BizHawk.Emulation.Cores/Consoles/Belogic/LibUzem.cs b/BizHawk.Emulation.Cores/Consoles/Belogic/LibUzem.cs
new file mode 100644
index 0000000000..8a1c6d4452
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Belogic/LibUzem.cs
@@ -0,0 +1,28 @@
+using BizHawk.Common.BizInvoke;
+using BizHawk.Emulation.Cores.Waterbox;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BizHawk.Emulation.Cores.Consoles.Belogic
+{
+ public abstract class LibUzem : LibWaterboxCore
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ public new class FrameInfo : LibWaterboxCore.FrameInfo
+ {
+ public int ButtonsP1;
+ public int ButtonsP2;
+ public int ButtonsConsole;
+ }
+
+ [BizImport(CC)]
+ public abstract bool Init();
+
+ [BizImport(CC)]
+ public abstract bool MouseEnabled();
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Belogic/Uzem.cs b/BizHawk.Emulation.Cores/Consoles/Belogic/Uzem.cs
new file mode 100644
index 0000000000..01923ef4d7
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Belogic/Uzem.cs
@@ -0,0 +1,153 @@
+using BizHawk.Emulation.Common;
+using BizHawk.Emulation.Cores.Waterbox;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BizHawk.Emulation.Cores.Consoles.Belogic
+{
+ [CoreAttributes("uzem", "David Etherton", true, false, "", "", false)]
+ public class Uzem : WaterboxCore
+ {
+ private LibUzem _uze;
+ private bool _mouseEnabled;
+
+ [CoreConstructor("UZE")]
+ public Uzem(CoreComm comm, byte[] rom)
+ :base(comm, new Configuration
+ {
+ DefaultWidth = 720,
+ DefaultHeight = 224,
+ MaxWidth = 720,
+ MaxHeight = 224,
+ MaxSamples = 4096,
+ SystemId = "UZE",
+ DefaultFpsNumerator = 28618182,
+ DefaultFpsDenominator = 476840
+ })
+ {
+ _uze = PreInit(new PeRunnerOptions
+ {
+ Filename = "uzem.wbx",
+ SbrkHeapSizeKB = 4,
+ SealedHeapSizeKB = 4,
+ InvisibleHeapSizeKB = 4,
+ MmapHeapSizeKB = 4,
+ PlainHeapSizeKB = 4,
+ });
+
+ _exe.AddReadonlyFile(rom, "romfile");
+ if (!_uze.Init())
+ throw new InvalidOperationException("Core rejected the rom!");
+ _mouseEnabled = _uze.MouseEnabled();
+ _exe.RemoveReadonlyFile("romfile");
+
+ PostInit();
+ }
+
+ private static readonly ControllerDefinition TwoPads = new ControllerDefinition
+ {
+ Name = "SNES Controller",
+ BoolButtons =
+ {
+ "P1 Up", "P1 Left", "P1 Right", "P1 Down", "P1 Select", "P1 Start", "P1 X", "P1 A", "P1 B", "P1 Y", "P1 R", "P1 L",
+ "P2 Up", "P2 Left", "P2 Right", "P2 Down", "P2 Select", "P2 Start", "P2 X", "P2 A", "P2 B", "P2 Y", "P2 R", "P2 L",
+ "Power"
+ }
+ };
+
+ private static readonly ControllerDefinition Mouse = new ControllerDefinition
+ {
+ Name = "SNES Controller",
+ BoolButtons =
+ {
+ "P1 Mouse Left", "P1 Mouse Right", "Power"
+ },
+ FloatControls =
+ {
+ "P1 Mouse X", "P1 Mouse Y"
+ },
+ FloatRanges =
+ {
+ new[] { -127f, 0f, 127f },
+ new[] { -127f, 0f, 127f }
+ }
+ };
+
+ private static readonly string[] PadBits =
+ {
+ "B", "Y", "Select", "Start", "Up", "Down", "Left", "Right", "A", "X", "L", "R"
+ };
+
+ private static int EncodePad(IController c, int p)
+ {
+ int ret = 0;
+ int val = 1;
+ int idx = 0;
+ foreach (var s in PadBits)
+ {
+ if (c.IsPressed("P" + p + " " + PadBits[idx++]))
+ ret |= val;
+ val <<= 1;
+ }
+ return ret;
+ }
+
+ private static int EncodeDelta(float value)
+ {
+ int v = (int)value;
+ if (v > 127)
+ v = 127;
+ if (v < -127)
+ v = -127;
+
+ int ret = 0;
+ if (v < 0)
+ {
+ ret |= 1;
+ v = -v;
+ }
+
+ int mask = 64;
+ int bit = 2;
+ while (mask != 0)
+ {
+ if ((v & mask) != 0)
+ ret |= bit;
+ mask >>= 1;
+ bit <<= 1;
+ }
+ return ret;
+ }
+
+ public override ControllerDefinition ControllerDefinition => _mouseEnabled ? Mouse : TwoPads;
+
+ protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
+ {
+ var ret = new LibUzem.FrameInfo();
+ if (_mouseEnabled)
+ {
+ ret.ButtonsP1 = EncodeDelta(controller.GetFloat("P1 X"))
+ | EncodeDelta(controller.GetFloat("P1 Y"))
+ | 0x8000;
+ if (controller.IsPressed("P1 Mouse Left"))
+ ret.ButtonsP1 |= 0x200;
+ if (controller.IsPressed("P1 Mouse Right"))
+ ret.ButtonsP1 |= 0x100;
+ }
+ else
+ {
+ ret.ButtonsP1 = EncodePad(controller, 1);
+ ret.ButtonsP2 = EncodePad(controller, 2);
+ }
+ if (controller.IsPressed("Power"))
+ ret.ButtonsConsole = 1;
+
+ return ret;
+ }
+
+ public override int VirtualHeight => 448; // TODO: It's not quite this, NTSC and such
+ }
+}
diff --git a/output/dll/uzem.wbx.gz b/output/dll/uzem.wbx.gz
new file mode 100644
index 0000000000..a969c60ea3
Binary files /dev/null and b/output/dll/uzem.wbx.gz differ
diff --git a/waterbox/uzem/.vscode/settings.json b/waterbox/uzem/.vscode/settings.json
new file mode 100644
index 0000000000..92991025fa
--- /dev/null
+++ b/waterbox/uzem/.vscode/settings.json
@@ -0,0 +1,10 @@
+{
+ "files.associations": {
+ "ios": "cpp",
+ "xiosbase": "cpp",
+ "queue": "cpp"
+ },
+ "editor.tabSize": 4,
+ "editor.insertSpaces": false,
+ "editor.detectIndentation": false
+}
\ No newline at end of file
diff --git a/waterbox/uzem/Makefile b/waterbox/uzem/Makefile
new file mode 100644
index 0000000000..2da509478e
--- /dev/null
+++ b/waterbox/uzem/Makefile
@@ -0,0 +1,45 @@
+CC = x86_64-nt64-midipix-g++
+
+CCFLAGS:= -I. -I../emulibc \
+ -Wall -Werror=pointer-to-int-cast -Werror=int-to-pointer-cast -Werror=implicit-function-declaration \
+ -Wno-reorder \
+ -std=c++0x -fomit-frame-pointer -fvisibility=hidden -fno-exceptions -fno-rtti \
+ -DNOGDB \
+ -O3 -flto
+
+TARGET = uzem.wbx
+
+LDFLAGS = -Wl,--dynamicbase,--export-all-symbols
+
+ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
+SRCS:=$(shell find $(ROOT_DIR) -type f -name '*.cpp')
+OBJ_DIR:=$(ROOT_DIR)/obj
+
+_OBJS:=$(SRCS:.cpp=.o)
+OBJS:=$(patsubst $(ROOT_DIR)%,$(OBJ_DIR)%,$(_OBJS))
+
+$(OBJ_DIR)/%.o: %.cpp
+ @mkdir -p $(@D)
+ @$(CC) -c -o $@ $< $(CCFLAGS)
+
+all: $(TARGET)
+
+.PHONY: clean all
+
+$(TARGET).in: $(OBJS)
+ @$(CC) -o $@ $(LDFLAGS) $(CCFLAGS) $(OBJS) ../emulibc/libemuhost.so
+
+$(TARGET): $(TARGET).in
+ strip $< -o $@ -R /4 -R /14 -R /29 -R /41 -R /55 -R /67 -R /78 -R /89 -R /104
+# cp $< $@
+
+clean:
+ rm -rf $(OBJ_DIR)
+ rm -f $(TARGET).in
+ rm -f $(TARGET)
+
+print-%:
+ @echo $* = $($*)
+
+#install:
+# $(CP) $(TARGET) $(DEST_$(ARCH))
diff --git a/waterbox/uzem/avr8.cpp b/waterbox/uzem/avr8.cpp
new file mode 100644
index 0000000000..0890cee1d8
--- /dev/null
+++ b/waterbox/uzem/avr8.cpp
@@ -0,0 +1,2328 @@
+/*
+(The MIT License)
+
+Copyright (c) 2008-2016 by
+David Etherton, Eric Anderton, Alec Bourque (Uze), Filipe Rinaldi,
+Sandor Zsuga (Jubatian), Matt Pandina (Artcfox)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+/*
+Revision Log
+------------
+7/8/2013 V1.16 Added emulation for Timer1 Overflow interrupt
+
+More info at uzebox.org
+
+*/
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "avr8.h"
+
+using namespace std;
+
+#define X ((XL) | (XH << 8))
+#define DEC_X (XL-- || XH--)
+#define INC_X (++XL || ++XH)
+
+#define Y ((YL) | (YH << 8))
+#define DEC_Y (YL-- || YH--)
+#define INC_Y (++YL || ++YH)
+
+#define Z ((ZL) | (ZH << 8))
+#define DEC_Z (ZL-- || ZH--)
+#define INC_Z (++ZL || ++ZH)
+
+#define SP (SPL | (SPH << 8))
+#define DEC_SP (SPL-- || SPH--)
+#define INC_SP (++SPL || ++SPH)
+
+#define SREG_I 7
+#define SREG_T 6
+#define SREG_H 5
+#define SREG_S 4 // == N ^ V
+#define SREG_V 3
+#define SREG_N 2
+#define SREG_Z 1
+#define SREG_C 0
+
+#define EEPM1 0x20
+#define EEPM0 0x10
+#define EERIE 0x08
+#define EEMPE 0x04
+#define EEPE 0x02
+#define EERE 0x01
+
+//Interrupts vector adresses
+#define INT_RESET 0x00
+#define WDT 0x10
+#define TIMER1_COMPA 0x1A
+#define TIMER1_COMPB 0x1C
+#define TIMER1_OVF 0x1E
+#define SPI_STC 0x26
+
+#define REG_TCNT1L 0x84
+
+//TIFR1 flags
+#define TOV1 1
+#define OCF1A 2
+#define OCF1B 4
+
+//TIMSK1 flags
+#define TOIE1 1
+#define OCIE1A 2
+#define OCIE1B 4
+#define ICIE1 32
+
+//TCCR1B flags
+#define CS10 1
+#define WGM12 8
+
+//Watchdog flags
+#define WDE 8
+#define WDIE 64
+#define WDIF 128
+
+#define DELAY16MS 457142 //in cpu cycles
+#define HSYNC_HALF_PERIOD 910 //in cpu cycles
+#define HSYNC_PERIOD 1820 //in cpu cycles
+
+#define SD_ENABLED() false
+
+/*
+#define D3 ((insn >> 4) & 7)
+#define R3 (insn & 7)
+#define D4 ((insn >> 4) & 15)
+#define R4 (insn & 15)
+#define R5 ((insn & 15) | ((insn >> 5) & 0x10))
+#define D5 ((insn >> 4) & 31)
+#define K8 (((insn >> 4) & 0xF0) | (insn & 0xF))
+#define k7 ((s16)(insn<<6)>>9)
+#define k12 ((s16)(insn<<4)>>4)
+*/
+
+#define BIT(x, b) (((x) >> (b)) & 1)
+#define C BIT(SREG, SREG_C)
+
+// Delayed output flags. If either is set, there is a delayed out waiting to
+// be written on the end of update_hardware, so the next cycle it can have
+// effect. The "dly_out" variable contains these flags, and the "dly_xxx"
+// variables the values to be written out.
+#define DLY_TCCR1B 0x0001U
+#define DLY_TCNT1 0x0002U
+
+// Masks for SREG bits, use to combine them
+#define SREG_IM (1U << SREG_I)
+#define SREG_TM (1U << SREG_T)
+#define SREG_HM (1U << SREG_H)
+#define SREG_SM (1U << SREG_S)
+#define SREG_VM (1U << SREG_V)
+#define SREG_NM (1U << SREG_N)
+#define SREG_ZM (1U << SREG_Z)
+#define SREG_CM (1U << SREG_C)
+
+// Clears bits. Use this on the bits which should change processing
+// the given instruction.
+inline static void clr_bits(u8 &dest, unsigned int bits)
+{
+ dest = dest & (~bits);
+}
+// Inverse set bit: Sets if value is zero. Mostly for Z flag
+inline static void set_bit_inv(u8 &dest, unsigned int bit, unsigned int value)
+{
+ // Assume at most 16 bits input on value, makes it 0 or 1, the latter
+ // if the input was nonzero. The "& 1U" part is usually thrown away by
+ // the compiler (32 bits). The "& 0xFFFFU" part might also be thrown
+ // away depending on the input.
+ value = ((value & 0xFFFFU) - 1U) >> 31;
+ dest = dest | ((value & 1U) << bit);
+}
+// Set bit using only the lowest bit of 'value': if 1, sets the bit.
+inline static void set_bit_1(u8 &dest, unsigned int bit, unsigned int value)
+{
+ // The "& 1" on 'value' might be thrown away for suitable input.
+ dest = dest | ((value & 1U) << bit);
+}
+// Store bit (either set or clear) using only the lowest bit of 'value'.
+inline static void store_bit_1(u8 &dest, unsigned int bit, unsigned int value)
+{
+ // The "& 1" on 'value' might be thrown away for suitable input
+ // If 'bit' is constant (inlining), it folds up well on optimizing.
+ dest = dest & (~(1U << bit));
+ dest = dest | ((0U - (value & 1U)) & (1U << bit));
+}
+
+// This computes both the half-carry (bit3) and full carry (bit7)
+#define BORROWS (~Rd & Rr) | (Rr & R) | (R & ~Rd)
+#define CARRIES ((Rd & Rr) | (Rr & ~R) | (~R & Rd))
+
+#define UPDATE_HC_SUB \
+ CH = BORROWS; \
+ set_bit_1(SREG, SREG_H, (CH & 0x08U) >> 3); \
+ set_bit_1(SREG, SREG_C, (CH & 0x80U) >> 7);
+#define UPDATE_HC_ADD \
+ CH = CARRIES; \
+ set_bit_1(SREG, SREG_H, (CH & 0x08U) >> 3); \
+ set_bit_1(SREG, SREG_C, (CH & 0x80U) >> 7);
+
+#define UPDATE_H set_bit_1(SREG, SREG_H, (CARRIES & 0x8) >> 3)
+#define UPDATE_Z set_bit_inv(SREG, SREG_Z, R)
+#define UPDATE_V_ADD set_bit_1(SREG, SREG_V, (((Rd & Rr & ~R) | (~Rd & ~Rr & R)) & 0x80) >> 7)
+#define UPDATE_V_SUB set_bit_1(SREG, SREG_V, (((Rd & ~Rr & ~R) | (~Rd & Rr & R)) & 0x80) >> 7)
+#define UPDATE_N set_bit_1(SREG, SREG_N, (R & 0x80) >> 7)
+#define UPDATE_S set_bit_1(SREG, SREG_S, BIT(SREG, SREG_N) ^ BIT(SREG, SREG_V))
+
+#define UPDATE_SVN_SUB \
+ UPDATE_V_SUB; \
+ UPDATE_N; \
+ UPDATE_S
+#define UPDATE_SVN_ADD \
+ UPDATE_V_ADD; \
+ UPDATE_N; \
+ UPDATE_S
+
+// Simplified version for logical insns.
+// sreg_clr on S, V, and N should be called before this.
+// If 7th bit of R is set:
+// Sets N, sets S, clears V.
+// If 7th bit of R is clear:
+// Clears N, clears S, clears V.
+#define UPDATE_SVN_LOGICAL \
+ SREG |= ((0x7FU - (unsigned int)(R)) >> 8) & (SREG_SM | SREG_NM);
+
+#define UPDATE_CZ_MUL(x) \
+ set_bit_1(SREG, SREG_C, (x & 0x8000) >> 15); \
+ set_bit_inv(SREG, SREG_Z, x)
+
+// UPDATE_CLEAR_Z: Updates Z flag by clearing if result is nonzero. This
+// should be used if the previous Z flag state is meant to be preserved (such
+// as in CPC), so don't include Z in a clr_bits then.
+#define UPDATE_CLEAR_Z (SREG &= ~(((0U - (unsigned int)(R)) >> 8) & SREG_ZM))
+
+#define SET_C (SREG |= (1 << SREG_C))
+
+#define ILLEGAL_OP \
+ fprintf(stderr, "invalid insn at address %x\n", currentPc); \
+ shutdown(1);
+
+#if defined(_DEBUG)
+#define DISASM 1
+#define DIS(fmt, ...) \
+ sprintf(insnBuf, fmt, ##__VA_ARGS__); \
+ if (disasmOnly) \
+ break
+#else
+#define DISASM 0
+#define DIS(fmt, ...)
+#endif
+
+static u8 encode_delta(int d)
+{
+ u8 result;
+ if (d < 0)
+ {
+ result = 0;
+ d = -d;
+ }
+ else
+ result = 1;
+ if (d > 127)
+ d = 127;
+ if (!(d & 64))
+ result |= 2;
+ if (!(d & 32))
+ result |= 4;
+ if (!(d & 16))
+ result |= 8;
+ if (!(d & 8))
+ result |= 16;
+ if (!(d & 4))
+ result |= 32;
+ if (!(d & 2))
+ result |= 64;
+ if (!(d & 1))
+ result |= 128;
+ return result;
+}
+
+void avr8::spi_calculateClock()
+{
+ // calculate the number of cycles before the write completes
+ u16 spiClockDivider;
+ switch (SPCR & 0x03)
+ {
+ case 0:
+ spiClockDivider = 4;
+ break;
+ case 1:
+ spiClockDivider = 16;
+ break;
+ case 2:
+ spiClockDivider = 64;
+ break;
+ case 3:
+ spiClockDivider = 128;
+ break;
+ }
+ if (SPSR & 0x01)
+ {
+ spiClockDivider = spiClockDivider >> 1; // double the speed
+ }
+ spiCycleWait = spiClockDivider * 8;
+ SPI_DEBUG("SPI divider set to : %d (%d cycles per byte)\n", spiClockDivider, spiCycleWait);
+}
+
+// Renders a line into a 32 bit output buffer.
+// Performs a shrink by 2
+static inline void render_line(u32 *dest, u8 const *src, unsigned int spos, u32 const *pal)
+{
+ for (unsigned int i = 0; i < VIDEO_DISP_WIDTH; i++)
+ dest[i] = pal[src[((i << 1) + spos) & 0x7FFU]];
+}
+
+inline void avr8::write_io(u8 addr, u8 value)
+{
+ // Pixel output ideally should inline, it is performed about 2 - 3
+ // million times per second in a Uzebox game.
+ if (addr == ports::PORTC)
+ {
+ pixel_raw = value & DDRC;
+ }
+ else
+ {
+ write_io_x(addr, value);
+ }
+}
+
+extern void SampleCallback(uint8_t val);
+
+// Should not be called directly, use write_io instead (pixel output!)
+void avr8::write_io_x(u8 addr, u8 value)
+{
+ u8 changed;
+ u8 went_low;
+
+ switch (addr)
+ {
+ case (ports::OCR2A):
+ if (enableSound && TCCR2B)
+ {
+ SampleCallback(value);
+ }
+ break;
+
+ case (ports::PORTD):
+ // write value with respect to DDRD register
+ io[addr] = value & DDRD;
+ break;
+
+ case (ports::PORTB):
+ if (value & 1)
+ {
+ elapsedCycles = cycleCounter - prevCyclesCounter;
+
+ if (scanline_count == -999 && elapsedCycles >= HSYNC_HALF_PERIOD - 10 && elapsedCycles <= HSYNC_HALF_PERIOD + 10)
+ {
+ scanline_count = scanline_top;
+ }
+ else if (scanline_count != -999)
+ {
+
+ if (scanline_count >= 0)
+ {
+ render_line(video_buffer + scanline_count * VIDEO_DISP_WIDTH,
+ &scanline_buf[0],
+ left_edge + left_edge_cycle,
+ palette);
+ }
+
+ scanline_count++;
+ left_edge_cycle = cycleCounter;
+
+ if (scanline_count == 224)
+ {
+ scanline_count = -999;
+ }
+ }
+
+ prevCyclesCounter = cycleCounter;
+ }
+ break;
+
+ case (ports::PORTA):
+ changed = value ^ io[addr];
+ went_low = changed & io[addr];
+
+ if (went_low == (1 << 2)) // LATCH
+ {
+ for (int i = 0; i < 2; i++)
+ {
+ latched_buttons[i] = buttons[i];
+ }
+ }
+ else if (went_low == (1 << 3)) // CLOCK
+ {
+ if (new_input_mode)
+ PINA = u8((latched_buttons[0] & 1) | ((latched_buttons[1] & 1) << 1));
+ latched_buttons[0] >>= 1;
+ latched_buttons[1] >>= 1;
+
+ if ((latched_buttons[1] < 0xFFFFF) && !new_input_mode)
+ {
+ //New input routines detected, switching emulation method
+ new_input_mode = true;
+ }
+ }
+ if (!new_input_mode)
+ PINA = u8((latched_buttons[0] & 1) | ((latched_buttons[1] & 1) << 1));
+
+ //Uzebox keyboard (always on P2 port)
+ switch (uzeKbState)
+ {
+ case KB_STOP:
+ //check uzekeyboard start condition: clock=low & latch=high simultaneously
+ if ((value & 0x0c) == 0x04)
+ {
+ uzeKbState = KB_TX_START;
+ uzeKbEnabled = true; //enable keyboard capture for Uzebox Keyboard
+ }
+ break;
+
+ case KB_TX_START:
+ //check start condition pulse completed: clock=high & latch=low (normal state)
+ if ((value & 0x0c) == 0x08)
+ {
+ uzeKbState = KB_TX_READY;
+ uzeKbClock = 8;
+ }
+ break;
+
+ case KB_TX_READY:
+ if (went_low == (1 << 3)) // CLOCK
+ {
+ if (uzeKbClock == 8)
+ {
+ uzeKbDataOut = 0;
+ //returns only keys (no commands response yet)
+ if (uzeKbScanCodeQueue.empty())
+ {
+ uzeKbDataIn = 0;
+ }
+ else
+ {
+ uzeKbDataIn = uzeKbScanCodeQueue.front();
+ uzeKbScanCodeQueue.pop();
+ }
+ }
+
+ //shift data out to keyboard
+ //latch pin is used as "Data Out"
+ uzeKbDataOut <<= 1;
+ if (value & 0x04)
+ { //latch pin=1?
+ uzeKbDataOut |= 1;
+ }
+
+ //shift data in from keyboard
+ if (uzeKbDataIn & 0x80)
+ {
+ PINA |= (0x02); //set P2 data bit
+ }
+ else
+ {
+ PINA &= ~(0x02); //clear P2 data bit
+ }
+ uzeKbDataIn <<= 1;
+
+ uzeKbClock--;
+ if (uzeKbClock == 0)
+ {
+ if (uzeKbDataOut == KB_SEND_END)
+ {
+ uzeKbState = KB_STOP;
+ }
+ else
+ {
+ uzeKbClock = 8;
+ }
+ }
+ }
+ break;
+ }
+
+ io[addr] = value;
+ break;
+
+ case (ports::TCNT1H):
+ // p106 in 644 manual; 16-bit values are latched
+ T16_latch = value;
+ break;
+
+ case (ports::TCNT1L):
+ dly_TCNT1L = value;
+ dly_TCNT1H = T16_latch;
+ dly_out |= DLY_TCNT1;
+ break;
+
+ case (ports::SPDR):
+ if ((SPCR & 0x40) && SD_ENABLED())
+ { // only if SPI is enabled and card is present
+ spiByte = value;
+ //TODO: flag collision if x-fer in progress
+ spiClock = spiCycleWait;
+ spiTransfer = 1;
+ SPSR ^= 0x80; // clear interrupt
+ //SPI_DEBUG("spiClock: %0.2X\n",spiClock);
+ }
+ // SPI_DEBUG("SPDR: %0.2X\n",value);
+ io[addr] = value;
+ break;
+
+ case (ports::SPCR):
+ SPI_DEBUG("SPCR: %02X\n", value);
+ io[addr] = value;
+ if (SD_ENABLED())
+ spi_calculateClock();
+ break;
+
+ case (ports::SPSR):
+ SPI_DEBUG("SPSR: %02X\n", value);
+ io[addr] = value;
+ if (SD_ENABLED())
+ spi_calculateClock();
+ break;
+
+ case (ports::EECR):
+ //printf("writing to port %s (%x) pc = %x\n",port_name(addr),value,pc-1);
+ //EEPROM can only be put into either read or write mode, and the master bit must be set
+
+ if (value & EERE)
+ {
+ if (io[addr] & EEPE)
+ {
+ io[addr] = value ^ EERE; // programming in progress, don't allow this to be set
+ }
+ else
+ {
+ io[addr] = value;
+ }
+ }
+ else if (value & EEPE)
+ {
+ if ((io[addr] & EERE) || !(io[addr] & EEMPE))
+ { // need master program enabled first
+ io[addr] = value ^ EEPE; // read in progress, don't allow this to be set
+ }
+ else
+ {
+ io[addr] = value;
+ }
+ }
+ if (value & EEMPE)
+ {
+ io[addr] = value;
+ // eeClock = 4; TODO: This was only set here, never used. Maybe a never completed EEPROM timing code.
+ }
+ else
+ {
+ io[addr] = value;
+ }
+ break;
+
+ // Note: This was commented out in the original code. If needed,
+ // integrate in the switch.
+ //else if(addr == ports::EEARH || addr == ports::EEARL || addr == ports::EEDR){
+ // io[addr] = value;
+
+ case (ports::TIFR1):
+ //clear flags by writing logical one
+ io[addr] &= ~(value);
+ break;
+
+ case (ports::TCCR1B):
+ dly_TCCR1B = value;
+ dly_out |= DLY_TCCR1B;
+ break;
+
+ case (ports::OCR1AH):
+ case (ports::OCR1AL):
+ case (ports::OCR1BH):
+ case (ports::OCR1BL):
+ // TODO: These should also be latched by the Atmel docs, maybe
+ // implement it later.
+ io[addr] = value;
+ TCNT1 += timer1_base - timer1_next;
+ timer1_base = 0U;
+ timer1_next = 0U; // Force timer state recalculation (update_hardware)
+ break;
+
+ case (ports::res3A):
+ // emulator-only whisper support
+ printf("%c", value);
+ break;
+
+ case (ports::res39):
+ // emulator-only whisper support
+ printf("%02x", value);
+ break;
+
+ default:
+ io[addr] = value;
+ break;
+ }
+}
+
+extern void (*InputCallback)();
+
+u8 avr8::read_io(u8 addr)
+{
+ if (addr == ports::PINA)
+ {
+ lagged = false;
+ if (InputCallback)
+ InputCallback();
+ }
+
+ // p106 in 644 manual; 16-bit values are latched
+ if (addr == ports::TCNT1L)
+ {
+ unsigned int curr_timer = TCNT1 + timer1_base - timer1_next;
+ T16_latch = (curr_timer >> 8) & 0xFFU;
+ return curr_timer & 0xFFU;
+ }
+ else if (addr == ports::TCNT1H)
+ {
+ return T16_latch;
+ }
+ else
+ {
+ return io[addr];
+ }
+}
+
+// Inline variation of update_hardware, to be used with frequent multi-cycle
+// instructions.
+inline void avr8::update_hardware_fast()
+{
+ if (timer1_next == 0U)
+ {
+ update_hardware();
+ return;
+ }
+
+ cycleCounter++;
+ timer1_next--;
+ scanline_buf[cycleCounter & 0x7FFU] = pixel_raw;
+}
+
+// Performs hardware updates which have to be calculated at cycle precision
+void avr8::update_hardware()
+{
+ cycleCounter++;
+
+ // timer1_next stores the cycles remaining until the next event on the
+ // Timer1 16 bit timer. It can be cleared to zero whenever the timer's
+ // state is changed (port writes). Locking it to zero should have no
+ // effect, causing the timer to re-calculate its state proper on every
+ // update_hardware call. It should only improve performance.
+
+ if (timer1_next == 0U)
+ {
+
+ // Apply time elapsed between full timer processings
+
+ TCNT1 += timer1_base;
+
+ // Apply delayed timer interrupt flags
+
+ TIFR1 |= itd_TIFR1;
+ itd_TIFR1 = 0U;
+
+ // Process timer
+
+ if ((TCCR1B & 7U) != 0U) // If timer 1 is started
+ {
+
+ unsigned int OCR1A = OCR1AL | ((unsigned int)(OCR1AH) << 8);
+ unsigned int OCR1B = OCR1BL | ((unsigned int)(OCR1BH) << 8);
+
+ if (TCCR1B & WGM12) // Timer in CTC mode: count up to OCRnA then resets to zero
+ {
+
+ if (TCNT1 == 0xFFFFU)
+ {
+ itd_TIFR1 |= TOV1;
+ }
+
+ if (TCNT1 == OCR1B)
+ {
+ itd_TIFR1 |= OCF1B;
+ }
+
+ if (TCNT1 == OCR1A)
+ {
+ TCNT1 = 0U;
+ itd_TIFR1 |= OCF1A;
+ }
+ else
+ {
+ TCNT1 = (TCNT1 + 1U) & 0xFFFFU;
+ }
+
+ // Calculate next timer event
+
+ if (itd_TIFR1 == 0U)
+ {
+ timer1_next = 0xFFFFU - TCNT1;
+ if ((TCNT1 <= OCR1B) &&
+ (timer1_next > (OCR1B - TCNT1)))
+ {
+ timer1_next = (OCR1B - TCNT1);
+ }
+ if ((TCNT1 <= OCR1A) &&
+ (timer1_next > (OCR1A - TCNT1)))
+ {
+ timer1_next = (OCR1A - TCNT1);
+ }
+ }
+ }
+ else
+ { //timer in normal mode: counts up to 0xffff then rolls over
+
+ if (TCNT1 == 0xFFFFU)
+ {
+ itd_TIFR1 |= TOV1;
+ }
+ TCNT1 = (TCNT1 + 1U) & 0xFFFFU;
+
+ // Calculate next timer event
+
+ if (itd_TIFR1 == 0U)
+ {
+ timer1_next = 0xFFFFU - TCNT1;
+ }
+ }
+ }
+
+ // Set timer base to be able to reproduce TCNT1 outside full timer
+ // processing
+
+ timer1_base = timer1_next;
+ }
+ else
+ {
+ timer1_next--;
+ }
+
+ // Draw pixel on scanline
+
+ scanline_buf[cycleCounter & 0x7FFU] = pixel_raw;
+}
+
+// Performs hardware updates which can be done at instruction precision
+// Also process interrupt requests
+inline void avr8::update_hardware_ins()
+{
+ // Apply delayed outputs
+ //
+ // Notes: This can be here since as of now, it looks like all
+ // instructions writing IO registers perform that in their last
+ // cycle, so a delayed output may only be produced then, that is,
+ // after the instruction. If this doesn't hold up, this will have
+ // to be placed in the update_hardware call.
+
+ if (dly_out != 0U)
+ {
+ if ((dly_out & DLY_TCCR1B) != 0U)
+ {
+ TCCR1B = dly_TCCR1B;
+ TCNT1 += timer1_base - timer1_next;
+ timer1_base = 0U;
+ timer1_next = 0U; // Timer state changes
+ }
+ if ((dly_out & DLY_TCNT1) != 0U)
+ {
+ TCNT1 = (dly_TCNT1H << 8) | dly_TCNT1L;
+ timer1_base = 0U;
+ timer1_next = 0U; // Timer state changes
+ }
+ dly_out = 0U;
+ }
+
+ // Get cycle count to emulate
+
+ unsigned int cycles = cycleCounter - cycle_ctr_ins;
+ cycle_ctr_ins = cycleCounter;
+
+ // Notes:
+ //
+ // From this point if further cycles are required to be consumed,
+ // those should be consumed using update_hardware(). This won't
+ // increase this run's cycle count (cycles), but will show in the
+ // next run proper.
+
+ // Watchdog notes:
+ //
+ // This is a bare minimum implementation to make the Uzebox kernel's
+ // seed generator operational (used for seeding a PRNG).
+
+ if (WDTCSR & WDE)
+ { //if watchdog enabled
+ watchdogTimer += cycles;
+ if (watchdogTimer >= DELAY16MS && (WDTCSR & WDIE))
+ {
+ WDTCSR |= WDIF; //watchdog interrupt
+ //reset watchdog
+ //watchdog is based on a RC oscillator
+ //so add some random variation to simulate entropy
+ watchdogTimer = rand() % 1024;
+ }
+ }
+
+ // clock the SPI hardware.
+ if ((SPCR & 0x40) && SD_ENABLED())
+ { // only if SPI is enabled
+ //TODO: test for master/slave modes (assume master for now)
+ // TODO: factor in clock divider
+
+ if (spiTransfer)
+ {
+ if (spiClock <= cycles)
+ {
+ //SPI_DEBUG("SPI transfer complete\n");
+ update_spi();
+ spiClock = 0;
+ spiTransfer = 0;
+ SPSR |= 0x80; // set interrupt
+ }
+ else
+ {
+ spiClock -= cycles;
+ }
+ }
+ /*
+ //HACK: instantaneous SPI access
+ if(spiTransfer && spiClock > 0){
+ update_spi();
+ SPSR |= 0x80; // set interrupt
+ spiTransfer = 0;
+ spiClock = 0;
+ }*/
+
+ // test for interrupt (enable and interrupt flag for SPI)
+ // TODO (Jubatian): Verify that the move is OK, if not, try to fix
+ // it there (where the other interrupts are)
+ }
+
+ //clock the EEPROM hardware
+ /*
+ 1. Wait until EEPE becomes zero.
+ 2. Wait until SELFPRGEN in SPMCSR becomes zero.
+ 3. Write new EEPROM address to EEAR (optional).
+ 4. Write new EEPROM data to EEDR (optional).
+ 5. Write a logical one to the EEMPE bit while writing a zero to EEPE in EECR.
+ 6. Within four clock cycles after setting EEMPE, write a logical one to EEPE.
+ The EEPROM can not be programmed during a CPU write to the Flash memory.
+ */
+ // are we attempting to program?
+
+ // TODO (Jubatian):
+ //
+ // cycleCounter is incremented here by 4, but this has no effect on at
+ // least Timer 1 and the video output, maybe even more. Not like
+ // writing to EEPROM would be a common task when drawing the video
+ // frame, though.
+
+ if (EECR & (EEPE | EERE))
+ {
+ if (EECR & EEPE)
+ {
+ //printf("attempting write of EEPROM\n");
+ cycleCounter += 4; // writes take four cycles
+ int addr = (EEARH << 8) | EEARL;
+ if (addr < eepromSize)
+ eeprom[addr] = EEDR;
+ EECR ^= (EEMPE | EEPE); // clear program bits
+
+ // interrupt?
+ //if((EECR & EERIE) && BIT(SREG,SREG_I)){
+ // SPSR ^= 0x80; // clear the interrupt
+ // trigger_interrupt(SPI_STC); // execute the vector
+ //}
+ }
+ // are we attempting to read?
+ else if (EECR & EERE)
+ {
+ // printf("attempting read of EEPROM\n");
+ cycleCounter += 4; // eeprom reads take 4 additonal cycles
+ int addr = (EEARH << 8) | EEARL;
+ if (addr < eepromSize)
+ EEDR = eeprom[addr];
+ EECR ^= EERE; // clear read bit
+
+ // interrupt?
+ //if((EECR & EERIE) && BIT(SREG,SREG_I)){
+ // SPSR ^= 0x80; // clear the interrupt
+ // trigger_interrupt(SPI_STC); // execute the vector
+ //}
+ }
+ }
+
+ // Process interrupts in order of priority
+
+ if (SREG & (1 << SREG_I))
+ {
+ // Note (Jubatian):
+ // The SD card's SPI interrupt trigger was within the SPI
+ // handling part in update_hardware, however it belongs to
+ // interrupt triggers. Priority order might be broken (but
+ // essentially the emulator behaved according to this order
+ // prior to this move).
+ if ((SPCR & 0x80) && (SPSR & 0x80))
+ {
+ // TODO: verify that SPI is dependent upon the global interrupt flag
+ SPSR ^= 0x80; // Clear the interrupt
+ trigger_interrupt(SPI_STC);
+ }
+ else if ((WDTCSR & (WDIF | WDIE)) == (WDIF | WDIE))
+ {
+ WDTCSR &= ~WDIF; // Clear watchdog flag
+ trigger_interrupt(WDT);
+ }
+ else if ((TIFR1 & (OCF1A | OCF1B | TOV1)) && (TIMSK1 & (OCIE1A | OCIE1B | TOIE1)))
+ {
+ if ((TIFR1 & OCF1A) && (TIMSK1 & OCIE1A))
+ {
+ TIFR1 &= ~OCF1A; // Clear CTC match flag
+ trigger_interrupt(TIMER1_COMPA);
+ }
+ else if ((TIFR1 & OCF1B) && (TIMSK1 & OCIE1B))
+ {
+ TIFR1 &= ~OCF1B; // Clear CTC match flag
+ trigger_interrupt(TIMER1_COMPB);
+ }
+ else if ((TIFR1 & TOV1) && (TIMSK1 & TOIE1))
+ {
+ TIFR1 &= ~TOV1; // Clear TOV1 flag
+ trigger_interrupt(TIMER1_OVF);
+ }
+ }
+ }
+}
+
+static const instructionList_t instructionList[] = {
+
+ {1, "ADC r%d, r%d ", 1, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0b0001110000000000, 0b0000000111110000, 0b0000001000001111},
+ {2, "ADD r%d, r%d ", 1, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0b0000110000000000, 0b0000000111110000, 0b0000001000001111},
+ {3, "ADIW r%d, %d ", 1, 2, 24, 0, 3, 1, 0, 0, 1, 2, 0b1001011000000000, 0b0000000000110000, 0b0000000011001111},
+ {4, "AND r%d, r%d ", 1, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0b0010000000000000, 0b0000000111110000, 0b0000001000001111},
+ {5, "ANDI r%d, %d ", 1, 1, 16, 0, 3, 1, 0, 0, 1, 1, 0b0111000000000000, 0b0000000011110000, 0b0000111100001111},
+ {6, "ASR r%d ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010000000101, 0b0000000111110000, 0b0000000000000000},
+ {7, "BCLR %d ", 7, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010010001000, 0b0000000001110000, 0b0000000000000000},
+ {8, "BLD r%d, %d ", 1, 1, 0, 0, 6, 1, 0, 0, 1, 1, 0b1111100000000000, 0b0000000111110000, 0b0000000000000111},
+ {9, "BRBC %d, %d ", 7, 1, 0, 0, 3, 1, 0, 1, 1, 2, 0b1111010000000000, 0b0000000000000111, 0b0000001111111000},
+ {10, "BRBS %d, %d ", 7, 1, 0, 0, 3, 1, 0, 1, 1, 2, 0b1111000000000000, 0b0000000000000111, 0b0000001111111000},
+ {11, "BREAK ", 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010110011000, 0b0000000000000000, 0b0000000000000000},
+ {12, "BSET %d ", 7, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010000001000, 0b0000000001110000, 0b0000000000000000},
+ {13, "BST r%d, %d ", 1, 1, 0, 0, 6, 1, 0, 0, 1, 1, 0b1111101000000000, 0b0000000111110000, 0b0000000000000111},
+ {14, "CALL %d (+ next word) ", 0, 1, 0, 0, 3, 1, 0, 0, 2, 4, 0b1001010000001110, 0b0000000000000000, 0b0000000111110001},
+ {15, "CBI io%d, %d ", 8, 1, 0, 0, 6, 1, 0, 0, 1, 2, 0b1001100000000000, 0b0000000011111000, 0b0000000000000111},
+ {16, "COM r%d ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010000000000, 0b0000000111110000, 0b0000000000000000},
+ {17, "CP r%d, r%d ", 1, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0b0001010000000000, 0b0000000111110000, 0b0000001000001111},
+ {18, "CPC r%d, r%d ", 1, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0b0000010000000000, 0b0000000111110000, 0b0000001000001111},
+ {19, "CPI r%d, %d ", 1, 1, 16, 0, 3, 1, 0, 0, 1, 1, 0b0011000000000000, 0b0000000011110000, 0b0000111100001111},
+ {20, "CPSE r%d, r%d ", 1, 1, 0, 0, 2, 1, 0, 0, 1, 3, 0b0001000000000000, 0b0000000111110000, 0b0000001000001111},
+ {21, "DEC r%d ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010000001010, 0b0000000111110000, 0b0000000000000000},
+ {22, "EOR r%d, r%d ", 1, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0b0010010000000000, 0b0000000111110000, 0b0000001000001111},
+ {23, "FMUL r%d, r%d ", 1, 1, 16, 0, 2, 1, 16, 0, 1, 2, 0b0000001100001000, 0b0000000001110000, 0b0000000000000111},
+ {24, "FMULS r%d, r%d ", 1, 1, 16, 0, 2, 1, 16, 0, 1, 2, 0b0000001110000000, 0b0000000001110000, 0b0000000000000111},
+ {25, "FMULSU r%d, r%d ", 1, 1, 16, 0, 2, 1, 16, 0, 1, 2, 0b0000001110001000, 0b0000000001110000, 0b0000000000000111},
+ {26, "ICALL ", 0, 1, 0, 0, 0, 1, 0, 0, 1, 3, 0b1001010100001001, 0b0000000000000000, 0b0000000000000000},
+ {27, "IJMP ", 0, 1, 0, 0, 0, 1, 0, 0, 1, 2, 0b1001010000001001, 0b0000000000000000, 0b0000000000000000},
+ {28, "IN r%d, io%d ", 1, 1, 0, 0, 8, 1, 0, 0, 1, 1, 0b1011000000000000, 0b0000000111110000, 0b0000011000001111},
+ {29, "INC r%d ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010000000011, 0b0000000111110000, 0b0000000000000000},
+ {30, "JMP %d (+ next word) ", 0, 1, 0, 0, 3, 1, 0, 0, 2, 3, 0b1001010000001100, 0b0000000000000000, 0b0000000111110001},
+ {31, "LD r%d, -X ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 3, 0b1001000000001110, 0b0000000111110000, 0b0000000000000000},
+ {32, "LD r%d, -Y ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 3, 0b1001000000001010, 0b0000000111110000, 0b0000000000000000},
+ {33, "LD r%d, -Z ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 3, 0b1001000000000010, 0b0000000111110000, 0b0000000000000000},
+ {34, "LD r%d, X ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 3, 0b1001000000001100, 0b0000000111110000, 0b0000000000000000},
+ {35, "LD r%d, X+ ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 3, 0b1001000000001101, 0b0000000111110000, 0b0000000000000000},
+ {36, "LD r%d, Y+ ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 3, 0b1001000000001001, 0b0000000111110000, 0b0000000000000000},
+ {37, "LD r%d, Y+%d ", 1, 1, 0, 0, 5, 1, 0, 0, 1, 3, 0b1000000000001000, 0b0000000111110000, 0b0010110000000111},
+ {38, "LD r%d, Z+ ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 3, 0b1001000000000001, 0b0000000111110000, 0b0000000000000000},
+ {39, "LD r%d, Z+%d ", 1, 1, 0, 0, 5, 1, 0, 0, 1, 3, 0b1000000000000000, 0b0000000111110000, 0b0010110000000111},
+ {40, "LDI r%d, %d ", 1, 1, 16, 0, 3, 1, 0, 0, 1, 1, 0b1110000000000000, 0b0000000011110000, 0b0000111100001111},
+ {41, "LDS r%d, %d (+next word) ", 1, 1, 0, 0, 0, 1, 0, 0, 2, 2, 0b1001000000000000, 0b0000000111110000, 0b0000000000000000},
+ {42, "LPM ", 0, 1, 0, 0, 0, 1, 0, 0, 1, 3, 0b1001010111001000, 0b0000000000000000, 0b0000000000000000},
+ {43, "LPM r%d, Z ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 3, 0b1001000000000100, 0b0000000111110000, 0b0000000000000000},
+ {44, "LPM r%d, Z+ ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 3, 0b1001000000000101, 0b0000000111110000, 0b0000000000000000},
+ {45, "LSR r%d ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010000000110, 0b0000000111110000, 0b0000000000000000},
+ {46, "MOV r%d, r%d ", 1, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0b0010110000000000, 0b0000000111110000, 0b0000001000001111},
+ {47, "MOVW r%d, r%d ", 1, 2, 0, 0, 2, 2, 0, 0, 1, 1, 0b0000000100000000, 0b0000000011110000, 0b0000000000001111},
+ {48, "MUL r%d, r%d ", 1, 1, 0, 0, 2, 1, 0, 0, 1, 2, 0b1001110000000000, 0b0000000111110000, 0b0000001000001111},
+ {49, "MULS r%d, r%d ", 1, 1, 16, 0, 2, 1, 16, 0, 1, 2, 0b0000001000000000, 0b0000000011110000, 0b0000000000001111},
+ {50, "MULSU r%d, r%d ", 1, 1, 16, 0, 2, 1, 16, 0, 1, 2, 0b0000001100000000, 0b0000000001110000, 0b0000000000000111},
+ {51, "NEG r%d ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010000000001, 0b0000000111110000, 0b0000000000000000},
+ {52, "NOP ", 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b0000000000000000, 0b0000000000000000, 0b0000000000000000},
+ {53, "OR r%d, r%d ", 1, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0b0010100000000000, 0b0000000111110000, 0b0000001000001111},
+ {54, "ORI r%d, %d ", 1, 1, 16, 0, 3, 1, 0, 0, 1, 1, 0b0110000000000000, 0b0000000011110000, 0b0000111100001111},
+ {55, "OUT io%d, r%d ", 8, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0b1011100000000000, 0b0000011000001111, 0b0000000111110000},
+ {56, "POP r%d ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 2, 0b1001000000001111, 0b0000000111110000, 0b0000000000000000},
+ {57, "PUSH r%d ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 2, 0b1001001000001111, 0b0000000111110000, 0b0000000000000000},
+ {58, "RCALL %d ", 0, 1, 0, 0, 3, 1, 0, 1, 1, 3, 0b1101000000000000, 0b0000000000000000, 0b0000111111111111},
+ {59, "RET ", 0, 1, 0, 0, 0, 1, 0, 0, 1, 4, 0b1001010100001000, 0b0000000000000000, 0b0000000000000000},
+ {60, "RETI ", 0, 1, 0, 0, 0, 1, 0, 0, 1, 4, 0b1001010100011000, 0b0000000000000000, 0b0000000000000000},
+ {61, "RJMP %d ", 0, 1, 0, 0, 3, 1, 0, 1, 1, 2, 0b1100000000000000, 0b0000000000000000, 0b0000111111111111},
+ {62, "ROR r%d ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010000000111, 0b0000000111110000, 0b0000000000000000},
+ {63, "SBC r%d, r%d ", 1, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0b0000100000000000, 0b0000000111110000, 0b0000001000001111},
+ {64, "SBCI r%d, %d ", 1, 1, 16, 0, 3, 1, 0, 0, 1, 1, 0b0100000000000000, 0b0000000011110000, 0b0000111100001111},
+ {65, "SBI io%d, %d ", 8, 1, 0, 0, 6, 1, 0, 0, 1, 2, 0b1001101000000000, 0b0000000011111000, 0b0000000000000111},
+ {66, "SBIC io%d, %d ", 8, 1, 0, 0, 6, 1, 0, 0, 1, 3, 0b1001100100000000, 0b0000000011111000, 0b0000000000000111},
+ {67, "SBIS io%d, %d ", 8, 1, 0, 0, 6, 1, 0, 0, 1, 3, 0b1001101100000000, 0b0000000011111000, 0b0000000000000111},
+ {68, "SBIW r%d, %d ", 1, 2, 24, 0, 3, 1, 0, 0, 1, 2, 0b1001011100000000, 0b0000000000110000, 0b0000000011001111},
+ {69, "SBRC r%d, %d ", 2, 1, 0, 0, 6, 1, 0, 0, 1, 3, 0b1111110000000000, 0b0000000111110000, 0b0000000000000111},
+ {70, "SBRS r%d, %d ", 2, 1, 0, 0, 6, 1, 0, 0, 1, 3, 0b1111111000000000, 0b0000000111110000, 0b0000000000000111},
+ {71, "SLEEP ", 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010110001000, 0b0000000000000000, 0b0000000000000000},
+ {72, "SPM z+ ", 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010111101000, 0b0000000000000000, 0b0000000000000000},
+ {73, "ST -x, r%d ", 2, 1, 0, 0, 0, 1, 0, 0, 1, 2, 0b1001001000001110, 0b0000000111110000, 0b0000000000000000},
+ {74, "ST -y, r%d ", 2, 1, 0, 0, 0, 1, 0, 0, 1, 2, 0b1001001000001010, 0b0000000111110000, 0b0000000000000000},
+ {75, "ST -z, r%d ", 2, 1, 0, 0, 0, 1, 0, 0, 1, 2, 0b1001001000000010, 0b0000000111110000, 0b0000000000000000},
+ {76, "ST x, r%d ", 2, 1, 0, 0, 0, 1, 0, 0, 1, 2, 0b1001001000001100, 0b0000000111110000, 0b0000000000000000},
+ {77, "ST x+, r%d ", 2, 1, 0, 0, 0, 1, 0, 0, 1, 2, 0b1001001000001101, 0b0000000111110000, 0b0000000000000000},
+ {78, "ST y+, r%d ", 2, 1, 0, 0, 0, 1, 0, 0, 1, 2, 0b1001001000001001, 0b0000000111110000, 0b0000000000000000},
+ {79, "ST y+q, r%d (q=%d) ", 1, 1, 0, 0, 5, 1, 0, 0, 1, 2, 0b1000001000001000, 0b0000000111110000, 0b0010110000000111},
+ {80, "ST z+, r%d ", 2, 1, 0, 0, 0, 1, 0, 0, 1, 2, 0b1001001000000001, 0b0000000111110000, 0b0000000000000000},
+ {81, "ST z+q, r%d (q=%d) ", 1, 1, 0, 0, 5, 1, 0, 0, 1, 2, 0b1000001000000000, 0b0000000111110000, 0b0010110000000111},
+ {82, "STS k, r%d ", 1, 1, 0, 0, 0, 1, 0, 0, 2, 2, 0b1001001000000000, 0b0000000111110000, 0b0000000000000000},
+ {83, "SUB r%d, r%d ", 1, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0b0001100000000000, 0b0000000111110000, 0b0000001000001111},
+ {84, "SUBI r%d, %d ", 1, 1, 16, 0, 3, 1, 0, 0, 1, 1, 0b0101000000000000, 0b0000000011110000, 0b0000111100001111},
+ {85, "SWAP r%d ", 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010000000010, 0b0000000111110000, 0b0000000000000000},
+ {86, "WDR ", 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0b1001010110101000, 0b0000000000000000, 0b0000000000000000},
+
+ {0, "END", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0b0000000000000000, 0b0000000000000000, 0b0000000000000000}
+
+};
+
+unsigned int avr8::exec()
+{
+ currentPc = pc;
+ const instructionDecode_t insnDecoded = progmemDecoded[pc];
+ const u8 opNum = insnDecoded.opNum;
+ const u8 arg1_8 = insnDecoded.arg1;
+ const s16 arg2_8 = insnDecoded.arg2;
+
+ const unsigned int startcy = cycleCounter;
+ u8 Rd, Rr, R, CH;
+ u16 uTmp, Rd16, R16;
+ s16 sTmp;
+
+ //Program counter must be incremented *after* GDB
+ pc++;
+
+ // Instruction decoder notes:
+ //
+ // The instruction's timing is determined by how many update_hardware
+ // calls are executed during its decoding. One update_hardware call is
+ // placed after the instruction decoder since all instructions take at
+ // least one cycle (there is at least one cycle after the last read /
+ // write effect), so only multi-cycle instructions need to perform
+ // extra calls to update_hardware.
+ //
+ // The read_sram and write_sram calls only access the sram.
+ // TODO: I am not sure whether the instructions calling these only do
+ // so on the real thing, but doing otherwise is unlikely, and may even
+ // be buggy then (the behavior of things like having the stack over IO
+ // area...). This solution is at least fast for these instructions.
+
+ switch (opNum)
+ {
+
+ case 1: // 0001 11rd dddd rrrr (1) ADC Rd,Rr (ROL is ADC Rd,Rd)
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ R = Rd + Rr + C;
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM | SREG_HM);
+ UPDATE_HC_ADD;
+ UPDATE_SVN_ADD;
+ UPDATE_Z;
+ r[arg1_8] = R;
+ break;
+
+ case 2: // 0000 11rd dddd rrrr (1) ADD Rd,Rr (LSL is ADD Rd,Rd)
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ R = Rd + Rr;
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM | SREG_HM);
+ UPDATE_HC_ADD;
+ UPDATE_SVN_ADD;
+ UPDATE_Z;
+ r[arg1_8] = R;
+ break;
+
+ case 3: // 1001 0110 KKdd KKKK (2) ADIW Rd+1:Rd,K (16-bit add to upper four register pairs)
+ Rd = arg1_8;
+ Rr = arg2_8;
+ Rd16 = r[Rd] | (r[Rd + 1] << 8);
+ R16 = Rd16 + Rr;
+ r[Rd] = (u8)R16;
+ r[Rd + 1] = (u8)(R16 >> 8);
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ set_bit_1(SREG, SREG_V, ((~Rd16 & R16) & 0x8000) >> 15);
+ set_bit_1(SREG, SREG_N, (R16 & 0x8000) >> 15);
+ UPDATE_S;
+ set_bit_inv(SREG, SREG_Z, R16);
+ set_bit_1(SREG, SREG_C, ((~R16 & Rd16) & 0x8000) >> 15);
+ update_hardware();
+ break;
+
+ case 4: // 0010 00rd dddd rrrr (1) AND Rd,Rr (TST is AND Rd,Rd)
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ R = Rd & Rr;
+ clr_bits(SREG, SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ UPDATE_SVN_LOGICAL;
+ UPDATE_Z;
+ r[arg1_8] = R;
+ break;
+
+ case 5: // 0111 KKKK dddd KKKK (1) ANDI Rd,K (CBR is ANDI with K complemented)
+ Rd = r[arg1_8];
+ Rr = arg2_8;
+ R = Rd & Rr;
+ clr_bits(SREG, SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ UPDATE_SVN_LOGICAL;
+ UPDATE_Z;
+ r[arg1_8] = R;
+ break;
+
+ case 6: // 1001 010d dddd 0101 (1) ASR Rd
+ Rd = r[arg1_8];
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ set_bit_1(SREG, SREG_C, Rd & 1);
+ r[arg1_8] = R = (Rd >> 1) | (Rd & 0x80);
+ UPDATE_N;
+ set_bit_1(SREG, SREG_V, (R >> 7) ^ (Rd & 1));
+ UPDATE_S;
+ UPDATE_Z;
+ break;
+
+ case 8: // 1111 100d dddd 0bbb (1) BLD Rd,b
+ Rd = arg1_8;
+ store_bit_1(r[Rd], arg2_8, (SREG >> SREG_T) & 1U);
+ break;
+
+ case 7: // 1001 0100 1sss 1000 (1) BCLR s (CLC, etc are aliases with sss implicit)
+ Rd = arg1_8;
+ SREG &= ~(1U << Rd);
+ break;
+
+ case 9: // 1111 01kk kkkk ksss (1/2) BRBC s,k (BRCC, etc are aliases for this with sss implicit)
+ if (!(SREG & (1 << (arg1_8))))
+ {
+ update_hardware();
+ pc += arg2_8;
+ }
+ break;
+
+ case 10: // 1111 00kk kkkk ksss (1/2) BRBS s,k (same here)
+ if (SREG & (1 << (arg1_8)))
+ {
+ update_hardware();
+ pc += arg2_8;
+ }
+ break;
+
+ case 11: // 1001 0101 1001 1000 (?) BREAK
+ // no operation
+ break;
+
+ case 12: // 1001 0100 0sss 1000 (1) BSET s (SEC, etc are aliases with sss implicit)
+ Rd = arg1_8;
+ SREG |= (1U << Rd);
+ break;
+
+ case 13: // 1111 101d dddd 0bbb (1) BST Rd,b
+ Rd = r[arg1_8];
+ store_bit_1(SREG, SREG_T, (Rd >> (arg2_8)) & 1U);
+ break;
+
+ case 14: // 1001 010k kkkk 111k (4) CALL k (next word is rest of address)
+ // Note: 64K progmem, so 'k' in first insn word is unused
+ update_hardware();
+ update_hardware();
+ update_hardware();
+ write_sram(SP, (pc + 1));
+ DEC_SP;
+ write_sram(SP, (pc + 1) >> 8);
+ DEC_SP;
+ pc = arg2_8;
+ break;
+
+ case 15: // 1001 1000 AAAA Abbb (2) CBI A,b
+ update_hardware();
+ Rd = arg1_8;
+ write_io(Rd, read_io(Rd) & ~(1 << (arg2_8)));
+ break;
+
+ case 16: // 1001 010d dddd 0000 (1) COM Rd
+ r[arg1_8] = R = ~r[arg1_8];
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ UPDATE_SVN_LOGICAL;
+ UPDATE_Z;
+ SET_C;
+ break;
+
+ case 17: // 0001 01rd dddd rrrr (1) CP Rd,Rr
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ R = Rd - Rr;
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM | SREG_HM);
+ UPDATE_HC_SUB;
+ UPDATE_SVN_SUB;
+ UPDATE_Z;
+ break;
+
+ case 18: // 0000 01rd dddd rrrr (1) CPC Rd,Rr
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ R = Rd - Rr - C;
+ clr_bits(SREG, SREG_CM | SREG_NM | SREG_VM | SREG_SM | SREG_HM);
+ UPDATE_HC_SUB;
+ UPDATE_SVN_SUB;
+ UPDATE_CLEAR_Z;
+ break;
+
+ case 19: // 0011 KKKK dddd KKKK (1) CPI Rd,K
+ Rd = r[arg1_8];
+ Rr = arg2_8;
+ R = Rd - Rr;
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM | SREG_HM);
+ UPDATE_HC_SUB;
+ UPDATE_SVN_SUB;
+ UPDATE_Z;
+ break;
+
+ case 20: // 0001 00rd dddd rrrr (1/2/3) CPSE Rd,Rr
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ if (Rd == Rr)
+ {
+ unsigned int icc = get_insn_size(progmemDecoded[pc].opNum);
+ pc += icc;
+ while (icc != 0U)
+ {
+ update_hardware();
+ icc--;
+ }
+ }
+ break;
+
+ case 21: // 1001 010d dddd 1010 (1) DEC Rd
+ R = --r[arg1_8];
+ clr_bits(SREG, SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ UPDATE_N;
+ set_bit_inv(SREG, SREG_V, (unsigned int)(R)-0x7FU);
+ UPDATE_S;
+ UPDATE_Z;
+ break;
+
+ case 22: // 0010 01rd dddd rrrr (1) EOR Rd,Rr (CLR is EOR Rd,Rd)
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ R = Rd ^ Rr;
+ clr_bits(SREG, SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ UPDATE_SVN_LOGICAL;
+ UPDATE_Z;
+ r[arg1_8] = R;
+ break;
+
+ case 23: // 0000 0011 0ddd 1rrr (2) FMUL Rd,Rr (registers are in 16-23 range)
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ uTmp = (u8)Rd * (u8)Rr;
+ r0 = (u8)(uTmp << 1);
+ r1 = (u8)(uTmp >> 7);
+ clr_bits(SREG, SREG_CM | SREG_ZM);
+ UPDATE_CZ_MUL(uTmp);
+ update_hardware();
+ break;
+
+ case 24: // 0000 0011 1ddd 0rrr (2) FMULS Rd,Rr
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ sTmp = (s8)Rd * (s8)Rr;
+ r0 = (u8)(sTmp << 1);
+ r1 = (u8)(sTmp >> 7);
+ clr_bits(SREG, SREG_CM | SREG_ZM);
+ UPDATE_CZ_MUL(sTmp);
+ update_hardware();
+ break;
+
+ case 25: // 0000 0011 1ddd 1rrr (2) FMULSU Rd,Rr
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ sTmp = (s8)Rd * (u8)Rr;
+ r0 = (u8)(sTmp << 1);
+ r1 = (u8)(sTmp >> 7);
+ clr_bits(SREG, SREG_CM | SREG_ZM);
+ UPDATE_CZ_MUL(sTmp);
+ update_hardware();
+ break;
+
+ case 26: // 1001 0101 0000 1001 (3) ICALL (call thru Z register)
+ update_hardware();
+ update_hardware();
+ write_sram(SP, u8(pc));
+ DEC_SP;
+ write_sram(SP, (pc) >> 8);
+ DEC_SP;
+ pc = Z;
+ break;
+
+ case 27: // 1001 0100 0000 1001 (2) IJMP (jump thru Z register)
+ update_hardware_fast();
+ pc = Z;
+ break;
+
+ case 28: // 1011 0AAd dddd AAAA (1) IN Rd,A
+ Rd = arg1_8;
+ Rr = arg2_8;
+ r[Rd] = read_io(Rr);
+ break;
+
+ case 29: // 1001 010d dddd 0011 (1) INC Rd
+ R = ++r[arg1_8];
+ clr_bits(SREG, SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ UPDATE_N;
+ set_bit_inv(SREG, SREG_V, (unsigned int)(R)-0x80U);
+ UPDATE_S;
+ UPDATE_Z;
+ break;
+
+ case 30: // 1001 010k kkkk 110k (3) JMP k (next word is rest of address)
+ // Note: 64K progmem, so 'k' in first insn word is unused
+ update_hardware();
+ update_hardware();
+ pc = arg2_8;
+ break;
+
+ case 31: // 1001 000d dddd 1110 (2) LD rd,-X
+ update_hardware();
+ DEC_X;
+ r[arg1_8] = read_sram_io(X);
+ break;
+
+ case 32: // 1001 000d dddd 1010 (2) LD Rd,-Y
+ update_hardware();
+ DEC_Y;
+ r[arg1_8] = read_sram_io(Y);
+ break;
+
+ case 33: // 1001 000d dddd 0010 (2) LD Rd,-Z
+ update_hardware();
+ DEC_Z;
+ r[arg1_8] = read_sram_io(Z);
+ break;
+
+ case 34: // 1001 000d dddd 1100 (2) LD rd,X
+ update_hardware();
+ r[arg1_8] = read_sram_io(X);
+ break;
+
+ case 35: // 1001 000d dddd 1101 (2) LD rd,X+
+ update_hardware();
+ r[arg1_8] = read_sram_io(X);
+ INC_X;
+ break;
+
+ case 36: // 1001 000d dddd 1001 (2) LD Rd,Y+
+ update_hardware();
+ r[arg1_8] = read_sram_io(Y);
+ INC_Y;
+ break;
+
+ case 37: // 10q0 qq0d dddd 1qqq (2) LDD Rd,Y+q
+ update_hardware();
+ Rd = arg1_8;
+ Rr = arg2_8;
+ r[Rd] = read_sram_io(Y + Rr);
+ break;
+
+ case 38: // 1001 000d dddd 0001 (2) LD Rd,Z+
+ update_hardware();
+ r[arg1_8] = read_sram_io(Z);
+ INC_Z;
+ break;
+
+ case 39: // 10q0 qq0d dddd 0qqq (2) LDD Rd,Z+q
+ update_hardware();
+ Rd = arg1_8;
+ Rr = arg2_8;
+ r[Rd] = read_sram_io(Z + Rr);
+ break;
+
+ case 40: // 1110 KKKK dddd KKKK (1) LDI Rd,K (SER is just LDI Rd,255)
+ r[arg1_8] = arg2_8;
+ break;
+
+ case 41: // 1001 000d dddd 0000 (2) LDS Rd,k (next word is rest of address)
+ update_hardware();
+ r[arg1_8] = read_sram_io(arg2_8);
+ pc++;
+ break;
+
+ case 42: // 1001 0101 1100 1000 (3) LPM (r0 implied, why is this special?)
+ update_hardware();
+ update_hardware();
+ r0 = read_progmem(Z);
+ break;
+
+ case 43: // 1001 000d dddd 0100 (3) LPM Rd,Z
+ update_hardware_fast();
+ update_hardware_fast();
+ r[arg1_8] = read_progmem(Z);
+ break;
+
+ case 44: // 1001 000d dddd 0101 (3) LPM Rd,Z+
+ update_hardware_fast();
+ update_hardware_fast();
+ r[arg1_8] = read_progmem(Z);
+ INC_Z;
+ break;
+
+ case 45: // 1001 010d dddd 0110 (1) LSR Rd
+ Rd = r[arg1_8];
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ set_bit_1(SREG, SREG_C, Rd & 1);
+ r[arg1_8] = R = (Rd >> 1);
+ UPDATE_N;
+ set_bit_1(SREG, SREG_V, Rd & 1);
+ UPDATE_S;
+ UPDATE_Z;
+ break;
+
+ case 46: // 0010 11rd dddd rrrr (1) MOV Rd,Rr
+ r[arg1_8] = r[arg2_8];
+ break;
+
+ case 47: // 0000 0001 dddd rrrr (1) MOVW Rd+1:Rd,Rr+1:R
+ Rd = arg1_8;
+ Rr = arg2_8;
+ r[Rd] = r[Rr];
+ r[Rd + 1] = r[Rr + 1];
+ break;
+
+ case 48: // 1001 11rd dddd rrrr (2) MUL Rd,Rr
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ uTmp = Rd * Rr;
+ r0 = (u8)uTmp;
+ r1 = (u8)(uTmp >> 8);
+ clr_bits(SREG, SREG_CM | SREG_ZM);
+ UPDATE_CZ_MUL(uTmp);
+ update_hardware_fast();
+ break;
+
+ case 49: // 0000 0010 dddd rrrr (2) MULS Rd,Rr
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ sTmp = (s8)Rd * (s8)Rr;
+ r0 = (u8)sTmp;
+ r1 = (u8)(sTmp >> 8);
+ clr_bits(SREG, SREG_CM | SREG_ZM);
+ UPDATE_CZ_MUL(sTmp);
+ update_hardware();
+ break;
+
+ case 50: // 0000 0011 0ddd 0rrr (2) MULSU Rd,Rr (registers are in 16-23 range)
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ sTmp = (s8)Rd * (u8)Rr;
+ r0 = (u8)sTmp;
+ r1 = (u8)(sTmp >> 8);
+ clr_bits(SREG, SREG_CM | SREG_ZM);
+ UPDATE_CZ_MUL(sTmp);
+ update_hardware();
+ break;
+
+ case 51: // 1001 010d dddd 0001 (1) NEG Rd
+ Rr = r[arg1_8];
+ Rd = 0;
+ r[arg1_8] = R = Rd - Rr;
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM | SREG_HM);
+ UPDATE_HC_SUB;
+ UPDATE_SVN_SUB;
+ UPDATE_Z;
+ break;
+
+ case 52: // 0000 0000 0000 0000 (1) NOP
+ break;
+
+ case 53: // 0010 10rd dddd rrrr (1) OR Rd,Rr
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ R = Rd | Rr;
+ clr_bits(SREG, SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ UPDATE_SVN_LOGICAL;
+ UPDATE_Z;
+ r[arg1_8] = R;
+ break;
+
+ case 54: // 0110 KKKK dddd KKKK (1) ORI Rd,K (same as SBR insn)
+ Rd = r[arg1_8];
+ Rr = arg2_8;
+ R = Rd | Rr;
+ clr_bits(SREG, SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ UPDATE_SVN_LOGICAL;
+ UPDATE_Z;
+ r[arg1_8] = R;
+ break;
+
+ case 55: // 1011 1AAd dddd AAAA (1) OUT A,Rd
+ Rd = arg2_8;
+ Rr = arg1_8;
+ write_io(Rr, r[Rd]);
+ break;
+
+ case 56: // 1001 000d dddd 1111 (2) POP Rd
+ update_hardware();
+ INC_SP;
+ r[arg1_8] = read_sram(SP);
+ break;
+
+ case 57: // 1001 001d dddd 1111 (2) PUSH Rd
+ update_hardware();
+ write_sram(SP, r[arg1_8]);
+ DEC_SP;
+ break;
+
+ case 58: // 1101 kkkk kkkk kkkk (3) RCALL k
+ update_hardware();
+ update_hardware();
+ write_sram(SP, (u8)pc);
+ DEC_SP;
+ write_sram(SP, pc >> 8);
+ DEC_SP;
+ pc += arg2_8;
+ break;
+
+ case 59: // 1001 0101 0000 1000 (4) RET
+ update_hardware();
+ update_hardware();
+ update_hardware();
+ INC_SP;
+ pc = read_sram(SP) << 8;
+ INC_SP;
+ pc |= read_sram(SP);
+ break;
+
+ case 60: // 1001 0101 0001 1000 (4) RETI
+ update_hardware();
+ update_hardware();
+ update_hardware();
+ INC_SP;
+ pc = read_sram(SP) << 8;
+ INC_SP;
+ pc |= read_sram(SP);
+ SREG |= (1 << SREG_I);
+ //--interruptLevel;
+ break;
+
+ case 61: // 1100 kkkk kkkk kkkk (2) RJMP k
+ update_hardware_fast();
+ pc += arg2_8;
+ break;
+
+ case 62: // 1001 010d dddd 0111 (1) ROR Rd
+ Rd = r[arg1_8];
+ r[arg1_8] = R = (Rd >> 1) | ((SREG & 1) << 7);
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ set_bit_1(SREG, SREG_C, Rd & 1);
+ UPDATE_N;
+ set_bit_1(SREG, SREG_V, (R >> 7) ^ (Rd & 1));
+ UPDATE_S;
+ UPDATE_Z;
+ break;
+
+ case 63: // 0000 10rd dddd rrrr (1) SBC Rd,Rr
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ R = Rd - Rr - C;
+ clr_bits(SREG, SREG_CM | SREG_NM | SREG_VM | SREG_SM | SREG_HM);
+ UPDATE_HC_SUB;
+ UPDATE_SVN_SUB;
+ UPDATE_CLEAR_Z;
+ r[arg1_8] = R;
+ break;
+
+ case 64: // 0100 KKKK dddd KKKK (1) SBCI Rd,K
+ Rd = r[arg1_8];
+ Rr = arg2_8;
+ R = Rd - Rr - C;
+ clr_bits(SREG, SREG_CM | SREG_NM | SREG_VM | SREG_SM | SREG_HM);
+ UPDATE_HC_SUB;
+ UPDATE_SVN_SUB;
+ UPDATE_CLEAR_Z;
+ r[arg1_8] = R;
+ break;
+
+ case 65: // 1001 1010 AAAA Abbb (2) SBI A,b
+ update_hardware();
+ Rd = arg1_8;
+ write_io(Rd, read_io(Rd) | (1 << (arg2_8)));
+ break;
+
+ case 66: // 1001 1001 AAAA Abbb (1/2/3) SBIC A,b
+ Rd = arg1_8;
+ if (!(read_io(Rd) & (1 << (arg2_8))))
+ {
+ unsigned int icc = get_insn_size(progmemDecoded[pc].opNum);
+ pc += icc;
+ while (icc != 0U)
+ {
+ update_hardware();
+ icc--;
+ }
+ }
+ break;
+
+ case 67: // 1001 1011 AAAA Abbb (1/2/3) SBIS A,b
+ Rd = arg1_8;
+ if (read_io(Rd) & (1 << (arg2_8)))
+ {
+ unsigned int icc = get_insn_size(progmemDecoded[pc].opNum);
+ pc += icc;
+ while (icc != 0U)
+ {
+ update_hardware();
+ icc--;
+ }
+ }
+ break;
+
+ case 68: // 1001 0111 KKdd KKKK (2) SBIW Rd+1:Rd,K
+ Rd = arg1_8;
+ Rr = arg2_8;
+ Rd16 = r[Rd] | (r[Rd + 1] << 8);
+ R16 = Rd16 - Rr;
+ r[Rd] = (u8)R16;
+ r[Rd + 1] = (u8)(R16 >> 8);
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM);
+ set_bit_1(SREG, SREG_V, ((Rd16 & ~R16) & 0x8000) >> 15);
+ set_bit_1(SREG, SREG_N, (R16 & 0x8000) >> 15);
+ UPDATE_S;
+ set_bit_inv(SREG, SREG_Z, R16);
+ set_bit_1(SREG, SREG_C, ((R16 & ~Rd16) & 0x8000) >> 15);
+ update_hardware();
+ break;
+
+ case 69: // 1111 110r rrrr 0bbb (1/2/3) SBRC Rr,b
+ Rd = r[arg1_8];
+ if (((Rd >> (arg2_8)) & 1U) == 0)
+ {
+ unsigned int icc = get_insn_size(progmemDecoded[pc].opNum);
+ pc += icc;
+ while (icc != 0U)
+ {
+ update_hardware();
+ icc--;
+ }
+ }
+ break;
+
+ case 70: // 1111 111r rrrr 0bbb (1/2/3) SBRS Rr,b
+ Rd = r[arg1_8];
+ if (((Rd >> (arg2_8)) & 1U) == 1)
+ {
+ unsigned int icc = get_insn_size(progmemDecoded[pc].opNum);
+ pc += icc;
+ while (icc != 0U)
+ {
+ update_hardware();
+ icc--;
+ }
+ }
+ break;
+
+ case 71: // 1001 0101 1000 1000 (?) SLEEP
+ elapsedCyclesSleep = cycleCounter - lastCyclesSleep;
+ lastCyclesSleep = cycleCounter;
+ break;
+
+ case 72: // 1001 0101 1110 1000 (?) SPM Z (writes R1:R0)
+ update_hardware();
+ update_hardware(); // Cycle count undocumented?!?!?
+ update_hardware(); // (4 cycles emulated)
+ if (Z >= progSize / 2)
+ {
+ fprintf(stderr, "illegal write to progmem addr %x\n", Z);
+ shutdown(1);
+ }
+ else
+ {
+ progmem[Z] = r0 | (r1 << 8);
+ decodeFlash(Z - 1);
+ decodeFlash(Z);
+ }
+ break;
+
+ case 73: // 1001 001r rrrr 1110 (2) ST -X,Rr
+ update_hardware();
+ DEC_X;
+ write_sram_io(X, r[arg1_8]);
+ break;
+
+ case 74: // 1001 001r rrrr 1010 (2) ST -Y,Rr
+ update_hardware();
+ DEC_Y;
+ write_sram_io(Y, r[arg1_8]);
+ break;
+
+ case 75: // 1001 001r rrrr 0010 (2) ST -Z,Rr
+ update_hardware();
+ DEC_Z;
+ write_sram_io(Z, r[arg1_8]);
+ break;
+
+ case 76: // 1001 001r rrrr 1100 (2) ST X,Rr
+ update_hardware();
+ write_sram_io(X, r[arg1_8]);
+ break;
+
+ case 77: // 1001 001r rrrr 1101 (2) ST X+,Rr
+ update_hardware();
+ write_sram_io(X, r[arg1_8]);
+ INC_X;
+ break;
+
+ case 78: // 1001 001r rrrr 1001 (2) ST Y+,Rr
+ update_hardware();
+ write_sram_io(Y, r[arg1_8]);
+ INC_Y;
+ break;
+
+ case 79: // 10q0 qq1d dddd 1qqq (2) STD Y+q,Rd
+ Rd = arg1_8;
+ Rr = arg2_8;
+ update_hardware();
+ write_sram_io(Y + Rr, r[Rd]);
+ break;
+
+ case 80: // 1001 001r rrrr 0001 (2) ST Z+,Rr
+ update_hardware();
+ write_sram_io(Z, r[arg1_8]);
+ INC_Z;
+ break;
+
+ case 81: // 10q0 qq1d dddd 0qqq (2) STD Z+q,Rd
+ Rd = arg1_8;
+ Rr = arg2_8;
+ update_hardware();
+ write_sram_io(Z + Rr, r[Rd]);
+ break;
+
+ case 82: // 1001 001d dddd 0000 (2) STS k,Rr (next word is rest of address)
+ update_hardware();
+ write_sram_io(arg2_8, r[arg1_8]);
+ pc++;
+ break;
+
+ case 83: // 0001 10rd dddd rrrr (1) SUB Rd,Rr
+ Rd = r[arg1_8];
+ Rr = r[arg2_8];
+ R = Rd - Rr;
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM | SREG_HM);
+ UPDATE_HC_SUB;
+ UPDATE_SVN_SUB;
+ UPDATE_Z;
+ r[arg1_8] = R;
+ break;
+
+ case 84: // 0101 KKKK dddd KKKK (1) SUBI Rd,K
+ Rd = r[arg1_8];
+ Rr = arg2_8;
+ R = Rd - Rr;
+ clr_bits(SREG, SREG_CM | SREG_ZM | SREG_NM | SREG_VM | SREG_SM | SREG_HM);
+ UPDATE_HC_SUB;
+ UPDATE_SVN_SUB;
+ UPDATE_Z;
+ r[arg1_8] = R;
+ break;
+
+ case 85: // 1001 010d dddd 0010 (1) SWAP Rd
+ Rd = r[arg1_8];
+ r[arg1_8] = (Rd >> 4) | (Rd << 4);
+ break;
+
+ case 86: // 1001 0101 1010 1000 (1) WDR
+ //watchdog is based on a RC oscillator
+ //so add some random variation to simulate entropy
+ watchdogTimer = rand() % 1024;
+ if (prevWDR)
+ {
+ printf("WDR measured %u cycles\n", cycleCounter - prevWDR);
+ prevWDR = 0;
+ }
+ else
+ {
+ prevWDR = cycleCounter + 1;
+ }
+ break;
+
+ default: // Illegal op.
+ ILLEGAL_OP;
+ break;
+ }
+
+ // Process hardware for the last instruction cycle
+
+ update_hardware_fast();
+
+ // Run instruction precise emulation tasks
+
+ update_hardware_ins();
+
+ // Done, return cycles consumed during the processing of this instruction.
+
+ return cycleCounter - startcy;
+}
+
+u16 avr8::decodeArg(u16 flash, u16 argMask, u8 argNeg)
+{
+ u16 argMaskShift = 0x0001;
+ u16 decodeShift = 0x0001;
+ u16 arg = 0x0000;
+
+ while (argMaskShift != 0x4000)
+ { //0x4000 is highest bit in argMask that can be set
+ if ((argMaskShift & argMask) != 0)
+ {
+ if ((argMaskShift & flash) != 0)
+ {
+ arg = arg | decodeShift;
+ }
+ decodeShift = decodeShift << 1;
+ }
+ argMaskShift = argMaskShift << 1;
+ }
+ decodeShift = decodeShift >> 1;
+
+ if ((argNeg == 1) && ((decodeShift & arg) != 0))
+ {
+ while (decodeShift != 0x8000)
+ {
+ decodeShift = decodeShift << 1;
+ arg = arg | decodeShift;
+ }
+ }
+
+ return (arg);
+}
+
+void avr8::instructionDecode(u16 address)
+{
+ int i = 0;
+ u16 rawFlash;
+ u16 thisMask;
+ u16 arg1;
+ u16 arg2;
+
+ rawFlash = progmem[address];
+
+ instructionDecode_t thisInst;
+
+ thisInst.opNum = 0;
+ thisInst.arg1 = 0;
+ thisInst.arg2 = 0;
+
+ while (instructionList[i].opNum != 0)
+ {
+ thisMask = ~(instructionList[i].arg1Mask | instructionList[i].arg2Mask);
+
+ if ((rawFlash & thisMask) == instructionList[i].mask)
+ {
+
+ arg1 = (decodeArg(rawFlash, instructionList[i].arg1Mask, instructionList[i].arg1Neg) * instructionList[i].arg1Mul) + instructionList[i].arg1Offset;
+ arg2 = (decodeArg(rawFlash, instructionList[i].arg2Mask, instructionList[i].arg2Neg) * instructionList[i].arg2Mul) + instructionList[i].arg2Offset;
+
+ if (instructionList[i].words == 2)
+ { // the 2 word instructions have k16 as the 2nd word of total 32bit instruction
+ arg2 = progmem[address + 1];
+ }
+
+ //fprintf(stdout, instructionList[i].opName, arg1, arg2);
+ //fprintf(stdout, "\n");
+
+ thisInst.opNum = instructionList[i].opNum;
+ thisInst.arg1 = arg1;
+ thisInst.arg2 = arg2;
+
+ progmemDecoded[address] = thisInst;
+ return;
+ }
+ i++;
+ }
+ return;
+}
+
+void avr8::decodeFlash(void)
+{
+ for (u16 i = 0; i < (progSize / 2); i++)
+ {
+ decodeFlash(i);
+ }
+}
+void avr8::decodeFlash(u16 address)
+{
+ if (address < (progSize / 2))
+ {
+ instructionDecode(address);
+ }
+}
+
+void avr8::trigger_interrupt(unsigned int location)
+{
+
+ // clear interrupt flag
+ store_bit_1(SREG, SREG_I, 0);
+
+ // push current PC
+ write_sram(SP, (u8)pc);
+ DEC_SP;
+ write_sram(SP, pc >> 8);
+ DEC_SP;
+
+ // jump to new location (which jumps to the real handler)
+ pc = location;
+
+ // bill the cycles consumed (3 cycles).
+ // Note that there is an error in the Atmega644 datasheet where
+ // it specifies the IRQ cycles as 5.
+ // see: http://www.avrfreaks.net/forum/interrupt-timing-conundrum
+ update_hardware();
+ update_hardware();
+ update_hardware();
+}
+
+bool avr8::init_gui()
+{
+ left_edge_cycle = cycleCounter;
+ scanline_top = -33 - 5;
+ scanline_count = -999;
+ //Syncronized with the kernel, this value now results in the image
+ //being perfectly centered in both the emulator and a real TV
+ left_edge = VIDEO_LEFT_EDGE;
+
+ latched_buttons[0] = buttons[0] = ~0;
+ latched_buttons[1] = buttons[1] = ~0;
+
+ // Precompute final palette for speed.
+ // Should build some NTSC compensation magic in here too.
+ for (int i = 0; i < 256; i++)
+ {
+ int red = (((i >> 0) & 7) * 255) / 7;
+ int green = (((i >> 3) & 7) * 255) / 7;
+ int blue = (((i >> 6) & 3) * 255) / 3;
+ palette[i] = 0xff000000 | red << 16 | green << 8 | blue;
+ }
+
+ return true;
+}
+
+// TODO:
+
+
+// keydown: see keyboard.h
+// uzeKbScanCodeQueue.push(...);
+// keup: uzeKbScanCodeQueue.push(0xf0); uzeKbScanCodeQueue.push(...);
+
+/* resizing the display "top lock" / "left lock"
+ case SDLK_1: if (left_edge > 0U) { left_edge--; } printf("left=%u\n",left_edge); break;
+ case SDLK_2: if (left_edge < 2047U - ((VIDEO_DISP_WIDTH * 7U) / 3U)) { left_edge++; } printf("left=%u\n",left_edge); break;
+ case SDLK_3: scanline_top--; printf("top=%d\n",scanline_top); break;
+ case SDLK_4: scanline_top++; printf("top=%d\n",scanline_top); break;
+*/
+
+// soft power switch
+// PIND = PIND & ~0b00001100; // press
+// PIND |= 0b00001100; // release
+
+/*
+ if (pad_mode == SNES_MOUSE)
+ {
+ // http://www.repairfaq.org/REPAIR/F_SNES.html
+ // we always report "low sensitivity"
+ int mouse_dx, mouse_dy;
+ u8 mouse_buttons = SDL_GetRelativeMouseState(&mouse_dx,&mouse_dy);
+ mouse_dx >>= mouse_scale;
+ mouse_dy >>= mouse_scale;
+ // clear high bit so we know it's the mouse
+ buttons[0] = (encode_delta(mouse_dx) << 24)
+ | (encode_delta(mouse_dy) << 16) | 0x7FFF;
+ if (mouse_buttons & SDL_BUTTON_LMASK)
+ buttons[0] &= ~(1<<9);
+ if (mouse_buttons & SDL_BUTTON_RMASK)
+ buttons[0] &= ~(1<<8);
+ // keep mouse centered so it doesn't get stuck on edge of screen.
+ // ...and immediately consume the bogus motion event it generated.
+ if (fullscreen)
+ {
+ SDL_WarpMouseInWindow(window,400,300);
+ SDL_GetRelativeMouseState(&mouse_dx,&mouse_dy);
+ }
+ }
+ else
+ buttons[0] |= 0xFFFF8000;
+*/
+
+#ifdef SPI_DEBUG
+char ascii(unsigned char ch)
+{
+ if (ch >= 32 && ch <= 127)
+ {
+ return ch;
+ }
+ return '.';
+}
+
+#endif
+
+void avr8::update_spi()
+{
+ // SPI state machine
+ switch (spiState)
+ {
+ case SPI_IDLE_STATE:
+ if (spiByte == 0xff)
+ {
+ //SPI_DEBUG("Idle->0xff\n");
+ SPDR = 0xff; // echo back that we're ready
+ break;
+ }
+ spiCommand = spiByte;
+ SPDR = 0x00;
+ spiState = SPI_ARG_X_HI;
+ break;
+ case SPI_ARG_X_HI:
+ SPI_DEBUG("x hi: %02X\n", spiByte);
+ spiArgXhi = spiByte;
+ SPDR = 0x00;
+ spiState = SPI_ARG_X_LO;
+ break;
+ case SPI_ARG_X_LO:
+ SPI_DEBUG("x lo: %02X\n", spiByte);
+ spiArgXlo = spiByte;
+ SPDR = 0x00;
+ spiState = SPI_ARG_Y_HI;
+ break;
+ case SPI_ARG_Y_HI:
+ SPI_DEBUG("y hi: %02X\n", spiByte);
+ spiArgYhi = spiByte;
+ SPDR = 0x00;
+ spiState = SPI_ARG_Y_LO;
+ break;
+ case SPI_ARG_Y_LO:
+ SPI_DEBUG("y lo: %02X\n", spiByte);
+ spiArgYlo = spiByte;
+ SPDR = 0x00;
+ spiState = SPI_ARG_CRC;
+ break;
+ case SPI_ARG_CRC:
+ SPI_DEBUG("SPI - CMD%d (%02X) X:%04X Y:%04X CRC: %02X\n", spiCommand ^ 0x40, spiCommand, spiArgX, spiArgY, spiByte);
+ // ignore CRC and process commands
+ switch (spiCommand)
+ {
+ case 0x40: //CMD0 = RESET / GO_IDLE_STATE
+ SPDR = 0x00;
+ spiState = SPI_RESPOND_SINGLE;
+ spiResponseBuffer[0] = 0xff; // 8 clock wait
+ spiResponseBuffer[1] = 0x01; // send command response R1->idle flag
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 2;
+ spiByteCount = 0;
+ break;
+ case 0x41: //CMD1 = INIT / SEND_OP_COND
+ SPDR = 0x00;
+ spiState = SPI_RESPOND_SINGLE;
+ spiResponseBuffer[0] = 0x00; // 8-clock wait
+ spiResponseBuffer[1] = 0x00; // no error
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 2;
+ spiByteCount = 0;
+ break;
+
+ case 0x48: //CMD8 = INIT / SEND_IF_COND
+ SPDR = 0x00;
+ spiState = SPI_RESPOND_SINGLE;
+ spiResponseBuffer[0] = 0xff; // 8-clock wait
+ spiResponseBuffer[1] = 0x01; // return command response R7
+ spiResponseBuffer[2] = 0x00; // return command response R7
+ spiResponseBuffer[3] = 0x00; // return command response R7
+ spiResponseBuffer[4] = 0x01; // return command response R7 voltage accepted
+ spiResponseBuffer[5] = spiArgYlo; // return command response R7 check pattern
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 6;
+ spiByteCount = 0;
+ break;
+
+ case 0x4c: //CMD12 = STOP_TRANSMISSION
+ SPDR = 0x00;
+ spiState = SPI_RESPOND_SINGLE;
+ spiResponseBuffer[0] = 0xff; //stuff byte
+ spiResponseBuffer[1] = 0xff; //stuff byte
+ spiResponseBuffer[2] = 0x00; // card is ready //in "trans" state
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 3;
+ spiByteCount = 0;
+ break;
+
+ case 0x51: //CMD17 = READ_BLOCK
+ SPDR = 0x00;
+ spiState = SPI_RESPOND_SINGLE;
+ spiResponseBuffer[0] = 0xFF; // 8-clock wait
+ spiResponseBuffer[1] = 0x00; // no error
+ spiResponseBuffer[2] = 0xFE; // start block
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 3;
+ //SDSeekToOffset(spiArg);
+ spiByteCount = 512;
+ break;
+ case 0x52: //CMD18 = MULTI_READ_BLOCK
+ SPDR = 0x00;
+ spiState = SPI_RESPOND_MULTI;
+ spiResponseBuffer[0] = 0xFF; // 8-clock wait
+ spiResponseBuffer[1] = 0x00; // no error
+ spiResponseBuffer[2] = 0xFE; // start block
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 3;
+ spiCommandDelay = 0;
+ //SDSeekToOffset(spiArg);
+ spiByteCount = 0;
+ break;
+ case 0x58: //CMD24 = WRITE_BLOCK
+ SPDR = 0x00;
+ spiState = SPI_WRITE_SINGLE;
+ spiResponseBuffer[0] = 0x00; // 8-clock wait
+ spiResponseBuffer[1] = 0x00; // no error
+ spiResponseBuffer[2] = 0xFE; // start block
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 3;
+ //SDSeekToOffset(spiArg);
+ spiByteCount = 512;
+ break;
+
+ case 0x69: //ACMD41 = SD_SEND_OP_COND (ACMD is the command sequence of CMD55-CMD)
+ SPDR = 0x00;
+ spiState = SPI_RESPOND_SINGLE;
+ spiResponseBuffer[0] = 0xff; // 8 clock wait
+ spiResponseBuffer[1] = 0x00; // send command response R1->OK
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 2;
+ spiByteCount = 0;
+ break;
+
+ case 0x77: //CMD55 = APP_CMD (ACMD is the command sequence of CMD55-CMD)
+ SPDR = 0x00;
+ spiState = SPI_RESPOND_SINGLE;
+ spiResponseBuffer[0] = 0xff; // 8 clock wait
+ spiResponseBuffer[1] = 0x01; // send command response R1->idle flag
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 2;
+ spiByteCount = 0;
+ break;
+
+ case 0x7A: //CMD58 = READ_OCR
+ SPDR = 0x00;
+ spiState = SPI_RESPOND_SINGLE;
+ spiResponseBuffer[0] = 0xff; // 8 clock wait
+ spiResponseBuffer[1] = 0x00; // send command response R1->ok
+ spiResponseBuffer[2] = 0x80; // return command response R3
+ spiResponseBuffer[3] = 0xff; // return command response R3
+ spiResponseBuffer[4] = 0x80; // return command response R3
+ spiResponseBuffer[5] = 0x00; // return command response R3
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 6;
+ spiByteCount = 0;
+ break;
+
+ default:
+ printf("Unknown SPI command: %d\n", spiCommand);
+ SPDR = 0x00;
+ spiState = SPI_RESPOND_SINGLE;
+ spiResponseBuffer[0] = 0x02; // data accepted
+ spiResponseBuffer[1] = 0x05; //i illegal command
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 2;
+ break;
+ }
+ break;
+
+ case SPI_RESPOND_SINGLE:
+ SPDR = *spiResponsePtr;
+ SPI_DEBUG("SPI - Respond: %02X\n", SPDR);
+ spiResponsePtr++;
+ if (spiResponsePtr == spiResponseEnd)
+ {
+ if (spiByteCount != 0)
+ {
+ spiState = SPI_READ_SINGLE_BLOCK;
+ }
+ else
+ {
+ spiState = SPI_IDLE_STATE;
+ }
+ }
+ break;
+
+ case SPI_RESPOND_MULTI:
+ if (spiCommandDelay != 0)
+ {
+ spiCommandDelay--;
+ SPDR = 0xff;
+ break;
+ }
+
+ SPDR = *spiResponsePtr;
+ SPI_DEBUG("SPI - Respond: %02X\n", SPDR);
+ spiResponsePtr++;
+
+ if (SPDR == 0 && spiByteCount == 0)
+ {
+ spiCommandDelay = 250; //average delay based on a sample of cards
+ }
+
+ if (spiResponsePtr == spiResponseEnd)
+ {
+ spiState = SPI_READ_MULTIPLE_BLOCK;
+ spiByteCount = 512;
+ }
+ break;
+
+ case SPI_READ_SINGLE_BLOCK:
+ SPDR = 0; //SDReadByte();
+#ifdef USE_SPI_DEBUG
+ {
+ // output a nice display to see sector data
+ int i = 512 - spiByteCount;
+ int ofs = i & 0x000F;
+ static unsigned char buf[16];
+ if (i > 0 && (ofs == 0))
+ {
+ printf("%04X: ", i - 16);
+ for (int j = 0; j < 16; j++)
+ printf("%02X ", buf[j]);
+ printf("| ");
+ for (int j = 0; j < 16; j++)
+ printf("%c", ascii(buf[j]));
+ SPI_DEBUG("\n");
+ }
+ buf[ofs] = SPDR;
+ }
+#endif
+ spiByteCount--;
+ if (spiByteCount == 0)
+ {
+ spiResponseBuffer[0] = 0x00; //CRC
+ spiResponseBuffer[1] = 0x00; //CRC
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 2;
+ spiState = SPI_RESPOND_SINGLE;
+ }
+ break;
+
+ case SPI_READ_MULTIPLE_BLOCK:
+ if (SPDR == 0x4C)
+ { //CMD12 - stop multiple read transmission
+ spiState = SPI_RESPOND_SINGLE;
+
+ spiCommand = 0x4C;
+ SPDR = 0x00;
+ spiState = SPI_ARG_X_HI;
+ spiByteCount = 0;
+ break;
+ }
+ else
+ {
+ SPDR = 0; //SDReadByte();
+ }
+ SPI_DEBUG("SPI - Data[%d]: %02X\n", 512 - spiByteCount, SPDR);
+ spiByteCount--;
+ //inter-sector
+ //NOTE: Current MoviePlayer.hex does not work with two delay bytes after the CRC. It has
+ //been coded to work with a MicroSD card. These cards usually have only 1 delay byte after the CRC.
+ //Uzem uses two delay bytes after the CRC since it is what regular SD cards does
+ //and we want to emulate the "worst" case.
+ if (spiByteCount == 0)
+ {
+ spiResponseBuffer[0] = 0x00; //CRC
+ spiResponseBuffer[1] = 0x00; //CRC
+ spiResponseBuffer[2] = 0xff; //delay
+ spiResponseBuffer[3] = 0xff; //delay
+ spiResponseBuffer[4] = 0xFE; // start block
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 5;
+ spiArg += 512; // automatically move to next block
+ //SDSeekToOffset(spiArg);
+ spiByteCount = 512;
+ spiState = SPI_RESPOND_MULTI;
+ }
+ break;
+
+ case SPI_WRITE_SINGLE:
+ SPDR = *spiResponsePtr;
+ SPI_DEBUG("SPI - Respond: %02X\n", SPDR);
+ spiResponsePtr++;
+ if (spiResponsePtr == spiResponseEnd)
+ {
+ if (spiByteCount != 0)
+ {
+ spiState = SPI_WRITE_SINGLE_BLOCK;
+ }
+ else
+ {
+ spiState = SPI_IDLE_STATE;
+ }
+ }
+ break;
+ case SPI_WRITE_SINGLE_BLOCK:
+ //SDWriteByte(SPDR);
+ SPI_DEBUG("SPI - Data[%d]: %02X\n", spiByteCount, SPDR);
+ SPDR = 0xFF;
+ spiByteCount--;
+ if (spiByteCount == 0)
+ {
+ spiResponseBuffer[0] = 0x00; //CRC
+ spiResponseBuffer[1] = 0x00; //CRC
+ spiResponsePtr = spiResponseBuffer;
+ spiResponseEnd = spiResponsePtr + 2;
+ spiState = SPI_RESPOND_SINGLE;
+ //SDCommit();
+ }
+ break;
+ }
+}
diff --git a/waterbox/uzem/avr8.h b/waterbox/uzem/avr8.h
new file mode 100644
index 0000000000..0cabfb4bfb
--- /dev/null
+++ b/waterbox/uzem/avr8.h
@@ -0,0 +1,697 @@
+/*
+(The MIT License)
+
+Copyright (c) 2008-2016 by
+David Etherton, Eric Anderton, Alec Bourque (Uze), Filipe Rinaldi,
+Sandor Zsuga (Jubatian), Matt Pandina (Artcfox)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#pragma once
+
+#include
+#include
+#include
+#include
+
+// Video: Offset of display on the emulator's surface
+// Syncronized with the kernel, this value now results in the image
+// being perfectly centered in both the emulator and a real TV
+#define VIDEO_LEFT_EDGE 168U
+// Video: Display width; the width of the emulator's output (before any
+// scaling applied) and video capturing
+#define VIDEO_DISP_WIDTH 720U
+
+//Uzebox keyboard defines
+#define KB_STOP 0
+#define KB_TX_START 1
+#define KB_TX_READY 2
+
+#define KB_SEND_KEY 0x00
+#define KB_SEND_END 0x01
+#define KB_SEND_DEVICE_ID 0x02
+#define KB_SEND_FIRMWARE_REV 0x03
+#define KB_RESET 0x7f
+
+// Joysticks
+#define MAX_JOYSTICKS 2
+#define NUM_JOYSTICK_BUTTONS 8
+#define MAX_JOYSTICK_AXES 8
+#define MAX_JOYSTICK_HATS 8
+
+#define JOY_SNES_X 0
+#define JOY_SNES_A 1
+#define JOY_SNES_B 2
+#define JOY_SNES_Y 3
+#define JOY_SNES_LSH 6
+#define JOY_SNES_RSH 7
+#define JOY_SNES_SELECT 8
+#define JOY_SNES_START 9
+
+#define JOY_DIR_UP 1
+#define JOY_DIR_RIGHT 2
+#define JOY_DIR_DOWN 4
+#define JOY_DIR_LEFT 8
+#define JOY_DIR_COUNT 4
+#define JOY_AXIS_UNUSED -1
+
+#define JOY_MASK_UP 0x11111111
+#define JOY_MASK_RIGHT 0x22222222
+#define JOY_MASK_DOWN 0x44444444
+#define JOY_MASK_LEFT 0x88888888
+
+#ifndef JOY_ANALOG_DEADZONE
+#define JOY_ANALOG_DEADZONE 4096
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+// don't whine about sprintf and fopen.
+// could switch to sprintf_s but that's not standard.
+#pragma warning(disable : 4996)
+#endif
+
+// 644 Overview: http://www.atmel.com/dyn/resources/prod_documents/doc2593.pdf
+// AVR8 insn set: http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf
+
+enum
+{
+ NES_A,
+ NES_B,
+ PAD_SELECT,
+ PAD_START,
+ PAD_UP,
+ PAD_DOWN,
+ PAD_LEFT,
+ PAD_RIGHT
+};
+enum
+{
+ SNES_B,
+ SNES_Y,
+ SNES_A = 8,
+ SNES_X,
+ SNES_LSH,
+ SNES_RSH
+};
+
+#if 1 // 644P
+const unsigned eepromSize = 2048;
+const unsigned sramSize = 4096;
+const unsigned progSize = 65536;
+#else // 1284P
+const unsigned eepromSize = 4096;
+const unsigned sramSize = 16384;
+const unsigned progSize = 131072;
+#endif
+
+#define IOBASE 32
+#define SRAMBASE 256
+
+namespace ports
+{
+enum
+{
+ PINA,
+ DDRA,
+ PORTA,
+ PINB,
+ DDRB,
+ PORTB,
+ PINC,
+ DDRC,
+ PORTC,
+ PIND,
+ DDRD,
+ PORTD,
+ res2C,
+ res2D,
+ res2E,
+ res2F,
+ res30,
+ res31,
+ res32,
+ res33,
+ res34,
+ TIFR0,
+ TIFR1,
+ TIFR2,
+ res38,
+ res39,
+ res3A,
+ PCIFR,
+ EIFR,
+ EIMSK,
+ GPIOR0,
+ EECR,
+ EEDR,
+ EEARL,
+ EEARH,
+ GTCCR,
+ TCCR0A,
+ TCCR0B,
+ TCNT0,
+ OCR0A,
+ OCR0B,
+ res49,
+ GPIOR1,
+ GPIOR2,
+ SPCR,
+ SPSR,
+ SPDR,
+ res4f,
+ ACSR,
+ OCDR,
+ res52,
+ SMCR,
+ MCUSR,
+ MCUCR,
+ res56,
+ SPMCSR,
+ res58,
+ res59,
+ res5A,
+ res5B,
+ res5C,
+ SPL,
+ SPH,
+ SREG,
+ WDTCSR,
+ CLKPR,
+ res62,
+ res63,
+ PRR,
+ res65,
+ OSCCAL,
+ res67,
+ PCICR,
+ EICRA,
+ res6a,
+ PCMSK0,
+ PCMSK1,
+ PCMSK2,
+ TIMSK0,
+ TIMSK1,
+ TIMSK2,
+ res71,
+ res72,
+ PCMSK3,
+ res74,
+ res75,
+ res76,
+ res77,
+ ADCL,
+ ADCH,
+ ADCSRA,
+ ADCSRB,
+ ADMUX,
+ res7d,
+ DIDR0,
+ DIDR1,
+ TCCR1A,
+ TCCR1B,
+ TCCR1C,
+ res83,
+ TCNT1L,
+ TCNT1H,
+ ICR1L,
+ ICR1H,
+ OCR1AL,
+ OCR1AH,
+ OCR1BL,
+ OCR1BH,
+ res8c,
+ res8d,
+ res8e,
+ res8f,
+ res90,
+ res91,
+ res92,
+ res93,
+ res94,
+ res95,
+ res96,
+ res97,
+ res98,
+ res99,
+ res9a,
+ res9b,
+ res9c,
+ res9d,
+ res9e,
+ res9f,
+ resa0,
+ resa1,
+ resa2,
+ resa3,
+ resa4,
+ resa5,
+ resa6,
+ resa7,
+ resa8,
+ resa9,
+ resaa,
+ resab,
+ resac,
+ resad,
+ resae,
+ resaf,
+ TCCR2A,
+ TCCR2B,
+ TCNT2,
+ OCR2A,
+ OCR2B,
+ resb5,
+ ASSR,
+ resb7,
+ TWBR,
+ TWSR,
+ TWAR,
+ TWDR,
+ TWCR,
+ TWAMR,
+ resbe,
+ resbf,
+ UCSR0A,
+ UCSR0B,
+ UCSR0C,
+ resc3,
+ UBRR0L,
+ UBRR0H,
+ UDR0,
+ resc7,
+ resc8,
+ resc9,
+ resca,
+ rescb,
+ rescc,
+ rescd,
+ resce,
+ rescf,
+ resd0,
+ resd1,
+ resd2,
+ resd3,
+ resd4,
+ resd5,
+ resd6,
+ resd7,
+ resd8,
+ resd9,
+ resda,
+ resdb,
+ resdc,
+ resdd,
+ resde,
+ resdf,
+ rese0,
+ rese1,
+ rese2,
+ rese3,
+ rese4,
+ rese5,
+ rese6,
+ rese7,
+ rese8,
+ rese9,
+ resea,
+ reseb,
+ resec,
+ resed,
+ resee,
+ resef,
+ resf0,
+ resf1,
+ resf2,
+ resf3,
+ resf4,
+ resf5,
+ resf6,
+ resf7,
+ resf8,
+ resf9,
+ resfa,
+ resfb,
+ resfc,
+ resfd,
+ resfe,
+ resff
+};
+}
+
+typedef uint8_t u8;
+typedef int8_t s8;
+typedef uint16_t u16;
+typedef int16_t s16;
+typedef uint32_t u32;
+typedef int32_t s32;
+
+typedef struct
+{
+ s16 arg2;
+ u8 arg1;
+ u8 opNum;
+} __attribute__((packed)) instructionDecode_t;
+
+typedef struct
+{
+ u8 opNum;
+ char opName[32];
+ u8 arg1Type;
+ u8 arg1Mul;
+ u8 arg1Offset;
+ u8 arg1Neg;
+ u8 arg2Type;
+ u8 arg2Mul;
+ u8 arg2Offset;
+ u8 arg2Neg;
+ u8 words;
+ u8 clocks;
+ u16 mask;
+ u16 arg1Mask;
+ u16 arg2Mask;
+} instructionList_t;
+
+using namespace std;
+
+//SPI state machine states
+enum
+{
+ SPI_IDLE_STATE,
+ SPI_ARG_X_LO,
+ SPI_ARG_X_HI,
+ SPI_ARG_Y_LO,
+ SPI_ARG_Y_HI,
+ SPI_ARG_CRC,
+ SPI_RESPOND_SINGLE,
+ SPI_RESPOND_MULTI,
+ SPI_READ_SINGLE_BLOCK,
+ SPI_READ_MULTIPLE_BLOCK,
+ SPI_WRITE_SINGLE,
+ SPI_WRITE_SINGLE_BLOCK,
+ SPI_RESPOND_R1,
+ SPI_RESPOND_R1B,
+ SPI_RESPOND_R2,
+ SPI_RESPOND_R3,
+ SPI_RESPOND_R7,
+};
+
+struct SDPartitionEntry
+{
+ u8 state;
+ u8 startHead;
+ u16 startCylinder;
+ u8 type;
+ u8 endHead;
+ u16 endCylinder;
+ u32 sectorOffset;
+ u32 sectorCount;
+};
+
+struct avr8
+{
+ avr8() : /*Core*/
+ pc(0),
+ watchdogTimer(0), prevPortB(0), prevWDR(0),
+ dly_out(0), itd_TIFR1(0), elapsedCyclesSleep(0),
+ timer1_next(0), timer1_base(0), TCNT1(0),
+ //to align with AVR Simulator 2 since it has a bug that the first JMP
+ //at the reset vector takes only 2 cycles
+ cycleCounter(-1),
+
+ /*Video*/
+ video_buffer(nullptr),
+
+ /*Audio*/
+ enableSound(true),
+
+ /*Joystick*/
+ new_input_mode(false), lagged(false),
+
+ /*Uzekeyboard*/
+ uzeKbState(0), uzeKbEnabled(false),
+
+ /*SPI Emulation*/
+ spiByte(0), spiClock(0), spiTransfer(0), spiState(SPI_IDLE_STATE), spiResponsePtr(0), spiResponseEnd(0)
+ {
+ memset(r, 0, sizeof(r));
+ memset(io, 0, sizeof(io));
+ memset(sram, 0, sizeof(sram));
+ memset(eeprom, 0, sizeof(eeprom));
+ memset(progmem, 0, progSize / 2);
+ memset(progmemDecoded, 0, progSize / 2);
+ }
+
+ /*Core*/
+ u16 progmem[progSize / 2];
+ instructionDecode_t progmemDecoded[progSize / 2];
+ u16 pc, currentPc;
+
+ private:
+ unsigned int cycleCounter;
+ unsigned int elapsedCycles, prevCyclesCounter, elapsedCyclesSleep, lastCyclesSleep;
+ unsigned int prevPortB, prevWDR;
+ unsigned int watchdogTimer;
+ unsigned int cycle_ctr_ins; // Used in update_hardware_ins to track elapsed cycles between calls
+ // u8 eeClock; TODO: Only set at one location, never used. Maybe a never completed EEPROM timing code.
+ unsigned int T16_latch; // Latch for 16-bit timers (16 bits used)
+ unsigned int TCNT1; // Timer 1 counter (used instead of TCNT1H:TCNT1L)
+ unsigned int timer1_next; // Cycles remaining until next timer1 event
+ unsigned int timer1_base; // Where the between-events timer started (to reproduce TCNT1)
+ unsigned int itd_TIFR1; // Interrupt delaying for TIFR1 (8 bits used)
+ unsigned int dly_out; // Delayed output flags
+ unsigned int dly_TCCR1B; // Delayed Timer1 controls
+ unsigned int dly_TCNT1L; // Delayed Timer1 count (low)
+ unsigned int dly_TCNT1H; // Delayed Timer1 count (high)
+ public:
+ int randomSeed;
+ u16 decodeArg(u16 flash, u16 argMask, u8 argNeg);
+ void instructionDecode(u16 address);
+ void decodeFlash(void);
+ void decodeFlash(u16 address);
+
+ struct
+ {
+ union {
+ u8 r[32]; // Register file
+ struct
+ {
+ u8 r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15;
+ u8 r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, XL, XH, YL, YH, ZL, ZH;
+ };
+ };
+ union {
+ u8 io[256]; // Direct-mapped I/O space
+ struct
+ {
+ u8 PINA, DDRA, PORTA, PINB, DDRB, PORTB, PINC, DDRC;
+ u8 PORTC, PIND, DDRD, PORTD, res2C, res2D, res2E, res2F;
+ u8 res30, res31, res32, res33, res34, TIFR0, TIFR1, TIFR2;
+ u8 res38, res39, res3A, PCIFR, EIFR, EIMSK, GPIOR0, EECR;
+ u8 EEDR, EEARL, EEARH, GTCCR, TCCR0A, TCCR0B, TCNT0, OCR0A;
+ u8 OCR0B, res49, GPIOR1, GPIOR2, SPCR, SPSR, SPDR, res4f;
+ u8 ACSR, OCDR, res52, SMCR, MCUSR, MCUCR, res56, SPMCSR;
+ u8 res58, res59, res5A, res5B, res5C, SPL, SPH, SREG;
+ u8 WDTCSR, CLKPR, res62, res63, PRR, res65, OSCCAL, res67;
+ u8 PCICR, EICRA, res6a, PCMSK0, PCMSK1, PCMSK2, TIMSK0, TIMSK1;
+ u8 TIMSK2, res71, res72, PCMSK3, res74, res75, res76, res77;
+ u8 ADCL, ADCH, ADCSRA, ADCSRB, ADMUX, res7d, DIDR0, DIDR1;
+ u8 TCCR1A, TCCR1B, TCCR1C, res83, TCNT1L, TCNT1H, ICR1L, ICR1H;
+ u8 OCR1AL, OCR1AH, OCR1BL, OCR1BH, res8c, res8d, res8e, res8f;
+ u8 res90, res91, res92, res93, res94, res95, res96, res97;
+ u8 res98, res99, res9a, res9b, res9c, res9d, res9e, res9f;
+ u8 resa0, resa1, resa2, resa3, resa4, resa5, resa6, resa7;
+ u8 resa8, resa9, resaa, resab, resac, resad, resae, resaf;
+ u8 TCCR2A, TCCR2B, TCNT2, OCR2A, OCR2B, resb5, ASSR, resb7;
+ u8 TWBR, TWSR, TWAR, TWDR, TWCR, TWAMR, resbe, resbf;
+ u8 UCSR0A, UCSR0B, UCSR0C, resc3, UBRR0L, UBRR0H, UDR0, resc7;
+ u8 resc8, resc9, resca, rescb, rescc, rescd, resce, rescf;
+ u8 resd0, resd1, resd2, resd3, resd4, resd5, resd6, resd7;
+ u8 resd8, resd9, resda, resdb, resdc, resdd, resde, resdf;
+ u8 rese0, rese1, rese2, rese3, rese4, rese5, rese6, rese7;
+ u8 rese8, rese9, resea, reseb, resec, resed, resee, resef;
+ u8 resf0, resf1, resf2, resf3, resf4, resf5, resf6, resf7;
+ u8 resf8, resf9, resfa, resfb, resfc, resfd, resfe, resff;
+ };
+ };
+ u8 sram[sramSize];
+ };
+ u8 eeprom[eepromSize];
+
+ int scanline_count;
+ unsigned int left_edge_cycle;
+ int scanline_top;
+ unsigned int left_edge;
+ u32 inset;
+ u32 palette[256];
+ u8 scanline_buf[2048]; // For collecting pixels from a single scanline
+ u8 pixel_raw; // Raw (8 bit) input pixel
+
+ u32 *video_buffer;
+
+ /*Audio*/
+ bool enableSound;
+
+ /*Joystick*/
+ // SNES bit order: 0 = B, Y, Select, Start, Up, Down, Left, Right, A, X, L, 11 = R
+ // NES bit order: 0 = A, B, Select, Start, Up, Down, Left, 7 = Right
+ u32 buttons[2], latched_buttons[2];
+ bool new_input_mode;
+ bool lagged;
+
+ /*Uzebox Keyboard*/
+ u8 uzeKbState;
+ u8 uzeKbDataOut;
+ bool uzeKbEnabled;
+ queue uzeKbScanCodeQueue;
+ u8 uzeKbDataIn;
+ u8 uzeKbClock;
+
+ /*SPI Emulation*/
+ u8 spiByte;
+ u8 spiTransfer;
+ u16 spiClock;
+ u16 spiCycleWait;
+ u8 spiState;
+ u8 spiCommand;
+ u8 spiCommandDelay;
+ union {
+ u32 spiArg;
+ union {
+ struct
+ {
+ u16 spiArgY;
+ u16 spiArgX;
+ };
+ struct
+ {
+ u8 spiArgYlo;
+ u8 spiArgYhi;
+ u8 spiArgXlo;
+ u8 spiArgXhi;
+ };
+ };
+ };
+ u32 spiByteCount;
+ u8 spiResponseBuffer[12];
+ u8 *spiResponsePtr;
+ u8 *spiResponseEnd;
+
+ private:
+ void write_io(u8 addr, u8 value);
+ u8 read_io(u8 addr);
+ // Should not be called directly (see write_io)
+ void write_io_x(u8 addr, u8 value);
+
+ inline u8 read_progmem(u16 addr)
+ {
+ u16 word = progmem[addr >> 1];
+ return (addr & 1) ? word >> 8 : word;
+ }
+
+ inline void write_sram(u16 addr, u8 value)
+ {
+ sram[(addr - SRAMBASE) & (sramSize - 1U)] = value;
+ }
+
+ void write_sram_io(u16 addr, u8 value)
+ {
+ if (addr >= SRAMBASE)
+ {
+ sram[(addr - SRAMBASE) & (sramSize - 1)] = value;
+ }
+ else if (addr >= IOBASE)
+ {
+ write_io(addr - IOBASE, value);
+ }
+ else
+ {
+ r[addr] = value; // Write a register
+ }
+ }
+
+ inline u8 read_sram(u16 addr)
+ {
+ return sram[(addr - SRAMBASE) & (sramSize - 1U)];
+ }
+
+ u8 read_sram_io(u16 addr)
+ {
+
+ if (addr >= SRAMBASE)
+ {
+ return sram[(addr - SRAMBASE) & (sramSize - 1)];
+ }
+ else if (addr >= IOBASE)
+ {
+ return read_io(addr - IOBASE);
+ }
+ else
+ {
+ return r[addr]; // Read a register
+ }
+ }
+
+ inline static unsigned int get_insn_size(unsigned int insn)
+ {
+ /* 41 LDS Rd,k (next word is rest of address)
+ 82 STS k,Rr (next word is rest of address)
+ 30 JMP k (next word is rest of address)
+ 14 CALL k (next word is rest of address) */
+ // This code is simplified by assuming upper k bits are zero on 644
+
+ if (insn == 14 || insn == 30 || insn == 41 || insn == 82)
+ {
+ return 2U;
+ }
+ else
+ {
+ return 1U;
+ }
+ }
+
+ public:
+ bool init_gui();
+ void draw_memorymap();
+ void trigger_interrupt(unsigned int location);
+ unsigned int exec();
+ void spi_calculateClock();
+ void update_hardware();
+ void update_hardware_fast();
+ void update_hardware_ins();
+ void update_spi();
+ u8 SDReadByte();
+ void SDWriteByte(u8 value);
+ void SDCommit();
+ void LoadEEPROMFile(const char *filename);
+ void shutdown(int errcode);
+ void idle(void);
+};
+
+// undefine the following to disable SPI debug messages
+#ifdef USE_SPI_DEBUG
+#define SPI_DEBUG(...) printf(__VA_ARGS__)
+#else
+#define SPI_DEBUG(...)
+#endif
+
+#ifdef USE_EEPROM_DEBUG
+#define EEPROM_DEBUG(...) printf(__VA_ARGS__)
+#else
+#define EEPROM_DEBUG(...)
+#endif
diff --git a/waterbox/uzem/avr_inst_table.txt b/waterbox/uzem/avr_inst_table.txt
new file mode 100644
index 0000000000..0e256990ae
--- /dev/null
+++ b/waterbox/uzem/avr_inst_table.txt
@@ -0,0 +1,92 @@
+0000 0000 0000 0000 NOP
+0000 0001 dddd rrrr MOVW Rd+1:Rd,Rr+1:R
+0000 0010 dddd rrrr MULS Rd,Rr
+0000 0011 0ddd 0rrr MULSU Rd,Rr (registers are in 16-23 range)
+0000 0011 0ddd 1rrr FMUL Rd,Rr (registers are in 16-23 range)
+0000 0011 1ddd 0rrr FMULS Rd,Rr
+0000 0011 1ddd 1rrr FMULSU Rd,Rr
+0000 01rd dddd rrrr CPC Rd,Rr
+0000 10rd dddd rrrr SBC Rd,Rr
+0000 11rd dddd rrrr ADD Rd,Rr (LSL is ADD Rd,Rd)
+0001 00rd dddd rrrr CPSE Rd,Rr
+0001 01rd dddd rrrr CP Rd,Rr
+0001 10rd dddd rrrr SUB Rd,Rr
+0001 11rd dddd rrrr ADC Rd,Rr (ROL is ADC Rd,Rd)
+0010 00rd dddd rrrr AND Rd,Rr (TST is AND Rd,Rd)
+0010 01rd dddd rrrr EOR Rd,Rr (CLR is EOR Rd,Rd)
+0010 10rd dddd rrrr OR Rd,Rr
+0010 11rd dddd rrrr MOV Rd,Rr
+0011 KKKK dddd KKKK CPI Rd,K
+0100 KKKK dddd KKKK SBCI Rd,K
+0101 KKKK dddd KKKK SUBI Rd,K
+0110 KKKK dddd KKKK ORI Rd,K (same as SBR insn)
+0111 KKKK dddd KKKK ANDI Rd,K (CBR is ANDI with K complemented)
+10q0 qq0d dddd 0qqq LD Rd,Z+q
+10q0 qq0d dddd 1qqq LD Rd,Y+q
+10q0 qq1d dddd 0qqq ST Z+q,Rd
+10q0 qq1d dddd 1qqq ST Y+q,Rd
+1001 000d dddd 0000 LDS Rd,k (next word is rest of address)
+1001 000d dddd 0001 LD Rd,Z+
+1001 000d dddd 0010 LD Rd,-Z
+1001 000d dddd 0100 LPM Rd,Z
+1001 000d dddd 0101 LPM Rd,Z+
+1001 000d dddd 0110 ELPM Rd,Z
+1001 000d dddd 0111 ELPM Rd,Z+
+1001 000d dddd 1001 LD Rd,Y+
+1001 000d dddd 1010 LD Rd,-Y
+1001 000d dddd 1100 LD rd,X
+1001 000d dddd 1101 LD rd,X+
+1001 000d dddd 1110 LD rd,-X
+1001 000d dddd 1111 POP Rd
+1001 001d dddd 0000 STS k,Rr (next word is rest of address)
+1001 001r rrrr 0001 ST Z+,Rr
+1001 001r rrrr 0010 ST -Z,Rr
+1001 001r rrrr 1001 ST Y+,Rr
+1001 001r rrrr 1010 ST -Y,Rr
+1001 001r rrrr 1100 ST X,Rr
+1001 001r rrrr 1101 ST X+,Rr
+1001 001r rrrr 1110 ST -X,Rr
+1001 001d dddd 1111 PUSH Rd
+1001 010d dddd 0000 COM Rd
+1001 010d dddd 0001 NEG Rd
+1001 010d dddd 0010 SWAP Rd
+1001 010d dddd 0011 INC Rd
+1001 010d dddd 0101 ASR Rd
+1001 010d dddd 0110 LSR Rd
+1001 010d dddd 0111 ROR Rd
+1001 010d dddd 1010 DEC Rd
+1001 010k kkkk 110k JMP k (next word is rest of address)
+1001 010k kkkk 111k CALL k (next word is rest of address)
+1001 0100 0sss 1000 BSET s (SEC, etc are aliases with sss implicit)
+1001 0100 1sss 1000 BCLR s (CLC, etc are aliases with sss implicit)
+1001 0100 0000 1001 IJMP (jump thru Z register)
+1001 0101 0000 1000 RET
+1001 0101 0000 1001 ICALL (call thru Z register)
+1001 0101 0001 1000 RETI
+1001 0101 1000 1000 SLEEP
+1001 0101 1001 1000 BREAK
+1001 0101 1010 1000 WDR
+1001 0101 1100 1000 LPM (r0 implied, why is this special?)
+1001 0101 1110 1000 SPM Z (writes R1:R0)
+1001 0110 KKdd KKKK ADIW Rd+1:Rd,K (16-bit add to upper four register pairs)
+1001 0111 KKdd KKKK SBIW Rd+1:Rd,K
+1001 1000 AAAA Abbb CBI A,b
+1001 1001 AAAA Abbb SBIC A,b
+1001 1010 AAAA Abbb SBI A,b
+1001 1011 AAAA Abbb SBIS A,b
+1001 11rd dddd rrrr MUL Rd,Rr
+1011 0AAd dddd AAAA IN Rd,A
+1011 1AAd dddd AAAA OUT A,Rd
+1100 kkkk kkkk kkkk RJMP k
+1101 kkkk kkkk kkkk RCALL k
+1110 KKKK dddd KKKK LDI Rd,K (SER is just LDI Rd,255)
+1111 00kk kkkk ksss BRBS s,k (same here)
+1111 01kk kkkk ksss BRBC s,k (BRCC, etc are aliases for this with sss implicit)
+1111 100d dddd 0bbb BLD Rd,b
+1111 101d dddd 0bbb BST Rd,b
+1111 110r rrrr 0bbb SBRC Rr,b
+1111 111r rrrr 0bbb SBRS Rr,b
+
+
+
+
diff --git a/waterbox/uzem/blip_buf.cpp b/waterbox/uzem/blip_buf.cpp
new file mode 100644
index 0000000000..1bd3377e92
--- /dev/null
+++ b/waterbox/uzem/blip_buf.cpp
@@ -0,0 +1,344 @@
+/* blip_buf 1.1.0. http://www.slack.net/~ant/ */
+
+#include "blip_buf.h"
+
+#include
+#include
+#include
+#include
+
+/* Library Copyright (C) 2003-2009 Shay Green. This library is free software;
+you can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+library is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#if defined (BLARGG_TEST) && BLARGG_TEST
+ #include "blargg_test.h"
+#endif
+
+/* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000.
+Avoids constants that don't fit in 32 bits. */
+#if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF
+ typedef unsigned long fixed_t;
+ enum { pre_shift = 32 };
+
+#elif defined(ULLONG_MAX)
+ typedef unsigned long long fixed_t;
+ enum { pre_shift = 32 };
+
+#else
+ typedef unsigned fixed_t;
+ enum { pre_shift = 0 };
+
+#endif
+
+enum { time_bits = pre_shift + 20 };
+
+static fixed_t const time_unit = (fixed_t) 1 << time_bits;
+
+enum { bass_shift = 9 }; /* affects high-pass filter breakpoint frequency */
+enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */
+
+enum { half_width = 8 };
+enum { buf_extra = half_width*2 + end_frame_extra };
+enum { phase_bits = 5 };
+enum { phase_count = 1 << phase_bits };
+enum { delta_bits = 15 };
+enum { delta_unit = 1 << delta_bits };
+enum { frac_bits = time_bits - pre_shift };
+
+/* We could eliminate avail and encode whole samples in offset, but that would
+limit the total buffered samples to blip_max_frame. That could only be
+increased by decreasing time_bits, which would reduce resample ratio accuracy.
+*/
+
+/** Sample buffer that resamples to output rate and accumulates samples
+until they're read out */
+struct blip_t
+{
+ fixed_t factor;
+ fixed_t offset;
+ int avail;
+ int size;
+ int integrator;
+};
+
+typedef int buf_t;
+
+/* probably not totally portable */
+#define SAMPLES( buf ) ((buf_t*) ((buf) + 1))
+
+/* Arithmetic (sign-preserving) right shift */
+#define ARITH_SHIFT( n, shift ) \
+ ((n) >> (shift))
+
+enum { max_sample = +32767 };
+enum { min_sample = -32768 };
+
+#define CLAMP( n ) \
+ {\
+ if ( (short) n != n )\
+ n = ARITH_SHIFT( n, 16 ) ^ max_sample;\
+ }
+
+static void check_assumptions( void )
+{
+ int n;
+
+ #if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF
+ #error "int must be at least 32 bits"
+ #endif
+
+ assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */
+
+ n = max_sample * 2;
+ CLAMP( n );
+ assert( n == max_sample );
+
+ n = min_sample * 2;
+ CLAMP( n );
+ assert( n == min_sample );
+
+ assert( blip_max_ratio <= time_unit );
+ assert( blip_max_frame <= (fixed_t) -1 >> time_bits );
+}
+
+blip_t* blip_new( int size )
+{
+ blip_t* m;
+ assert( size >= 0 );
+
+ m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) );
+ if ( m )
+ {
+ m->factor = time_unit / blip_max_ratio;
+ m->size = size;
+ blip_clear( m );
+ check_assumptions();
+ }
+ return m;
+}
+
+void blip_delete( blip_t* m )
+{
+ if ( m != NULL )
+ {
+ /* Clear fields in case user tries to use after freeing */
+ memset( m, 0, sizeof *m );
+ free( m );
+ }
+}
+
+void blip_set_rates( blip_t* m, double clock_rate, double sample_rate )
+{
+ double factor = time_unit * sample_rate / clock_rate;
+ m->factor = (fixed_t) factor;
+
+ /* Fails if clock_rate exceeds maximum, relative to sample_rate */
+ assert( 0 <= factor - m->factor && factor - m->factor < 1 );
+
+ /* Avoid requiring math.h. Equivalent to
+ m->factor = (int) ceil( factor ) */
+ if ( m->factor < factor )
+ m->factor++;
+
+ /* At this point, factor is most likely rounded up, but could still
+ have been rounded down in the floating-point calculation. */
+}
+
+void blip_clear( blip_t* m )
+{
+ /* We could set offset to 0, factor/2, or factor-1. 0 is suitable if
+ factor is rounded up. factor-1 is suitable if factor is rounded down.
+ Since we don't know rounding direction, factor/2 accommodates either,
+ with the slight loss of showing an error in half the time. Since for
+ a 64-bit factor this is years, the halving isn't a problem. */
+
+ m->offset = m->factor / 2;
+ m->avail = 0;
+ m->integrator = 0;
+ memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) );
+}
+
+int blip_clocks_needed( const blip_t* m, int samples )
+{
+ fixed_t needed;
+
+ /* Fails if buffer can't hold that many more samples */
+ assert( samples >= 0 && m->avail + samples <= m->size );
+
+ needed = (fixed_t) samples * time_unit;
+ if ( needed < m->offset )
+ return 0;
+
+ return (needed - m->offset + m->factor - 1) / m->factor;
+}
+
+void blip_end_frame( blip_t* m, unsigned t )
+{
+ fixed_t off = t * m->factor + m->offset;
+ m->avail += off >> time_bits;
+ m->offset = off & (time_unit - 1);
+
+ /* Fails if buffer size was exceeded */
+ assert( m->avail <= m->size );
+}
+
+int blip_samples_avail( const blip_t* m )
+{
+ return m->avail;
+}
+
+static void remove_samples( blip_t* m, int count )
+{
+ buf_t* buf = SAMPLES( m );
+ int remain = m->avail + buf_extra - count;
+ m->avail -= count;
+
+ memmove( &buf [0], &buf [count], remain * sizeof buf [0] );
+ memset( &buf [remain], 0, count * sizeof buf [0] );
+}
+
+int blip_read_samples( blip_t* m, short out [], int count, int stereo )
+{
+ assert( count >= 0 );
+
+ if ( count > m->avail )
+ count = m->avail;
+
+ if ( count )
+ {
+ int const step = stereo ? 2 : 1;
+ buf_t const* in = SAMPLES( m );
+ buf_t const* end = in + count;
+ int sum = m->integrator;
+ do
+ {
+ /* Eliminate fraction */
+ int s = ARITH_SHIFT( sum, delta_bits );
+
+ sum += *in++;
+
+ CLAMP( s );
+
+ *out = s;
+ out += step;
+
+ /* High-pass filter */
+ sum -= s << (delta_bits - bass_shift);
+ }
+ while ( in != end );
+ m->integrator = sum;
+
+ remove_samples( m, count );
+ }
+
+ return count;
+}
+
+/* Things that didn't help performance on x86:
+ __attribute__((aligned(128)))
+ #define short int
+ restrict
+*/
+
+/* Sinc_Generator( 0.9, 0.55, 4.5 ) */
+static short const bl_step [phase_count + 1] [half_width] =
+{
+{ 43, -115, 350, -488, 1136, -914, 5861,21022},
+{ 44, -118, 348, -473, 1076, -799, 5274,21001},
+{ 45, -121, 344, -454, 1011, -677, 4706,20936},
+{ 46, -122, 336, -431, 942, -549, 4156,20829},
+{ 47, -123, 327, -404, 868, -418, 3629,20679},
+{ 47, -122, 316, -375, 792, -285, 3124,20488},
+{ 47, -120, 303, -344, 714, -151, 2644,20256},
+{ 46, -117, 289, -310, 634, -17, 2188,19985},
+{ 46, -114, 273, -275, 553, 117, 1758,19675},
+{ 44, -108, 255, -237, 471, 247, 1356,19327},
+{ 43, -103, 237, -199, 390, 373, 981,18944},
+{ 42, -98, 218, -160, 310, 495, 633,18527},
+{ 40, -91, 198, -121, 231, 611, 314,18078},
+{ 38, -84, 178, -81, 153, 722, 22,17599},
+{ 36, -76, 157, -43, 80, 824, -241,17092},
+{ 34, -68, 135, -3, 8, 919, -476,16558},
+{ 32, -61, 115, 34, -60, 1006, -683,16001},
+{ 29, -52, 94, 70, -123, 1083, -862,15422},
+{ 27, -44, 73, 106, -184, 1152,-1015,14824},
+{ 25, -36, 53, 139, -239, 1211,-1142,14210},
+{ 22, -27, 34, 170, -290, 1261,-1244,13582},
+{ 20, -20, 16, 199, -335, 1301,-1322,12942},
+{ 18, -12, -3, 226, -375, 1331,-1376,12293},
+{ 15, -4, -19, 250, -410, 1351,-1408,11638},
+{ 13, 3, -35, 272, -439, 1361,-1419,10979},
+{ 11, 9, -49, 292, -464, 1362,-1410,10319},
+{ 9, 16, -63, 309, -483, 1354,-1383, 9660},
+{ 7, 22, -75, 322, -496, 1337,-1339, 9005},
+{ 6, 26, -85, 333, -504, 1312,-1280, 8355},
+{ 4, 31, -94, 341, -507, 1278,-1205, 7713},
+{ 3, 35, -102, 347, -506, 1238,-1119, 7082},
+{ 1, 40, -110, 350, -499, 1190,-1021, 6464},
+{ 0, 43, -115, 350, -488, 1136, -914, 5861}
+};
+
+/* Shifting by pre_shift allows calculation using unsigned int rather than
+possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient.
+And by having pre_shift 32, a 32-bit platform can easily do the shift by
+simply ignoring the low half. */
+
+void blip_add_delta( blip_t* m, unsigned time, int delta )
+{
+ unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
+ buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
+
+ int const phase_shift = frac_bits - phase_bits;
+ int phase = fixed >> phase_shift & (phase_count - 1);
+ short const* in = bl_step [phase];
+ short const* rev = bl_step [phase_count - phase];
+
+ int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1);
+ int delta2 = (delta * interp) >> delta_bits;
+ delta -= delta2;
+
+ /* Fails if buffer size was exceeded */
+ assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
+
+ out [0] += in[0]*delta + in[half_width+0]*delta2;
+ out [1] += in[1]*delta + in[half_width+1]*delta2;
+ out [2] += in[2]*delta + in[half_width+2]*delta2;
+ out [3] += in[3]*delta + in[half_width+3]*delta2;
+ out [4] += in[4]*delta + in[half_width+4]*delta2;
+ out [5] += in[5]*delta + in[half_width+5]*delta2;
+ out [6] += in[6]*delta + in[half_width+6]*delta2;
+ out [7] += in[7]*delta + in[half_width+7]*delta2;
+
+ in = rev;
+ out [ 8] += in[7]*delta + in[7-half_width]*delta2;
+ out [ 9] += in[6]*delta + in[6-half_width]*delta2;
+ out [10] += in[5]*delta + in[5-half_width]*delta2;
+ out [11] += in[4]*delta + in[4-half_width]*delta2;
+ out [12] += in[3]*delta + in[3-half_width]*delta2;
+ out [13] += in[2]*delta + in[2-half_width]*delta2;
+ out [14] += in[1]*delta + in[1-half_width]*delta2;
+ out [15] += in[0]*delta + in[0-half_width]*delta2;
+}
+
+void blip_add_delta_fast( blip_t* m, unsigned time, int delta )
+{
+ unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
+ buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
+
+ int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1);
+ int delta2 = delta * interp;
+
+ /* Fails if buffer size was exceeded */
+ assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
+
+ out [7] += delta * delta_unit - delta2;
+ out [8] += delta2;
+}
diff --git a/waterbox/uzem/blip_buf.h b/waterbox/uzem/blip_buf.h
new file mode 100644
index 0000000000..e9a5d4cc3b
--- /dev/null
+++ b/waterbox/uzem/blip_buf.h
@@ -0,0 +1,72 @@
+/** \file
+Sample buffer that resamples from input clock rate to output sample rate */
+
+/* blip_buf 1.1.0 */
+#ifndef BLIP_BUF_H
+#define BLIP_BUF_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/** First parameter of most functions is blip_t*, or const blip_t* if nothing
+is changed. */
+typedef struct blip_t blip_t;
+
+/** Creates new buffer that can hold at most sample_count samples. Sets rates
+so that there are blip_max_ratio clocks per sample. Returns pointer to new
+buffer, or NULL if insufficient memory. */
+blip_t* blip_new( int sample_count );
+
+/** Sets approximate input clock rate and output sample rate. For every
+clock_rate input clocks, approximately sample_rate samples are generated. */
+void blip_set_rates( blip_t*, double clock_rate, double sample_rate );
+
+enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
+clock_rate must not be greater than sample_rate*blip_max_ratio. */
+blip_max_ratio = 1 << 20 };
+
+/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
+void blip_clear( blip_t* );
+
+/** Adds positive/negative delta into buffer at specified clock time. */
+void blip_add_delta( blip_t*, unsigned int clock_time, int delta );
+
+/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
+void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta );
+
+/** Length of time frame, in clocks, needed to make sample_count additional
+samples available. */
+int blip_clocks_needed( const blip_t*, int sample_count );
+
+enum { /** Maximum number of samples that can be generated from one time frame. */
+blip_max_frame = 4000 };
+
+/** Makes input clocks before clock_duration available for reading as output
+samples. Also begins new time frame at clock_duration, so that clock time 0 in
+the new time frame specifies the same clock as clock_duration in the old time
+frame specified. Deltas can have been added slightly past clock_duration (up to
+however many clocks there are in two output samples). */
+void blip_end_frame( blip_t*, unsigned int clock_duration );
+
+/** Number of buffered samples available for reading. */
+int blip_samples_avail( const blip_t* );
+
+/** Reads and removes at most 'count' samples and writes them to 'out'. If
+'stereo' is true, writes output to every other element of 'out', allowing easy
+interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed
+samples. Returns number of samples actually read. */
+int blip_read_samples( blip_t*, short out [], int count, int stereo );
+
+/** Frees buffer. No effect if NULL is passed. */
+void blip_delete( blip_t* );
+
+
+/* Deprecated */
+typedef blip_t blip_buffer_t;
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/waterbox/uzem/dump.c b/waterbox/uzem/dump.c
new file mode 100644
index 0000000000..fc418d9e90
--- /dev/null
+++ b/waterbox/uzem/dump.c
@@ -0,0 +1,173 @@
+/*
+ Copyright (c) 2009 Eric Anderton
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include
+#include
+#include
+
+struct SDPartitionEntry{
+ BYTE state;
+ BYTE startHead;
+ WORD startCylinder;
+ BYTE type;
+ BYTE endHead;
+ WORD endCylinder;
+ DWORD sectorOffset;
+ DWORD sectorCount;
+};
+
+// Partition 1 example
+/*
+entry.state = 0x00;
+entry.startHead = 0x03;
+entry.startCylinder = 0x003D;
+entry.type = 0x06;
+entry.endHead = 0x0D;
+entry.endCylinder = 0xDBED;
+entry.sectorOffset = 0x000000F9;
+entry.sectorCount = 0x001E5F07;
+*/
+
+//Code mostly borrowed from: http://support.microsoft.com/kb/138434
+
+#define SECTORS_PER_WRITE 4096
+
+char drivePath[] = "\\\\.\\X:";
+
+int main(int argc,char** argv)
+{
+ HANDLE hCD, hFile;
+ DWORD dwNotUsed;
+
+ if (argc<3){
+ printf("Disk Image dumper - creates binary images of disks, suitable for SD media.\n");
+ printf("(c) 2009 Eric Anderton\n");
+ printf("\nUsage: dump DRIVELETTER FILENAME\n");
+ exit(1);
+ }
+
+ if(strlen(argv[1]) > 1){
+ printf("Invalid drive letter.\n");
+ exit(1);
+ }
+
+ // set the drive letter in the path specification
+ drivePath[4] = argv[1][0];
+
+ hFile = CreateFile (argv[2],GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ hCD = CreateFile (drivePath, GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL);
+
+ if (hCD != INVALID_HANDLE_VALUE){
+ DISK_GEOMETRY disk;
+ PARTITION_INFORMATION partition;
+ PARTITION_INFORMATION_MBR mbr;
+
+ // Get sector size of compact disc
+ if (DeviceIoControl (hCD, IOCTL_DISK_GET_DRIVE_GEOMETRY,
+ NULL, 0, &disk, sizeof(disk),
+ &dwNotUsed, NULL))
+ {
+ LPBYTE lpSector;
+ DWORD dwSize = disk.BytesPerSector; // 2 sectors
+ DWORD dwReadSize = dwSize*SECTORS_PER_WRITE;
+ __int64 cylinders = *((__int64*)&disk);
+ __int64 sectors = cylinders * disk.TracksPerCylinder * disk.SectorsPerTrack;
+ __int64 totalSize = sectors*dwSize;
+ __int64 i;
+
+ printf("Cylinders %lld\nTracks Per Cylinder: %d\nSectors Per Track %d\nSector Size: %d\n",cylinders,disk.TracksPerCylinder,disk.SectorsPerTrack,dwSize);
+ printf("Total Sectors: %lld\n",sectors);
+ printf("Media Size: %lld\n",totalSize);
+
+ // Allocate buffer to hold sectors from compact disc. Note that
+ // the buffer will be allocated on a sector boundary because the
+ // allocation granularity is larger than the size of a sector on a
+ // compact disk.
+ lpSector = (LPBYTE)VirtualAlloc (NULL, dwReadSize,MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
+
+ SDPartitionEntry entry;
+ // query system about partition and fill out the partition structure
+ if(DeviceIoControl(hCD, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &partition, sizeof(PARTITION_INFORMATION), &dwNotUsed, NULL)){
+ entry.state = 0x00;
+ entry.startCylinder = 0;//(*(__int64*)(&partition.StartingOffset))/dwSize;
+
+ entry.startHead = 0x00; //TODO
+ entry.startCylinder = 0x0000; //TODO
+ entry.type = partition.PartitionType;
+ entry.endHead = 0x00; //TODO
+ entry.endCylinder = 0x0000; //TODO
+ entry.sectorOffset = partition.HiddenSectors;
+ entry.sectorCount = (*(__int64*)(&partition.PartitionLength))/dwSize;
+ printf("----------\n");
+ printf("state: %0.4X\n",entry.state);
+ // printf("startHead: %0.4X\n",entry.startHead);
+ // printf("startCylinder: %0.4X\n",entry.startCylinder);
+ printf("type: %0.2X\n",entry.type);
+ // printf("endHead: %0.4X\n",entry.endHead);
+ // printf("endCylinder: %0.4X\n",entry.endCylinder);
+ printf("sectorCount: %0.8X\n",entry.sectorCount);
+ printf("sectorOffset: %0.8X\n",entry.sectorOffset);
+ }
+ else{
+ printf("Error reading parition info.\n");
+ exit(1);
+ }
+
+ // build a replica of the MBR for a single-partition image (common for SD media)
+ memset(lpSector,0,dwSize);
+ memcpy(lpSector + 0x1BE,&entry,sizeof(SDPartitionEntry));
+
+ // Executable Marker
+ lpSector[0x1FE] = 0x55;
+ lpSector[0x1FF] = 0xAA;
+
+ WriteFile (hFile, lpSector, dwSize, &dwNotUsed, NULL);
+
+ // write out hidden sectors (empty)
+ memset(lpSector,0,dwSize);
+ for(i = 1; i < entry.sectorOffset; i++){
+ WriteFile (hFile, lpSector, dwSize, &dwNotUsed, NULL);
+ }
+
+ // iteratively read all the sectors for the disk image
+ printf("Writing...");
+ for(i = 0; i < sectors/SECTORS_PER_WRITE; i++){
+ // Read sectors from the disc and write them to a file.
+ ReadFile (hCD, lpSector, dwReadSize, &dwNotUsed, NULL);
+ WriteFile (hFile, lpSector, dwReadSize, &dwNotUsed, NULL);
+ }
+ DWORD leftovers = sectors-i;
+ if(leftovers > 0){
+ dwReadSize = leftovers*dwSize;
+ ReadFile (hCD, lpSector, dwReadSize, &dwNotUsed, NULL);
+ WriteFile (hFile, lpSector, dwReadSize, &dwNotUsed, NULL);
+ }
+ VirtualFree (lpSector, 0, MEM_RELEASE);
+ }
+ CloseHandle (hCD);
+ CloseHandle (hFile);
+ }
+}
diff --git a/waterbox/uzem/uzem.cpp b/waterbox/uzem/uzem.cpp
new file mode 100644
index 0000000000..b52e0b8de5
--- /dev/null
+++ b/waterbox/uzem/uzem.cpp
@@ -0,0 +1,181 @@
+/*
+(The MIT License)
+
+Copyright (c) 2008-2016 by
+David Etherton, Eric Anderton, Alec Bourque (Uze), Filipe Rinaldi,
+Sandor Zsuga (Jubatian), Matt Pandina (Artcfox)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include "uzem.h"
+#include "avr8.h"
+#include "uzerom.h"
+#include "blip_buf.h"
+#include
+#include
+#include
+#include
+#include
+
+#include "../emulibc/emulibc.h"
+#include "../emulibc/waterboxcore.h"
+
+#define EXPORT extern "C" ECL_EXPORT
+
+// header for use with UzeRom files
+static RomHeader uzeRomHeader;
+static avr8 uzebox;
+static blip_t* blip;
+
+void avr8::shutdown(int errcode)
+{
+ printf("Oh no, that's bad!\n");
+}
+
+int main(void)
+{
+ return 0;
+}
+
+EXPORT bool MouseEnabled()
+{
+ return uzeRomHeader.mouse;
+}
+
+EXPORT bool Init()
+{
+ const char *heximage = "romfile";
+
+ unsigned char *buffer = (unsigned char *)(uzebox.progmem);
+
+ if (isUzeromFile(heximage))
+ {
+ printf("-- Loading UzeROM Image --\n");
+ if (!loadUzeImage(heximage, &uzeRomHeader, buffer))
+ {
+ printf("Error: cannot load UzeRom file '%s'.\n\n", heximage);
+ return false;
+ }
+ // enable mouse support if required
+ if (uzeRomHeader.mouse)
+ {
+ printf("Mouse support enabled\n");
+ }
+ }
+ else
+ {
+ printf("Error: Doesn't seem to be an UzeROM image\n");
+ return false;
+ /*printf("Loading Hex Image...\n");
+ if (!loadHex(heximage, buffer))
+ {
+ printf("Error: cannot load HEX image '%s'.\n\n", heximage);
+ return false;
+ }*/
+ }
+
+ uzebox.decodeFlash();
+ if (!uzebox.init_gui())
+ return false;
+
+ uzebox.randomSeed = time(NULL);
+ srand(uzebox.randomSeed); //used for the watchdog timer entropy
+
+ blip = blip_new(2048);
+ blip_set_rates(blip, 28618182, 44100);
+
+ return true;
+}
+
+EXPORT void GetMemoryAreas(MemoryArea *m)
+{
+ m[0].Data = uzebox.sram;
+ m[0].Name = "SRAM";
+ m[0].Size = sramSize;
+ m[0].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY;
+
+ m[1].Data = uzebox.eeprom;
+ m[1].Name = "EEPROM";
+ m[1].Size = eepromSize;
+ m[1].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_SAVERAMMABLE;
+
+ m[2].Data = uzebox.progmem;
+ m[2].Name = "ROM";
+ m[2].Size = progSize;
+ m[2].Flags = MEMORYAREA_FLAGS_WORDSIZE2;
+}
+
+struct MyFrameInfo : public FrameInfo
+{
+ uint32_t Buttons[3];
+};
+
+static int cycles;
+
+static int audio_value;
+void SampleCallback(uint8_t val)
+{
+ int v = (val - 128) * 100;
+ int delta = v - audio_value;
+ if (delta)
+ blip_add_delta(blip, cycles, delta);
+ audio_value = v;
+}
+
+EXPORT void FrameAdvance(MyFrameInfo* f)
+{
+ cycles = 0;
+ uzebox.video_buffer = f->VideoBuffer;
+ uzebox.lagged = true;
+ uzebox.buttons[0] = ~f->Buttons[0];
+ uzebox.buttons[1] = ~f->Buttons[1];
+ if (f->Buttons[2] & 1) // power pressed
+ {
+ uzebox.PIND = uzebox.PIND & ~0b00001100;
+ }
+ else
+ {
+ uzebox.PIND |= 0b00001100;
+ }
+
+ while (uzebox.scanline_count == -999 && cycles < 700000)
+ {
+ cycles += uzebox.exec();
+ }
+ while (uzebox.scanline_count != -999 && cycles < 700000)
+ {
+ cycles += uzebox.exec();
+ }
+ uzebox.video_buffer = nullptr;
+ f->Cycles = cycles;
+ f->Width = VIDEO_DISP_WIDTH;
+ f->Height = 224;
+ blip_end_frame(blip, cycles);
+ f->Samples = blip_read_samples(blip, f->SoundBuffer, 2048, true);
+ for (int i = 0; i < f->Samples; i++)
+ f->SoundBuffer[i * 2 + 1] = f->SoundBuffer[i * 2];
+ f->Lagged = uzebox.lagged;
+}
+
+void (*InputCallback)();
+
+EXPORT void SetInputCallback(void (*callback)())
+{
+ InputCallback = callback;
+}
diff --git a/waterbox/uzem/uzem.h b/waterbox/uzem/uzem.h
new file mode 100644
index 0000000000..c952b0f498
--- /dev/null
+++ b/waterbox/uzem/uzem.h
@@ -0,0 +1,31 @@
+/*
+(The MIT License)
+
+Copyright (c) 2008-2016 by
+David Etherton, Eric Anderton, Alec Bourque (Uze), Filipe Rinaldi,
+Sandor Zsuga (Jubatian), Matt Pandina (Artcfox)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#ifndef UZEM_H
+#define UZEM_H
+
+#define VERSION "v2.0"
+
+#endif
diff --git a/waterbox/uzem/uzerom.cpp b/waterbox/uzem/uzerom.cpp
new file mode 100644
index 0000000000..7ded9c068d
--- /dev/null
+++ b/waterbox/uzem/uzerom.cpp
@@ -0,0 +1,195 @@
+/*
+(The MIT License)
+
+Copyright (c) 2008-2016 by
+David Etherton, Eric Anderton, Alec Bourque (Uze), Filipe Rinaldi,
+Sandor Zsuga (Jubatian), Matt Pandina (Artcfox)
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include "uzerom.h"
+#include
+#include
+#include
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned long u32;
+
+#define MAGIC_SIZE 6
+
+const unsigned char magic[7] = "UZEBOX";
+
+bool isUzeromFile(const char *in_filename)
+{
+ unsigned char test[MAGIC_SIZE];
+ FILE *f = fopen(in_filename, "rb");
+ if (f)
+ {
+ if (fread(test, 1, MAGIC_SIZE, f) != MAGIC_SIZE)
+ {
+ printf("Error: failed to read the file %s.\n", in_filename);
+ return false;
+ }
+
+ for (int i = 0; i < MAGIC_SIZE; i++)
+ {
+ if (test[i] != magic[i])
+ return false;
+ }
+ fclose(f);
+ return true;
+ }
+ return false;
+}
+
+bool loadUzeImage(const char *in_filename, RomHeader *header, u8 *buffer)
+{
+ FILE *f = fopen(in_filename, "rb");
+ size_t ret;
+ if (f)
+ {
+ ret = fread(header, 1, 512, f);
+ if (ret != 512)
+ {
+ printf("Error: failed to read the file %s.\n", in_filename);
+ return false;
+ }
+
+ if (header->version != HEADER_VERSION)
+ {
+ printf("Error: cannot parse version %d UzeROM files.\n", header->version);
+ }
+ printf("%s\n", header->name);
+ printf("%s\n", header->author);
+ printf("%d\n", header->year);
+
+ if (header->target == 0)
+ {
+ printf("Uzebox 1.0 - ATmega644\n");
+ }
+ else if (header->target == 1)
+ {
+ printf("Uzebox 2.0 - XTmega128\n");
+ printf("Error: Uzebox 2.0 ROM images are not supported.\n");
+ return false;
+ }
+ printf("\n");
+
+ if (fread(buffer, 1, header->progSize, f) != header->progSize)
+ {
+ printf("Erro: failed to read the file %s.\n", in_filename);
+ return false;
+ }
+ fclose(f);
+ return true;
+ }
+ return false;
+}
+
+static inline int parse_hex_nibble(char s)
+{
+ if (s >= '0' && s <= '9')
+ return s - '0';
+ else if (s >= 'A' && s <= 'F')
+ return s - 'A' + 10;
+ else if (s >= 'a' && s <= 'a')
+ return s - 'a' + 10;
+ else
+ return -1;
+}
+
+static int parse_hex_byte(const char *s)
+{
+ return (parse_hex_nibble(s[0]) << 4) | parse_hex_nibble(s[1]);
+}
+
+static int parse_hex_word(const char *s)
+{
+ return (parse_hex_nibble(s[0]) << 12) | (parse_hex_nibble(s[1]) << 8) |
+ (parse_hex_nibble(s[2]) << 4) | parse_hex_nibble(s[3]);
+}
+
+bool loadHex(const char *in_filename, unsigned char *buffer, unsigned int *bytesRead)
+{
+ (void)bytesRead;
+ //http://en.wikipedia.org/wiki/.hex
+
+ //(I've added the spaces for clarity, they don't exist in the real files)
+ //:10 65B0 00 661F771F881F991F1A9469F760957095 59
+ //:10 65C0 00 809590959B01AC01BD01CF010895F894 91
+ //:02 65D0 00 FFCF FB
+ //:02 65D2 00 0100 C6
+ //:00 0000 01 FF [EOF marker]
+
+ //First field is the byte count. Second field is the 16-bit address. Third field is the record type;
+ //00 is data, 01 is EOF. For record type zero, next "wide" field is the actual data, followed by a
+ //checksum.
+ u16 progmemLast = 0;
+ char line[128];
+ int lineNumber = 1;
+
+ FILE *in_file = fopen(in_filename, "rb");
+ if (!in_file)
+ return false;
+
+ while (fgets(line, sizeof(line), in_file) && line[0]==':')
+ {
+ int bytes = parse_hex_byte(line + 1);
+ int addr = parse_hex_word(line + 3);
+ int recordType = parse_hex_byte(line + 7);
+ if (recordType == 0)
+ {
+ char *lp = line + 9;
+ while (bytes > 0)
+ {
+ unsigned char value = parse_hex_byte(lp);
+ buffer[addr] = value;
+ addr++;
+ if (addr > progmemLast)
+ {
+ progmemLast = addr;
+ }
+ lp += 2;
+ bytes -= 1;
+ }
+ }
+ else if (recordType == 1)
+ {
+ break;
+ }
+ else
+ fprintf(stderr, "ignoring unknown record type %d in line %d of %s\n", recordType, lineNumber, in_filename);
+
+ ++lineNumber;
+ }
+ /*
+ if(bytesRead != 0){
+ *bytesRead=progmemLast;
+ }
+ */
+ fclose(in_file);
+
+ return true;
+}
diff --git a/waterbox/uzem/uzerom.h b/waterbox/uzem/uzerom.h
new file mode 100644
index 0000000000..5a02a7f964
--- /dev/null
+++ b/waterbox/uzem/uzerom.h
@@ -0,0 +1,74 @@
+/*
+(The MIT License)
+
+Copyright (c) 2008-2016 by
+David Etherton, Eric Anderton, Alec Bourque (Uze), Filipe Rinaldi,
+Sandor Zsuga (Jubatian), Matt Pandina (Artcfox)
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include
+
+#ifndef UZEROM_H
+
+#define HEADER_VERSION 1
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 0
+#define MAX_PROG_SIZE 61440 //65536-4096
+#define HEADER_SIZE 512
+
+#pragma pack( 1 )
+struct RomHeader{
+ //Header fields (512 bytes)
+ uint8_t marker[6]; //'UZEBOX'
+ uint8_t version; //header version
+ uint8_t target; //AVR target (ATmega644=0, ATmega1284=1)
+ uint32_t progSize; //program memory size in bytes
+ uint16_t year;
+ uint8_t name[32];
+ uint8_t author[32];
+ uint8_t icon[16*16];
+ uint32_t crc32;
+ uint8_t mouse;
+ uint8_t description[64];
+ uint8_t reserved[114];
+};
+#pragma pack()
+
+
+/*
+ isUzeromFile - returns true if the file is indeed an .uze file
+*/
+bool isUzeromFile(const char* in_filename);
+
+/*
+ readUzeImage - reads an .uze file into the header and buffer structures provided.
+*/
+bool loadUzeImage(const char* in_filename,RomHeader *header,unsigned char *buffer);
+
+/*
+ load_hex - loads a hex image into the buffer provided, and provides the number of bytes read.
+*/
+bool loadHex(const char *in_filename,unsigned char *buffer,unsigned int *bytesRead = 0);
+
+#endif