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