From bd5787117112c3f8719c37be9a41bf958d4b88a7 Mon Sep 17 00:00:00 2001 From: nattthebear Date: Mon, 3 Jul 2017 21:29:34 -0400 Subject: [PATCH] New! With more games than the 32X! And better sounding ones too! --- ...izHawkSystemIdToCoreSystemEnumConverter.cs | 449 ++-- BizHawk.Emulation.Common/Database/Database.cs | 4 + .../BizHawk.Emulation.Cores.csproj | 2 + .../Consoles/Belogic/LibUzem.cs | 28 + .../Consoles/Belogic/Uzem.cs | 153 ++ output/dll/uzem.wbx.gz | Bin 0 -> 15857 bytes waterbox/uzem/.vscode/settings.json | 10 + waterbox/uzem/Makefile | 45 + waterbox/uzem/avr8.cpp | 2328 +++++++++++++++++ waterbox/uzem/avr8.h | 697 +++++ waterbox/uzem/avr_inst_table.txt | 92 + waterbox/uzem/blip_buf.cpp | 344 +++ waterbox/uzem/blip_buf.h | 72 + waterbox/uzem/dump.c | 173 ++ waterbox/uzem/uzem.cpp | 181 ++ waterbox/uzem/uzem.h | 31 + waterbox/uzem/uzerom.cpp | 195 ++ waterbox/uzem/uzerom.h | 74 + 18 files changed, 4654 insertions(+), 224 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Belogic/LibUzem.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Belogic/Uzem.cs create mode 100644 output/dll/uzem.wbx.gz create mode 100644 waterbox/uzem/.vscode/settings.json create mode 100644 waterbox/uzem/Makefile create mode 100644 waterbox/uzem/avr8.cpp create mode 100644 waterbox/uzem/avr8.h create mode 100644 waterbox/uzem/avr_inst_table.txt create mode 100644 waterbox/uzem/blip_buf.cpp create mode 100644 waterbox/uzem/blip_buf.h create mode 100644 waterbox/uzem/dump.c create mode 100644 waterbox/uzem/uzem.cpp create mode 100644 waterbox/uzem/uzem.h create mode 100644 waterbox/uzem/uzerom.cpp create mode 100644 waterbox/uzem/uzerom.h 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 0000000000000000000000000000000000000000..a969c60ea3526e46809e9556a4afd1506d361f30 GIT binary patch literal 15857 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 |1(&7k&dO3w5|>_1^AuDYO!+ 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�afng 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 H4vYW*@dM6Y literal 0 HcmV?d00001 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