diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs
index 994f7e1922..c433152c8a 100644
--- a/BizHawk.Client.Common/RomLoader.cs
+++ b/BizHawk.Client.Common/RomLoader.cs
@@ -422,9 +422,17 @@ namespace BizHawk.Client.Common
nextEmulator = c64;
break;
case "GBA":
- var gba = new GBA(nextComm);
- gba.Load(rom.RomData);
- nextEmulator = gba;
+ if (false)
+ {
+ var gba = new GBA(nextComm);
+ gba.Load(rom.RomData);
+ nextEmulator = gba;
+ }
+ else
+ {
+ var gba = new VBANext(rom.RomData, nextComm);
+ nextEmulator = gba;
+ }
break;
case "N64":
nextEmulator = new N64(nextComm, game, rom.RomData,
diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index 706879e63e..5755c00d99 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -238,7 +238,9 @@
+
+
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibVBANext.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibVBANext.cs
new file mode 100644
index 0000000000..781c19d40a
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibVBANext.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace BizHawk.Emulation.Cores.Nintendo.GBA
+{
+ public class LibVBANext
+ {
+ const string dllname = "libvbanext.dll";
+ const CallingConvention cc = CallingConvention.Cdecl;
+
+ [Flags]
+ public enum Buttons : int
+ {
+ A = 1,
+ B = 2,
+ Select = 4,
+ Start = 8,
+ Right = 16,
+ Left = 32,
+ Up = 64,
+ Down = 128,
+ R = 256,
+ L = 512
+ }
+
+ ///
+ /// create a new context
+ ///
+ ///
+ [DllImport(dllname, CallingConvention = cc)]
+ public static extern IntPtr Create();
+
+ ///
+ /// destroy a context
+ ///
+ ///
+ [DllImport(dllname, CallingConvention = cc)]
+ public static extern void Destroy(IntPtr g);
+
+ ///
+ /// load a rom
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// success
+ [DllImport(dllname, CallingConvention = cc)]
+ public static extern bool LoadRom(IntPtr g, byte[] romfile, uint romfilelen, byte[] biosfile, uint biosfilelen);
+
+ ///
+ /// hard reset
+ ///
+ ///
+ [DllImport(dllname, CallingConvention = cc)]
+ public static extern void Reset(IntPtr g);
+
+ ///
+ /// frame advance
+ ///
+ ///
+ ///
+ /// 240x160 packed argb32
+ [DllImport(dllname, CallingConvention = cc)]
+ public static extern void FrameAdvance(IntPtr g, Buttons input, int[] videobuffer);
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.cs
new file mode 100644
index 0000000000..315b13fa67
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.cs
@@ -0,0 +1,247 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using System.IO;
+
+using BizHawk.Common.BufferExtensions;
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.GBA
+{
+ [CoreAttributes("VBA-Next", "TODO", true, false, "cd508312a29ed8c29dacac1b11c2dce56c338a54", "https://github.com/libretro/vba-next")]
+ public class VBANext : IEmulator, IVideoProvider, ISyncSoundProvider
+ {
+ IntPtr Core;
+
+ public VBANext(byte[] romfile, CoreComm nextComm)
+ {
+ CoreComm = nextComm;
+ byte[] biosfile = CoreComm.CoreFileProvider.GetFirmware("GBA", "Bios", true, "GBA bios file is mandatory.");
+
+ if (romfile.Length > 32 * 1024 * 1024)
+ throw new ArgumentException("ROM is too big to be a GBA ROM!");
+ if (biosfile.Length != 16 * 1024)
+ throw new ArgumentException("BIOS file is not exactly 16K!");
+
+ Core = LibVBANext.Create();
+ if (Core == IntPtr.Zero)
+ throw new InvalidOperationException("Create() returned nullptr!");
+ try
+ {
+ if (!LibVBANext.LoadRom(Core, romfile, (uint)romfile.Length, biosfile, (uint)biosfile.Length))
+ throw new InvalidOperationException("LoadRom() returned false!");
+ }
+ catch
+ {
+ Dispose();
+ throw;
+ }
+ }
+
+
+
+
+ public void FrameAdvance(bool render, bool rendersound = true)
+ {
+ Frame++;
+
+ if (Controller["Power"])
+ LibVBANext.Reset(Core);
+
+ IsLagFrame = false; // todo
+ LibVBANext.FrameAdvance(Core, GetButtons(), videobuff);
+
+ if (IsLagFrame)
+ LagCount++;
+ }
+
+ public int Frame { get; private set; }
+ public int LagCount { get; set; }
+ public bool IsLagFrame { get; private set; }
+
+ public string SystemId { get { return "GBA"; } }
+
+ public bool DeterministicEmulation { get { return true; } }
+
+ public void ResetCounters()
+ {
+ Frame = 0;
+ LagCount = 0;
+ IsLagFrame = false;
+ }
+
+ public string BoardName
+ {
+ // could we do something useful here?
+ get { return "BAMBOOZLED!"; }
+ }
+
+ public CoreComm CoreComm { get; private set; }
+
+ public void Dispose()
+ {
+ if (Core != IntPtr.Zero)
+ {
+ LibVBANext.Destroy(Core);
+ Core = IntPtr.Zero;
+ }
+ }
+
+ #region SaveRam
+
+ public byte[] ReadSaveRam()
+ {
+ return new byte[16];
+ }
+
+ public void StoreSaveRam(byte[] data)
+ {
+ }
+
+ public void ClearSaveRam()
+ {
+ }
+
+ public bool SaveRamModified
+ {
+ get
+ {
+ return false;
+ }
+ set
+ {
+ }
+ }
+
+ #endregion
+
+ #region SaveStates
+
+ public void SaveStateText(TextWriter writer)
+ {
+ }
+
+ public void LoadStateText(TextReader reader)
+ {
+ }
+
+ public void SaveStateBinary(BinaryWriter writer)
+ {
+ }
+
+ public void LoadStateBinary(BinaryReader reader)
+ {
+ }
+
+ public byte[] SaveStateBinary()
+ {
+ return new byte[16];
+ }
+
+ public bool BinarySaveStatesPreferred
+ {
+ get { return true; }
+ }
+
+ #endregion
+
+ #region Debugging
+
+ public MemoryDomainList MemoryDomains
+ {
+ get { return MemoryDomainList.GetDummyList(); }
+ }
+
+ public Dictionary GetCpuFlagsAndRegisters()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetCpuRegister(string register, int value)
+ {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region Settings
+
+ public object GetSettings()
+ {
+ return null;
+ }
+
+ public object GetSyncSettings()
+ {
+ return null;
+ }
+
+ public bool PutSettings(object o)
+ {
+ return false;
+ }
+
+ public bool PutSyncSettings(object o)
+ {
+ return false;
+ }
+
+ #endregion
+
+ #region Controller
+
+ public ControllerDefinition ControllerDefinition { get { return GBA.GBAController; } }
+ public IController Controller { get; set; }
+
+ private LibVBANext.Buttons GetButtons()
+ {
+ LibVBANext.Buttons ret = 0;
+ foreach (string s in Enum.GetNames(typeof(LibVBANext.Buttons)))
+ {
+ if (Controller[s])
+ ret |= (LibVBANext.Buttons)Enum.Parse(typeof(LibVBANext.Buttons), s);
+ }
+ return ret;
+ }
+
+ #endregion
+
+ #region VideoProvider
+
+ int[] videobuff = new int[240 * 160];
+
+ public IVideoProvider VideoProvider { get { return this; } }
+ public int[] GetVideoBuffer() { return videobuff; }
+ public int VirtualWidth { get { return 240; } }
+ public int VirtualHeight { get { return 160; } }
+ public int BufferWidth { get { return 240; } }
+ public int BufferHeight { get { return 160; } }
+ public int BackgroundColor { get { return unchecked((int)0xff000000); } }
+
+ #endregion
+
+ #region SoundProvider
+
+ short[] soundbuff = new short[2048];
+
+ public ISoundProvider SoundProvider { get { return null; } }
+ public ISyncSoundProvider SyncSoundProvider { get { return this; } }
+ public bool StartAsyncSound() { return false; }
+ public void EndAsyncSound() { }
+
+ public void GetSamples(out short[] samples, out int nsamp)
+ {
+ // TODO
+ samples = soundbuff;
+ nsamp = 735;
+ }
+
+ public void DiscardSamples()
+ {
+ }
+
+ #endregion
+ }
+}
diff --git a/output/dll/libvbanext.dll b/output/dll/libvbanext.dll
new file mode 100644
index 0000000000..a7a773b1f1
Binary files /dev/null and b/output/dll/libvbanext.dll differ
diff --git a/vbanext/constarrays.h b/vbanext/constarrays.h
new file mode 100644
index 0000000000..0d32b46194
--- /dev/null
+++ b/vbanext/constarrays.h
@@ -0,0 +1,54 @@
+#ifndef CONSTARRAYS_H
+#define CONSTARRAYS_H
+
+static float const apu_vols [4] = { -0.25f, -0.5f, -1.0f, -0.25f };
+
+static const int table [0x40] =
+{
+ 0xFF10, 0,0xFF11,0xFF12,0xFF13,0xFF14, 0, 0,
+ 0xFF16,0xFF17, 0, 0,0xFF18,0xFF19, 0, 0,
+ 0xFF1A, 0,0xFF1B,0xFF1C,0xFF1D,0xFF1E, 0, 0,
+ 0xFF20,0xFF21, 0, 0,0xFF22,0xFF23, 0, 0,
+ 0xFF24,0xFF25, 0, 0,0xFF26, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,
+ 0xFF38,0xFF39,0xFF3A,0xFF3B,0xFF3C,0xFF3D,0xFF3E,0xFF3F,
+};
+
+static const uint32_t TIMER_TICKS[4] = {0, 6, 8, 10};
+
+static const uint8_t gamepakRamWaitState[4] = { 4, 3, 2, 8 };
+
+static const uint32_t objTilesAddress [3] = {0x010000, 0x014000, 0x014000};
+
+static const u32 AlphaClampLUT[64] =
+{
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
+ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F
+};
+
+static const u32 map_sizes_rot[] = { 128, 256, 512, 1024 };
+
+static const u32 map_widths[] = { 256, 512, 256, 512 };
+static const u32 map_heights[] = { 256, 256, 512, 512 };
+
+
+static const int coeff[32] = {0,1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16};
+
+
+static const uint8_t memoryWait_init[16] =
+ { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0 };
+static const uint8_t memoryWaitSeq_init[16] =
+ { 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 4, 4, 8, 8, 4, 0 };
+static const uint8_t memoryWait32_init[16] =
+ { 0, 0, 5, 0, 0, 1, 1, 0, 7, 7, 9, 9, 13, 13, 4, 0 };
+static const uint8_t memoryWaitSeq32_init[16] =
+ { 0, 0, 5, 0, 0, 1, 1, 0, 5, 5, 9, 9, 17, 17, 4, 0 };
+
+
+#endif
diff --git a/vbanext/instance.cpp b/vbanext/instance.cpp
new file mode 100644
index 0000000000..a99f7245db
--- /dev/null
+++ b/vbanext/instance.cpp
@@ -0,0 +1,13286 @@
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#define LSB_FIRST
+#ifdef SPEEDHAX
+#error NO SPEEDHAX
+#endif
+#define HAVE_HLE_BIOS
+
+#include "port.h"
+
+#include "instance.h"
+
+#include "sound_blargg.h"
+
+#include "constarrays.h"
+
+#define INLINE
+
+class Gigazoid
+{
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// BEGIN MEMORY.CPP
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*============================================================
+ UTIL
+============================================================ */
+
+static int utilGetSize(int size)
+{
+ int res = 1;
+
+ while(res < size)
+ res <<= 1;
+
+ return res;
+}
+
+/* Not endian safe, but VBA itself doesn't seem to care */
+void utilWriteIntMem(uint8_t *& data, int val)
+{
+ memcpy(data, &val, sizeof(int));
+ data += sizeof(int);
+}
+
+void utilWriteMem(uint8_t *& data, const void *in_data, unsigned size)
+{
+ memcpy(data, in_data, size);
+ data += size;
+}
+
+int utilReadIntMem(const uint8_t *& data)
+{
+ int res;
+
+ memcpy(&res, data, sizeof(int));
+ data += sizeof(int);
+ return res;
+}
+
+void utilReadMem(void *buf, const uint8_t *& data, unsigned size)
+{
+ memcpy(buf, data, size);
+ data += size;
+}
+
+/*============================================================
+ FLASH
+============================================================ */
+
+
+#define FLASH_READ_ARRAY 0
+#define FLASH_CMD_1 1
+#define FLASH_CMD_2 2
+#define FLASH_AUTOSELECT 3
+#define FLASH_CMD_3 4
+#define FLASH_CMD_4 5
+#define FLASH_CMD_5 6
+#define FLASH_ERASE_COMPLETE 7
+#define FLASH_PROGRAM 8
+#define FLASH_SETBANK 9
+
+#ifdef __LIBRETRO__
+extern uint8_t libretro_save_buf[0x20000 + 0x2000];
+uint8_t *flashSaveMemory = libretro_save_buf;
+#else
+uint8_t flashSaveMemory[FLASH_128K_SZ];
+#endif
+
+int flashState; // = FLASH_READ_ARRAY;
+int flashReadState; // = FLASH_READ_ARRAY;
+int flashSize; // = 0x10000;
+int flashDeviceID; // = 0x1b;
+int flashManufacturerID; // = 0x32;
+int flashBank; // = 0;
+
+void flashInit (void)
+{
+#ifdef __LIBRETRO__
+ memset(flashSaveMemory, 0xff, 0x20000);
+#else
+ memset(flashSaveMemory, 0xff, sizeof(flashSaveMemory));
+#endif
+}
+
+void flashReset()
+{
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ flashBank = 0;
+}
+
+void flashSetSize(int size)
+{
+ if(size == 0x10000) {
+ flashDeviceID = 0x1b;
+ flashManufacturerID = 0x32;
+ } else {
+ flashDeviceID = 0x13; //0x09;
+ flashManufacturerID = 0x62; //0xc2;
+ }
+ // Added to make 64k saves compatible with 128k ones
+ // (allow wrongfuly set 64k saves to work for Pokemon games)
+ if ((size == 0x20000) && (flashSize == 0x10000))
+ memcpy((uint8_t *)(flashSaveMemory+0x10000), (uint8_t *)(flashSaveMemory), 0x10000);
+ flashSize = size;
+}
+
+uint8_t flashRead(uint32_t address)
+{
+ address &= 0xFFFF;
+
+ switch(flashReadState) {
+ case FLASH_READ_ARRAY:
+ return flashSaveMemory[(flashBank << 16) + address];
+ case FLASH_AUTOSELECT:
+ switch(address & 0xFF)
+ {
+ case 0:
+ // manufacturer ID
+ return flashManufacturerID;
+ case 1:
+ // device ID
+ return flashDeviceID;
+ }
+ break;
+ case FLASH_ERASE_COMPLETE:
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ return 0xFF;
+ };
+ return 0;
+}
+
+void flashSaveDecide(uint32_t address, uint8_t byte)
+{
+ if(address == 0x0e005555) {
+ saveType = 2;
+ cpuSaveGameFunc = &Gigazoid::flashWrite;
+ } else {
+ saveType = 1;
+ cpuSaveGameFunc = &Gigazoid::sramWrite;
+ }
+
+ (this->*cpuSaveGameFunc)(address, byte);
+}
+
+void flashDelayedWrite(uint32_t address, uint8_t byte)
+{
+ saveType = 2;
+ cpuSaveGameFunc = &Gigazoid::flashWrite;
+ flashWrite(address, byte);
+}
+
+void flashWrite(uint32_t address, uint8_t byte)
+{
+ address &= 0xFFFF;
+ switch(flashState) {
+ case FLASH_READ_ARRAY:
+ if(address == 0x5555 && byte == 0xAA)
+ flashState = FLASH_CMD_1;
+ break;
+ case FLASH_CMD_1:
+ if(address == 0x2AAA && byte == 0x55)
+ flashState = FLASH_CMD_2;
+ else
+ flashState = FLASH_READ_ARRAY;
+ break;
+ case FLASH_CMD_2:
+ if(address == 0x5555) {
+ if(byte == 0x90) {
+ flashState = FLASH_AUTOSELECT;
+ flashReadState = FLASH_AUTOSELECT;
+ } else if(byte == 0x80) {
+ flashState = FLASH_CMD_3;
+ } else if(byte == 0xF0) {
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ } else if(byte == 0xA0) {
+ flashState = FLASH_PROGRAM;
+ } else if(byte == 0xB0 && flashSize == 0x20000) {
+ flashState = FLASH_SETBANK;
+ } else {
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ }
+ } else {
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ }
+ break;
+ case FLASH_CMD_3:
+ if(address == 0x5555 && byte == 0xAA) {
+ flashState = FLASH_CMD_4;
+ } else {
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ }
+ break;
+ case FLASH_CMD_4:
+ if(address == 0x2AAA && byte == 0x55) {
+ flashState = FLASH_CMD_5;
+ } else {
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ }
+ break;
+ case FLASH_CMD_5:
+ if(byte == 0x30) {
+ // SECTOR ERASE
+ memset(&flashSaveMemory[(flashBank << 16) + (address & 0xF000)],
+ 0,
+ 0x1000);
+ flashReadState = FLASH_ERASE_COMPLETE;
+ } else if(byte == 0x10) {
+ // CHIP ERASE
+ memset(flashSaveMemory, 0, flashSize);
+ flashReadState = FLASH_ERASE_COMPLETE;
+ } else {
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ }
+ break;
+ case FLASH_AUTOSELECT:
+ if(byte == 0xF0) {
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ } else if(address == 0x5555 && byte == 0xAA)
+ flashState = FLASH_CMD_1;
+ else {
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ }
+ break;
+ case FLASH_PROGRAM:
+ flashSaveMemory[(flashBank<<16)+address] = byte;
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ break;
+ case FLASH_SETBANK:
+ if(address == 0) {
+ flashBank = (byte & 1);
+ }
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ break;
+ }
+}
+
+/*============================================================
+ EEPROM
+============================================================ */
+int eepromMode; // = EEPROM_IDLE;
+int eepromByte; // = 0;
+int eepromBits; // = 0;
+int eepromAddress; // = 0;
+
+#ifdef __LIBRETRO__
+// Workaround for broken-by-design GBA save semantics.
+extern u8 libretro_save_buf[0x20000 + 0x2000];
+u8 *eepromData = libretro_save_buf + 0x20000;
+#else
+u8 eepromData[0x2000];
+#endif
+
+u8 eepromBuffer[16];
+bool eepromInUse; // = false;
+int eepromSize; // = 512;
+
+void eepromInit (void)
+{
+#ifdef __LIBRETRO__
+ memset(eepromData, 255, 0x2000);
+#else
+ memset(eepromData, 255, sizeof(eepromData));
+#endif
+}
+
+void eepromReset (void)
+{
+ eepromMode = EEPROM_IDLE;
+ eepromByte = 0;
+ eepromBits = 0;
+ eepromAddress = 0;
+ eepromInUse = false;
+ eepromSize = 512;
+}
+
+int eepromRead (void)
+{
+ switch(eepromMode)
+ {
+ case EEPROM_IDLE:
+ case EEPROM_READADDRESS:
+ case EEPROM_WRITEDATA:
+ return 1;
+ case EEPROM_READDATA:
+ {
+ eepromBits++;
+ if(eepromBits == 4) {
+ eepromMode = EEPROM_READDATA2;
+ eepromBits = 0;
+ eepromByte = 0;
+ }
+ return 0;
+ }
+ case EEPROM_READDATA2:
+ {
+ int data = 0;
+ int address = eepromAddress << 3;
+ int mask = 1 << (7 - (eepromBits & 7));
+ data = (eepromData[address+eepromByte] & mask) ? 1 : 0;
+ eepromBits++;
+ if((eepromBits & 7) == 0)
+ eepromByte++;
+ if(eepromBits == 0x40)
+ eepromMode = EEPROM_IDLE;
+ return data;
+ }
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void eepromWrite(u8 value)
+{
+ if(cpuDmaCount == 0)
+ return;
+ int bit = value & 1;
+ switch(eepromMode) {
+ case EEPROM_IDLE:
+ eepromByte = 0;
+ eepromBits = 1;
+ eepromBuffer[eepromByte] = bit;
+ eepromMode = EEPROM_READADDRESS;
+ break;
+ case EEPROM_READADDRESS:
+ eepromBuffer[eepromByte] <<= 1;
+ eepromBuffer[eepromByte] |= bit;
+ eepromBits++;
+ if((eepromBits & 7) == 0) {
+ eepromByte++;
+ }
+ if(cpuDmaCount == 0x11 || cpuDmaCount == 0x51) {
+ if(eepromBits == 0x11) {
+ eepromInUse = true;
+ eepromSize = 0x2000;
+ eepromAddress = ((eepromBuffer[0] & 0x3F) << 8) |
+ ((eepromBuffer[1] & 0xFF));
+ if(!(eepromBuffer[0] & 0x40)) {
+ eepromBuffer[0] = bit;
+ eepromBits = 1;
+ eepromByte = 0;
+ eepromMode = EEPROM_WRITEDATA;
+ } else {
+ eepromMode = EEPROM_READDATA;
+ eepromByte = 0;
+ eepromBits = 0;
+ }
+ }
+ } else {
+ if(eepromBits == 9) {
+ eepromInUse = true;
+ eepromAddress = (eepromBuffer[0] & 0x3F);
+ if(!(eepromBuffer[0] & 0x40)) {
+ eepromBuffer[0] = bit;
+ eepromBits = 1;
+ eepromByte = 0;
+ eepromMode = EEPROM_WRITEDATA;
+ } else {
+ eepromMode = EEPROM_READDATA;
+ eepromByte = 0;
+ eepromBits = 0;
+ }
+ }
+ }
+ break;
+ case EEPROM_READDATA:
+ case EEPROM_READDATA2:
+ // should we reset here?
+ eepromMode = EEPROM_IDLE;
+ break;
+ case EEPROM_WRITEDATA:
+ eepromBuffer[eepromByte] <<= 1;
+ eepromBuffer[eepromByte] |= bit;
+ eepromBits++;
+ if((eepromBits & 7) == 0)
+ eepromByte++;
+ if(eepromBits == 0x40)
+ {
+ eepromInUse = true;
+ // write data;
+ for(int i = 0; i < 8; i++)
+ eepromData[(eepromAddress << 3) + i] = eepromBuffer[i];
+ }
+ else if(eepromBits == 0x41)
+ {
+ eepromMode = EEPROM_IDLE;
+ eepromByte = 0;
+ eepromBits = 0;
+ }
+ break;
+ }
+}
+
+/*============================================================
+ SRAM
+============================================================ */
+
+u8 sramRead(u32 address)
+{
+ return flashSaveMemory[address & 0xFFFF];
+}
+
+void sramDelayedWrite(u32 address, u8 byte)
+{
+ saveType = 1;
+ cpuSaveGameFunc = &Gigazoid::sramWrite;
+ sramWrite(address, byte);
+}
+
+void sramWrite(u32 address, u8 byte)
+{
+ flashSaveMemory[address & 0xFFFF] = byte;
+}
+
+/*============================================================
+ RTC
+============================================================ */
+
+#define IDLE 0
+#define COMMAND 1
+#define DATA 2
+#define READDATA 3
+
+typedef struct
+{
+ u8 byte0;
+ u8 byte1;
+ u8 byte2;
+ u8 command;
+ int dataLen;
+ int bits;
+ int state;
+ u8 data[12];
+ // reserved variables for future
+ u8 reserved[12];
+ bool reserved2;
+ u32 reserved3;
+} RTCCLOCKDATA;
+
+RTCCLOCKDATA rtcClockData;
+bool rtcEnabled; // = false;
+
+void rtcEnable(bool e)
+{
+ rtcEnabled = e;
+}
+
+bool rtcIsEnabled (void)
+{
+ return rtcEnabled;
+}
+
+u16 rtcRead(u32 address)
+{
+ if(rtcEnabled)
+ {
+ switch(address)
+ {
+ case 0x80000c8:
+ return rtcClockData.byte2;
+ case 0x80000c6:
+ return rtcClockData.byte1;
+ case 0x80000c4:
+ return rtcClockData.byte0;
+ }
+ }
+
+ return READ16LE((&rom[address & 0x1FFFFFE]));
+}
+
+static u8 toBCD(u8 value)
+{
+ value = value % 100;
+ int l = value % 10;
+ int h = value / 10;
+ return h * 16 + l;
+}
+
+bool rtcWrite(u32 address, u16 value)
+{
+ if(!rtcEnabled)
+ return false;
+
+ if(address == 0x80000c8)
+ rtcClockData.byte2 = (u8)value; // enable ?
+ else if(address == 0x80000c6)
+ rtcClockData.byte1 = (u8)value; // read/write
+ else if(address == 0x80000c4)
+ {
+ if(rtcClockData.byte2 & 1)
+ {
+ if(rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5)
+ {
+ rtcClockData.state = COMMAND;
+ rtcClockData.bits = 0;
+ rtcClockData.command = 0;
+ }
+ else if(!(rtcClockData.byte0 & 1) && (value & 1))
+ { // bit transfer
+ rtcClockData.byte0 = (u8)value;
+ switch(rtcClockData.state)
+ {
+ case COMMAND:
+ rtcClockData.command |= ((value & 2) >> 1) << (7-rtcClockData.bits);
+ rtcClockData.bits++;
+ if(rtcClockData.bits == 8)
+ {
+ rtcClockData.bits = 0;
+ switch(rtcClockData.command)
+ {
+ case 0x60:
+ // not sure what this command does but it doesn't take parameters
+ // maybe it is a reset or stop
+ rtcClockData.state = IDLE;
+ rtcClockData.bits = 0;
+ break;
+ case 0x62:
+ // this sets the control state but not sure what those values are
+ rtcClockData.state = READDATA;
+ rtcClockData.dataLen = 1;
+ break;
+ case 0x63:
+ rtcClockData.dataLen = 1;
+ rtcClockData.data[0] = 0x40;
+ rtcClockData.state = DATA;
+ break;
+ case 0x64:
+ break;
+ case 0x65:
+ {
+ struct tm *newtime;
+ time_t long_time;
+
+ time( &long_time ); /* Get time as long integer. */
+ newtime = localtime( &long_time ); /* Convert to local time. */
+
+ rtcClockData.dataLen = 7;
+ rtcClockData.data[0] = toBCD(newtime->tm_year);
+ rtcClockData.data[1] = toBCD(newtime->tm_mon+1);
+ rtcClockData.data[2] = toBCD(newtime->tm_mday);
+ rtcClockData.data[3] = toBCD(newtime->tm_wday);
+ rtcClockData.data[4] = toBCD(newtime->tm_hour);
+ rtcClockData.data[5] = toBCD(newtime->tm_min);
+ rtcClockData.data[6] = toBCD(newtime->tm_sec);
+ rtcClockData.state = DATA;
+ }
+ break;
+ case 0x67:
+ {
+ struct tm *newtime;
+ time_t long_time;
+
+ time( &long_time ); /* Get time as long integer. */
+ newtime = localtime( &long_time ); /* Convert to local time. */
+
+ rtcClockData.dataLen = 3;
+ rtcClockData.data[0] = toBCD(newtime->tm_hour);
+ rtcClockData.data[1] = toBCD(newtime->tm_min);
+ rtcClockData.data[2] = toBCD(newtime->tm_sec);
+ rtcClockData.state = DATA;
+ }
+ break;
+ default:
+ systemMessage(0, "Unknown RTC command %02x", rtcClockData.command);
+ rtcClockData.state = IDLE;
+ break;
+ }
+ }
+ break;
+ case DATA:
+ if(rtcClockData.byte1 & 2)
+ {
+ }
+ else
+ {
+ rtcClockData.byte0 = (rtcClockData.byte0 & ~2) |
+ ((rtcClockData.data[rtcClockData.bits >> 3] >>
+ (rtcClockData.bits & 7)) & 1)*2;
+ rtcClockData.bits++;
+ if(rtcClockData.bits == 8*rtcClockData.dataLen)
+ {
+ rtcClockData.bits = 0;
+ rtcClockData.state = IDLE;
+ }
+ }
+ break;
+ case READDATA:
+ if(!(rtcClockData.byte1 & 2)) {
+ } else {
+ rtcClockData.data[rtcClockData.bits >> 3] =
+ (rtcClockData.data[rtcClockData.bits >> 3] >> 1) |
+ ((value << 6) & 128);
+ rtcClockData.bits++;
+ if(rtcClockData.bits == 8*rtcClockData.dataLen) {
+ rtcClockData.bits = 0;
+ rtcClockData.state = IDLE;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ } else
+ rtcClockData.byte0 = (u8)value;
+ }
+ }
+ return true;
+}
+
+void rtcReset (void)
+{
+ memset(&rtcClockData, 0, sizeof(rtcClockData));
+
+ rtcClockData.byte0 = 0;
+ rtcClockData.byte1 = 0;
+ rtcClockData.byte2 = 0;
+ rtcClockData.command = 0;
+ rtcClockData.dataLen = 0;
+ rtcClockData.bits = 0;
+ rtcClockData.state = IDLE;
+}
+
+void rtcSaveGameMem(uint8_t *& data)
+{
+ utilWriteMem(data, &rtcClockData, sizeof(rtcClockData));
+}
+
+void rtcReadGameMem(const uint8_t *& data)
+{
+ utilReadMem(&rtcClockData, data, sizeof(rtcClockData));
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// END MEMORY.CPP
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// BEGIN SOUND.CPP
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#define NR10 0x60
+#define NR11 0x62
+#define NR12 0x63
+#define NR13 0x64
+#define NR14 0x65
+#define NR21 0x68
+#define NR22 0x69
+#define NR23 0x6c
+#define NR24 0x6d
+#define NR30 0x70
+#define NR31 0x72
+#define NR32 0x73
+#define NR33 0x74
+#define NR34 0x75
+#define NR41 0x78
+#define NR42 0x79
+#define NR43 0x7c
+#define NR44 0x7d
+#define NR50 0x80
+#define NR51 0x81
+#define NR52 0x84
+
+/* 1/100th of a second */
+#define SOUND_CLOCK_TICKS_ 167772
+#define SOUNDVOLUME 0.5f
+#define SOUNDVOLUME_ -1
+
+/*============================================================
+ CLASS DECLS
+============================================================ */
+
+class Blip_Buffer
+{
+ public:
+ Blip_Buffer::Blip_Buffer()
+ {
+ factor_ = INT_MAX;
+ buffer_ = 0;
+ buffer_size_ = 0;
+ sample_rate_ = 0;
+ clock_rate_ = 0;
+ length_ = 0;
+
+ clear();
+ }
+
+ Blip_Buffer::~Blip_Buffer()
+ {
+ if (buffer_)
+ free(buffer_);
+ }
+
+ void Blip_Buffer::clear( void)
+ {
+ offset_ = 0;
+ reader_accum_ = 0;
+ if (buffer_)
+ memset( buffer_, 0, (buffer_size_ + BLIP_BUFFER_EXTRA_) * sizeof (int32_t) );
+ }
+
+ const char * Blip_Buffer::set_sample_rate( long new_rate, int msec )
+ {
+ /* start with maximum length that resampled time can represent*/
+ long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - BLIP_BUFFER_EXTRA_ - 64;
+ if ( msec != 0)
+ {
+ long s = (new_rate * (msec + 1) + 999) / 1000;
+ if ( s < new_size )
+ new_size = s;
+ }
+
+ if ( buffer_size_ != new_size )
+ {
+ void* p = realloc( buffer_, (new_size + BLIP_BUFFER_EXTRA_) * sizeof *buffer_ );
+ if ( !p )
+ return "Out of memory";
+ buffer_ = (int32_t *) p;
+ }
+
+ buffer_size_ = new_size;
+
+ /* update things based on the sample rate*/
+ sample_rate_ = new_rate;
+ length_ = new_size * 1000 / new_rate - 1;
+
+ /* update these since they depend on sample rate*/
+ if ( clock_rate_ )
+ factor_ = clock_rate_factor( clock_rate_);
+
+ clear();
+
+ return 0;
+ }
+
+ /* Sets number of source time units per second */
+
+ uint32_t Blip_Buffer::clock_rate_factor( long rate ) const
+ {
+ double ratio = (double) sample_rate_ / rate;
+ int32_t factor = (int32_t) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 );
+ return (uint32_t) factor;
+ }
+ long clock_rate_;
+ int length_; /* Length of buffer in milliseconds*/
+ long sample_rate_; /* Current output sample rate*/
+ uint32_t factor_;
+ uint32_t offset_;
+ int32_t * buffer_;
+ int32_t buffer_size_;
+ int32_t reader_accum_;
+ private:
+ Blip_Buffer( const Blip_Buffer& );
+ Blip_Buffer& operator = ( const Blip_Buffer& );
+};
+
+class Blip_Synth
+{
+ public:
+ Blip_Buffer* buf;
+ int delta_factor;
+
+ Blip_Synth()
+ {
+ }
+
+ void volume( double v ) { delta_factor = int ((v * 1.0) * (1L << BLIP_SAMPLE_BITS) + 0.5); }
+ INLINE void Blip_Synth::offset_resampled( uint32_t time, int delta, Blip_Buffer* blip_buf ) const
+ {
+ int32_t left, right, phase;
+ int32_t *buf;
+
+ delta *= delta_factor;
+ buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY);
+ phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & BLIP_RES_MIN_ONE);
+
+ left = buf [0] + delta;
+
+ right = (delta >> BLIP_PHASE_BITS) * phase;
+
+ left -= right;
+ right += buf [1];
+
+ buf [0] = left;
+ buf [1] = right;
+ }
+
+ INLINE void Blip_Synth::offset( int32_t t, int delta, Blip_Buffer* buf ) const
+ {
+ offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
+ }
+ void offset_inline( int32_t t, int delta, Blip_Buffer* buf ) const {
+ offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
+ }
+ void offset_inline( int32_t t, int delta ) const {
+ offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
+ }
+};
+
+#define TRIGGER_MASK 0x80
+#define LENGTH_ENABLED 0x40
+
+#define VOLUME_SHIFT_PLUS_FOUR 6
+#define SIZE20_MASK 0x20
+
+
+
+#define reload_sweep_timer() \
+ sweep_delay = (regs [0] & PERIOD_MASK) >> 4; \
+ if ( !sweep_delay ) \
+ sweep_delay = 8;
+
+class Gb_Osc
+{
+ public:
+ Blip_Buffer* outputs [4]; /* NULL, right, left, center*/
+ Blip_Buffer* output; /* where to output sound*/
+ uint8_t * regs; /* osc's 5 registers*/
+ int mode; /* mode_dmg, mode_cgb, mode_agb*/
+ int dac_off_amp; /* amplitude when DAC is off*/
+ int last_amp; /* current amplitude in Blip_Buffer*/
+ Blip_Synth const* good_synth;
+ Blip_Synth const* med_synth;
+
+ int delay; /* clocks until frequency timer expires*/
+ int length_ctr; /* length counter*/
+ unsigned phase; /* waveform phase (or equivalent)*/
+ bool enabled; /* internal enabled flag*/
+
+ void Gb_Osc::clock_length()
+ {
+ if ( (regs [4] & LENGTH_ENABLED) && length_ctr )
+ {
+ if ( --length_ctr <= 0 )
+ enabled = false;
+ }
+ }
+ void Gb_Osc::reset()
+ {
+ output = 0;
+ last_amp = 0;
+ delay = 0;
+ phase = 0;
+ enabled = false;
+ }
+ protected:
+ INLINE void Gb_Osc::update_amp( int32_t time, int new_amp )
+ {
+ int delta = new_amp - last_amp;
+ if ( delta )
+ {
+ last_amp = new_amp;
+ med_synth->offset( time, delta, output );
+ }
+ }
+ int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data )
+ {
+ int data = regs [4];
+
+ if ( (frame_phase & 1) && !(old_data & LENGTH_ENABLED) && length_ctr )
+ {
+ if ( (data & LENGTH_ENABLED))
+ length_ctr--;
+ }
+
+ if ( data & TRIGGER_MASK )
+ {
+ enabled = true;
+ if ( !length_ctr )
+ {
+ length_ctr = max_len;
+ if ( (frame_phase & 1) && (data & LENGTH_ENABLED) )
+ length_ctr--;
+ }
+ }
+
+ if ( !length_ctr )
+ enabled = false;
+
+ return data & TRIGGER_MASK;
+ }
+};
+
+class Gb_Env : public Gb_Osc
+{
+ public:
+ int env_delay;
+ int volume;
+ bool env_enabled;
+
+ void Gb_Env::clock_envelope()
+ {
+ if ( env_enabled && --env_delay <= 0 && reload_env_timer() )
+ {
+ int v = volume + (regs [2] & 0x08 ? +1 : -1);
+ if ( 0 <= v && v <= 15 )
+ volume = v;
+ else
+ env_enabled = false;
+ }
+ }
+ bool Gb_Env::write_register( int frame_phase, int reg, int old, int data )
+ {
+ int const max_len = 64;
+
+ switch ( reg )
+ {
+ case 1:
+ length_ctr = max_len - (data & (max_len - 1));
+ break;
+
+ case 2:
+ if ( !GB_ENV_DAC_ENABLED() )
+ enabled = false;
+
+ zombie_volume( old, data );
+
+ if ( (data & 7) && env_delay == 8 )
+ {
+ env_delay = 1;
+ clock_envelope(); // TODO: really happens at next length clock
+ }
+ break;
+
+ case 4:
+ if ( write_trig( frame_phase, max_len, old ) )
+ {
+ volume = regs [2] >> 4;
+ reload_env_timer();
+ env_enabled = true;
+ if ( frame_phase == 7 )
+ env_delay++;
+ if ( !GB_ENV_DAC_ENABLED() )
+ enabled = false;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void reset()
+ {
+ env_delay = 0;
+ volume = 0;
+ Gb_Osc::reset();
+ }
+ private:
+ INLINE void Gb_Env::zombie_volume( int old, int data )
+ {
+ int v = volume;
+
+ // CGB-05 behavior, very close to AGB behavior as well
+ if ( (old ^ data) & 8 )
+ {
+ if ( !(old & 8) )
+ {
+ v++;
+ if ( old & 7 )
+ v++;
+ }
+
+ v = 16 - v;
+ }
+ else if ( (old & 0x0F) == 8 )
+ v++;
+ volume = v & 0x0F;
+ }
+ INLINE int Gb_Env::reload_env_timer()
+ {
+ int raw = regs [2] & 7;
+ env_delay = (raw ? raw : 8);
+ return raw;
+ }
+};
+
+class Gb_Square : public Gb_Env
+{
+ public:
+ bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data )
+ {
+ bool result = Gb_Env::write_register( frame_phase, reg, old_data, data );
+ if ( result )
+ delay = (delay & (CLK_MUL_MUL_4 - 1)) + period();
+ return result;
+ }
+ void Gb_Square::run( int32_t time, int32_t end_time )
+ {
+ /* Calc duty and phase*/
+ static unsigned char const duty_offsets [4] = { 1, 1, 3, 7 };
+ static unsigned char const duties [4] = { 1, 2, 4, 6 };
+ int const duty_code = regs [1] >> 6;
+ int32_t duty_offset = duty_offsets [duty_code];
+ int32_t duty = duties [duty_code];
+ /* AGB uses inverted duty*/
+ duty_offset -= duty;
+ duty = 8 - duty;
+ int ph = (phase + duty_offset) & 7;
+
+ /* Determine what will be generated*/
+ int vol = 0;
+ Blip_Buffer* const out = output;
+ if ( out )
+ {
+ int amp = dac_off_amp;
+ if ( GB_ENV_DAC_ENABLED() )
+ {
+ if ( enabled )
+ vol = volume;
+
+ amp = -(vol >> 1);
+
+ /* Play inaudible frequencies as constant amplitude*/
+ if ( GB_OSC_FREQUENCY() >= 0x7FA && delay < CLK_MUL_MUL_32 )
+ {
+ amp += (vol * duty) >> 3;
+ vol = 0;
+ }
+
+ if ( ph < duty )
+ {
+ amp += vol;
+ vol = -vol;
+ }
+ }
+ update_amp( time, amp );
+ }
+
+ /* Generate wave*/
+ time += delay;
+ if ( time < end_time )
+ {
+ int const per = period();
+ if ( !vol )
+ {
+ /* Maintain phase when not playing*/
+ int count = (end_time - time + per - 1) / per;
+ ph += count; /* will be masked below*/
+ time += (int32_t) count * per;
+ }
+ else
+ {
+ /* Output amplitude transitions*/
+ int delta = vol;
+ do
+ {
+ ph = (ph + 1) & 7;
+ if ( ph == 0 || ph == duty )
+ {
+ good_synth->offset_inline( time, delta, out );
+ delta = -delta;
+ }
+ time += per;
+ }
+ while ( time < end_time );
+
+ if ( delta != vol )
+ last_amp -= delta;
+ }
+ phase = (ph - duty_offset) & 7;
+ }
+ delay = time - end_time;
+ }
+
+ void reset()
+ {
+ Gb_Env::reset();
+ delay = 0x40000000; /* TODO: something less hacky (never clocked until first trigger)*/
+ }
+ private:
+ /* Frequency timer period*/
+ int period() const { return (2048 - GB_OSC_FREQUENCY()) * (CLK_MUL_MUL_4); }
+};
+
+class Gb_Sweep_Square : public Gb_Square
+{
+ public:
+ int sweep_freq;
+ int sweep_delay;
+ bool sweep_enabled;
+ bool sweep_neg;
+
+ void Gb_Sweep_Square::clock_sweep()
+ {
+ if ( --sweep_delay <= 0 )
+ {
+ reload_sweep_timer();
+ if ( sweep_enabled && (regs [0] & PERIOD_MASK) )
+ {
+ calc_sweep( true );
+ calc_sweep( false );
+ }
+ }
+ }
+ INLINE void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data )
+ {
+ if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) )
+ enabled = false; // sweep negate disabled after used
+
+ if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) )
+ {
+ sweep_freq = GB_OSC_FREQUENCY();
+ sweep_neg = false;
+ reload_sweep_timer();
+ sweep_enabled = (regs [0] & (PERIOD_MASK | SHIFT_MASK)) != 0;
+ if ( regs [0] & SHIFT_MASK )
+ calc_sweep( false );
+ }
+ }
+
+ void reset()
+ {
+ sweep_freq = 0;
+ sweep_delay = 0;
+ sweep_enabled = false;
+ sweep_neg = false;
+ Gb_Square::reset();
+ }
+ private:
+ void Gb_Sweep_Square::calc_sweep( bool update )
+ {
+ int shift, delta, freq;
+
+ shift = regs [0] & SHIFT_MASK;
+ delta = sweep_freq >> shift;
+ sweep_neg = (regs [0] & 0x08) != 0;
+ freq = sweep_freq + (sweep_neg ? -delta : delta);
+
+ if ( freq > 0x7FF )
+ enabled = false;
+ else if ( shift && update )
+ {
+ sweep_freq = freq;
+
+ regs [3] = freq & 0xFF;
+ regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07);
+ }
+ }
+};
+
+class Gb_Noise : public Gb_Env
+{
+ public:
+ int divider; /* noise has more complex frequency divider setup*/
+
+ /* Quickly runs LFSR for a large number of clocks. For use when noise is generating*/
+ /* no sound.*/
+ unsigned run_lfsr( unsigned s, unsigned mask, int count )
+ {
+ /* optimization used in several places:*/
+ /* ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n)*/
+
+ if ( mask == 0x4000 )
+ {
+ if ( count >= 32767 )
+ count %= 32767;
+
+ /* Convert from Fibonacci to Galois configuration,*/
+ /* shifted left 1 bit*/
+ s ^= (s & 1) * 0x8000;
+
+ /* Each iteration is equivalent to clocking LFSR 255 times*/
+ while ( (count -= 255) > 0 )
+ s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3);
+ count += 255;
+
+ /* Each iteration is equivalent to clocking LFSR 15 times*/
+ /* (interesting similarity to single clocking below)*/
+ while ( (count -= 15) > 0 )
+ s ^= ((s & 2) * (3 << 13)) ^ (s >> 1);
+ count += 15;
+
+ /* Remaining singles*/
+ do{
+ --count;
+ s = ((s & 2) * (3 << 13)) ^ (s >> 1);
+ }while(count >= 0);
+
+ /* Convert back to Fibonacci configuration*/
+ s &= 0x7FFF;
+ }
+ else if ( count < 8)
+ {
+ /* won't fully replace upper 8 bits, so have to do the unoptimized way*/
+ do{
+ --count;
+ s = (s >> 1 | mask) ^ (mask & -((s - 1) & 2));
+ }while(count >= 0);
+ }
+ else
+ {
+ if ( count > 127 )
+ {
+ count %= 127;
+ if ( !count )
+ count = 127; /* must run at least once*/
+ }
+
+ /* Need to keep one extra bit of history*/
+ s = s << 1 & 0xFF;
+
+ /* Convert from Fibonacci to Galois configuration,*/
+ /* shifted left 2 bits*/
+ s ^= (s & 2) << 7;
+
+ /* Each iteration is equivalent to clocking LFSR 7 times*/
+ /* (interesting similarity to single clocking below)*/
+ while ( (count -= 7) > 0 )
+ s ^= ((s & 4) * (3 << 5)) ^ (s >> 1);
+ count += 7;
+
+ /* Remaining singles*/
+ while ( --count >= 0 )
+ s = ((s & 4) * (3 << 5)) ^ (s >> 1);
+
+ /* Convert back to Fibonacci configuration and*/
+ /* repeat last 8 bits above significant 7*/
+ s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F);
+ }
+
+ return s;
+ }
+
+ void Gb_Noise::run( int32_t time, int32_t end_time )
+ {
+ /* Determine what will be generated*/
+ int vol = 0;
+ Blip_Buffer* const out = output;
+ if ( out )
+ {
+ int amp = dac_off_amp;
+ if ( GB_ENV_DAC_ENABLED() )
+ {
+ if ( enabled )
+ vol = volume;
+
+ amp = -(vol >> 1);
+
+ if ( !(phase & 1) )
+ {
+ amp += vol;
+ vol = -vol;
+ }
+ }
+
+ /* AGB negates final output*/
+ vol = -vol;
+ amp = -amp;
+
+ update_amp( time, amp );
+ }
+
+ /* Run timer and calculate time of next LFSR clock*/
+ static unsigned char const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 };
+ int const period1 = period1s [regs [3] & 7] * CLK_MUL;
+ {
+ int extra = (end_time - time) - delay;
+ int const per2 = GB_NOISE_PERIOD2(8);
+ time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1;
+
+ int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1);
+ divider = (divider - count) & PERIOD2_MASK;
+ delay = count * period1 - extra;
+ }
+
+ /* Generate wave*/
+ if ( time < end_time )
+ {
+ unsigned const mask = GB_NOISE_LFSR_MASK();
+ unsigned bits = phase;
+
+ int per = GB_NOISE_PERIOD2( period1 * 8 );
+ if ( GB_NOISE_PERIOD2_INDEX() >= 0xE )
+ {
+ time = end_time;
+ }
+ else if ( !vol )
+ {
+ /* Maintain phase when not playing*/
+ int count = (end_time - time + per - 1) / per;
+ time += (int32_t) count * per;
+ bits = run_lfsr( bits, ~mask, count );
+ }
+ else
+ {
+ /* Output amplitude transitions*/
+ int delta = -vol;
+ do
+ {
+ unsigned changed = bits + 1;
+ bits = bits >> 1 & mask;
+ if ( changed & 2 )
+ {
+ bits |= ~mask;
+ delta = -delta;
+ med_synth->offset_inline( time, delta, out );
+ }
+ time += per;
+ }
+ while ( time < end_time );
+
+ if ( delta == vol )
+ last_amp += delta;
+ }
+ phase = bits;
+ }
+ }
+ INLINE void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data )
+ {
+ if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) )
+ {
+ phase = 0x7FFF;
+ delay += CLK_MUL_MUL_8;
+ }
+ }
+
+ void reset()
+ {
+ divider = 0;
+ Gb_Env::reset();
+ delay = CLK_MUL_MUL_4; /* TODO: remove?*/
+ }
+};
+
+class Gb_Wave : public Gb_Osc
+{
+ public:
+ int sample_buf; /* last wave RAM byte read (hardware has this as well)*/
+ int agb_mask; /* 0xFF if AGB features enabled, 0 otherwise*/
+ uint8_t* wave_ram; /* 32 bytes (64 nybbles), stored in APU*/
+
+ INLINE void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data )
+ {
+ switch ( reg )
+ {
+ case 0:
+ if ( !GB_WAVE_DAC_ENABLED() )
+ enabled = false;
+ break;
+
+ case 1:
+ length_ctr = 256 - data;
+ break;
+
+ case 4:
+ bool was_enabled = enabled;
+ if ( write_trig( frame_phase, 256, old_data ) )
+ {
+ if ( !GB_WAVE_DAC_ENABLED() )
+ enabled = false;
+ phase = 0;
+ delay = period() + CLK_MUL_MUL_6;
+ }
+ }
+ }
+ void Gb_Wave::run( int32_t time, int32_t end_time )
+ {
+ /* Calc volume*/
+ static unsigned char const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 };
+ int const volume_idx = regs [2] >> 5 & (agb_mask | 3); /* 2 bits on DMG/CGB, 3 on AGB*/
+ int const volume_mul = volumes [volume_idx];
+
+ /* Determine what will be generated*/
+ int playing = false;
+ Blip_Buffer* const out = output;
+ if ( out )
+ {
+ int amp = dac_off_amp;
+ if ( GB_WAVE_DAC_ENABLED() )
+ {
+ /* Play inaudible frequencies as constant amplitude*/
+ amp = 128; /* really depends on average of all samples in wave*/
+
+ /* if delay is larger, constant amplitude won't start yet*/
+ if ( GB_OSC_FREQUENCY() <= 0x7FB || delay > CLK_MUL_MUL_15 )
+ {
+ if ( volume_mul )
+ playing = (int) enabled;
+
+ amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing;
+ }
+
+ amp = ((amp * volume_mul) >> VOLUME_SHIFT_PLUS_FOUR) - DAC_BIAS;
+ }
+ update_amp( time, amp );
+ }
+
+ /* Generate wave*/
+ time += delay;
+ if ( time < end_time )
+ {
+ unsigned char const* wave = wave_ram;
+
+ /* wave size and bank*/
+ int const flags = regs [0] & agb_mask;
+ int const wave_mask = (flags & SIZE20_MASK) | 0x1F;
+ int swap_banks = 0;
+ if ( flags & BANK40_MASK)
+ {
+ swap_banks = flags & SIZE20_MASK;
+ wave += BANK_SIZE_DIV_TWO - (swap_banks >> 1);
+ }
+
+ int ph = phase ^ swap_banks;
+ ph = (ph + 1) & wave_mask; /* pre-advance*/
+
+ int const per = period();
+ if ( !playing )
+ {
+ /* Maintain phase when not playing*/
+ int count = (end_time - time + per - 1) / per;
+ ph += count; /* will be masked below*/
+ time += (int32_t) count * per;
+ }
+ else
+ {
+ /* Output amplitude transitions*/
+ int lamp = last_amp + DAC_BIAS;
+ do
+ {
+ /* Extract nybble*/
+ int nybble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0;
+ ph = (ph + 1) & wave_mask;
+
+ /* Scale by volume*/
+ int amp = (nybble * volume_mul) >> VOLUME_SHIFT_PLUS_FOUR;
+
+ int delta = amp - lamp;
+ if ( delta )
+ {
+ lamp = amp;
+ med_synth->offset_inline( time, delta, out );
+ }
+ time += per;
+ }
+ while ( time < end_time );
+ last_amp = lamp - DAC_BIAS;
+ }
+ ph = (ph - 1) & wave_mask; /* undo pre-advance and mask position*/
+
+ /* Keep track of last byte read*/
+ if ( enabled )
+ sample_buf = wave [ph >> 1];
+
+ phase = ph ^ swap_banks; /* undo swapped banks*/
+ }
+ delay = time - end_time;
+ }
+
+ /* Reads/writes wave RAM*/
+ INLINE int Gb_Wave::read( unsigned addr ) const
+ {
+ int index;
+
+ if(enabled)
+ index = access( addr );
+ else
+ index = addr & 0x0F;
+
+ unsigned char const * wave_bank = &wave_ram[(~regs[0] & BANK40_MASK) >> 2 & agb_mask];
+
+ return (index < 0 ? 0xFF : wave_bank[index]);
+ }
+
+ INLINE void Gb_Wave::write( unsigned addr, int data )
+ {
+ int index;
+
+ if(enabled)
+ index = access( addr );
+ else
+ index = addr & 0x0F;
+
+ unsigned char * wave_bank = &wave_ram[(~regs[0] & BANK40_MASK) >> 2 & agb_mask];
+
+ if ( index >= 0 )
+ wave_bank[index] = data;;
+ }
+
+
+ void reset()
+ {
+ sample_buf = 0;
+ Gb_Osc::reset();
+ }
+
+ private:
+ friend class Gb_Apu;
+
+ /* Frequency timer period*/
+ int period() const { return (2048 - GB_OSC_FREQUENCY()) * (CLK_MUL_MUL_2); }
+
+ void Gb_Wave::corrupt_wave()
+ {
+ int pos = ((phase + 1) & BANK_SIZE_MIN_ONE) >> 1;
+ if ( pos < 4 )
+ wave_ram [0] = wave_ram [pos];
+ else
+ for ( int i = 4; --i >= 0; )
+ wave_ram [i] = wave_ram [(pos & ~3) + i];
+ }
+
+ /* Wave index that would be accessed, or -1 if no access would occur*/
+ int Gb_Wave::access( unsigned addr ) const
+ {
+ addr = (phase & BANK_SIZE_MIN_ONE) >> 1;
+ return addr & 0x0F;
+ }
+};
+
+/*============================================================
+ INLINE CLASS FUNCS
+============================================================ */
+
+int16_t soundFinalWave [1600];
+static const long soundSampleRate = 44100; // = 22050;
+int SOUND_CLOCK_TICKS; // = SOUND_CLOCK_TICKS_;
+int soundTicks; // = SOUND_CLOCK_TICKS_;
+
+int soundEnableFlag; // = 0x3ff; /* emulator channels enabled*/
+
+typedef struct
+{
+ int last_amp;
+ int last_time;
+ int shift;
+ Blip_Buffer* output;
+} gba_pcm_t;
+
+typedef struct
+{
+ bool enabled;
+ uint8_t fifo [32];
+ int count;
+ int dac;
+ int readIndex;
+ int writeIndex;
+ int which;
+ int timer;
+ gba_pcm_t pcm;
+} gba_pcm_fifo_t;
+
+gba_pcm_fifo_t pcm [2];
+
+
+Blip_Synth pcm_synth; // 32 kHz, 16 kHz, 8 kHz
+
+Blip_Buffer bufs_buffer [BUFS_SIZE];
+int mixer_samples_read;
+
+void gba_pcm_init (void)
+{
+ pcm[0].pcm.output = 0;
+ pcm[0].pcm.last_time = 0;
+ pcm[0].pcm.last_amp = 0;
+ pcm[0].pcm.shift = 0;
+
+ pcm[1].pcm.output = 0;
+ pcm[1].pcm.last_time = 0;
+ pcm[1].pcm.last_amp = 0;
+ pcm[1].pcm.shift = 0;
+}
+
+void gba_pcm_apply_control( int pcm_idx, int idx )
+{
+ int ch = 0;
+ pcm[pcm_idx].pcm.shift = ~ioMem [SGCNT0_H] >> (2 + idx) & 1;
+
+ if ( (ioMem [NR52] & 0x80) )
+ ch = ioMem [SGCNT0_H+1] >> (idx << 2) & 3;
+
+ Blip_Buffer* out = 0;
+ switch ( ch )
+ {
+ case 1:
+ out = &bufs_buffer[1];
+ break;
+ case 2:
+ out = &bufs_buffer[0];
+ break;
+ case 3:
+ out = &bufs_buffer[2];
+ break;
+ }
+
+ if ( pcm[pcm_idx].pcm.output != out )
+ {
+ if ( pcm[pcm_idx].pcm.output )
+ pcm_synth.offset( SOUND_CLOCK_TICKS - soundTicks, -pcm[pcm_idx].pcm.last_amp, pcm[pcm_idx].pcm.output );
+ pcm[pcm_idx].pcm.last_amp = 0;
+ pcm[pcm_idx].pcm.output = out;
+ }
+}
+
+/*============================================================
+ GB APU
+============================================================ */
+
+/* 0: Square 1, 1: Square 2, 2: Wave, 3: Noise */
+#define OSC_COUNT 4
+
+/* Resets hardware to initial power on state BEFORE boot ROM runs. Mode selects*/
+/* sound hardware. Additional AGB wave features are enabled separately.*/
+#define MODE_AGB 2
+
+#define START_ADDR 0xFF10
+#define END_ADDR 0xFF3F
+
+/* Reads and writes must be within the START_ADDR to END_ADDR range, inclusive.*/
+/* Addresses outside this range are not mapped to the sound hardware.*/
+#define REGISTER_COUNT 48
+#define REGS_SIZE 64
+
+/* Clock rate that sound hardware runs at.
+ * formula: 4194304 * 4
+ * */
+#define CLOCK_RATE 16777216
+
+struct gb_apu_t
+{
+ bool reduce_clicks_;
+ uint8_t regs[REGS_SIZE]; // last values written to registers
+ int32_t last_time; // time sound emulator has been run to
+ int32_t frame_time; // time of next frame sequencer action
+ int32_t frame_period; // clocks between each frame sequencer step
+ int32_t frame_phase; // phase of next frame sequencer step
+ double volume_;
+ Gb_Osc* oscs [OSC_COUNT];
+ Gb_Sweep_Square square1;
+ Gb_Square square2;
+ Gb_Wave wave;
+ Gb_Noise noise;
+ Blip_Synth good_synth;
+ Blip_Synth med_synth;
+} gb_apu;
+
+// Format of save state. Should be stable across versions of the library,
+// with earlier versions properly opening later save states. Includes some
+// room for expansion so the state size shouldn't increase.
+struct gb_apu_state_t
+{
+ // Values stored as plain int so your code can read/write them easily.
+ // Structure can NOT be written to disk, since format is not portable.
+ typedef int val_t;
+
+ enum { format0 = 0x50414247 };
+
+ val_t format; // format of all following data
+ val_t version; // later versions just add fields to end
+
+ unsigned char regs [0x40];
+ val_t frame_time;
+ val_t frame_phase;
+
+ val_t sweep_freq;
+ val_t sweep_delay;
+ val_t sweep_enabled;
+ val_t sweep_neg;
+ val_t noise_divider;
+ val_t wave_buf;
+
+ val_t delay [4];
+ val_t length_ctr [4];
+ val_t phase [4];
+ val_t enabled [4];
+
+ val_t env_delay [3];
+ val_t env_volume [3];
+ val_t env_enabled [3];
+
+ val_t unused [13]; // for future expansion
+};
+
+#define VOL_REG 0xFF24
+#define STEREO_REG 0xFF25
+#define STATUS_REG 0xFF26
+#define WAVE_RAM 0xFF30
+#define POWER_MASK 0x80
+
+#define OSC_COUNT 4
+
+void gb_apu_reduce_clicks( bool reduce )
+{
+ gb_apu.reduce_clicks_ = reduce;
+
+ /* Click reduction makes DAC off generate same output as volume 0*/
+ int dac_off_amp = 0;
+
+ gb_apu.oscs[0]->dac_off_amp = dac_off_amp;
+ gb_apu.oscs[1]->dac_off_amp = dac_off_amp;
+ gb_apu.oscs[2]->dac_off_amp = dac_off_amp;
+ gb_apu.oscs[3]->dac_off_amp = dac_off_amp;
+
+ /* AGB always eliminates clicks on wave channel using same method*/
+ gb_apu.wave.dac_off_amp = -DAC_BIAS;
+}
+
+void gb_apu_synth_volume( int iv )
+{
+ double v = gb_apu.volume_ * 0.60 / OSC_COUNT / 15 /*steps*/ / 8 /*master vol range*/ * iv;
+ gb_apu.good_synth.volume( v );
+ gb_apu.med_synth .volume( v );
+}
+
+void gb_apu_apply_volume (void)
+{
+ int data, left, right, vol_tmp;
+ data = gb_apu.regs [VOL_REG - START_ADDR];
+ left = data >> 4 & 7;
+ right = data & 7;
+ vol_tmp = left < right ? right : left;
+ gb_apu_synth_volume( vol_tmp + 1 );
+}
+
+void gb_apu_silence_osc( Gb_Osc& o )
+{
+ int delta;
+
+ delta = -o.last_amp;
+ if ( delta )
+ {
+ o.last_amp = 0;
+ if ( o.output )
+ {
+ gb_apu.med_synth.offset( gb_apu.last_time, delta, o.output );
+ }
+ }
+}
+
+void gb_apu_run_until_( int32_t end_time )
+{
+ int32_t time;
+
+ do{
+ /* run oscillators*/
+ time = end_time;
+ if ( time > gb_apu.frame_time )
+ time = gb_apu.frame_time;
+
+ gb_apu.square1.run( gb_apu.last_time, time );
+ gb_apu.square2.run( gb_apu.last_time, time );
+ gb_apu.wave .run( gb_apu.last_time, time );
+ gb_apu.noise .run( gb_apu.last_time, time );
+ gb_apu.last_time = time;
+
+ if ( time == end_time )
+ break;
+
+ /* run frame sequencer*/
+ gb_apu.frame_time += gb_apu.frame_period * CLK_MUL;
+ switch ( gb_apu.frame_phase++ )
+ {
+ case 2:
+ case 6:
+ /* 128 Hz*/
+ gb_apu.square1.clock_sweep();
+ case 0:
+ case 4:
+ /* 256 Hz*/
+ gb_apu.square1.clock_length();
+ gb_apu.square2.clock_length();
+ gb_apu.wave .clock_length();
+ gb_apu.noise .clock_length();
+ break;
+
+ case 7:
+ /* 64 Hz*/
+ gb_apu.frame_phase = 0;
+ gb_apu.square1.clock_envelope();
+ gb_apu.square2.clock_envelope();
+ gb_apu.noise .clock_envelope();
+ }
+ }while(1);
+}
+
+void gb_apu_write_osc( int index, int reg, int old_data, int data )
+{
+ reg -= index * 5;
+ switch ( index )
+ {
+ case 0:
+ gb_apu.square1.write_register( gb_apu.frame_phase, reg, old_data, data );
+ break;
+ case 1:
+ gb_apu.square2.write_register( gb_apu.frame_phase, reg, old_data, data );
+ break;
+ case 2:
+ gb_apu.wave.write_register( gb_apu.frame_phase, reg, old_data, data );
+ break;
+ case 3:
+ gb_apu.noise.write_register( gb_apu.frame_phase, reg, old_data, data );
+ break;
+ }
+}
+
+INLINE int gb_apu_calc_output( int osc )
+{
+ int bits = gb_apu.regs [STEREO_REG - START_ADDR] >> osc;
+ return (bits >> 3 & 2) | (bits & 1);
+}
+
+void gb_apu_write_register( int32_t time, unsigned addr, int data )
+{
+ int reg = addr - START_ADDR;
+ if ( (unsigned) reg >= REGISTER_COUNT )
+ return;
+
+ if ( addr < STATUS_REG && !(gb_apu.regs [STATUS_REG - START_ADDR] & POWER_MASK) )
+ return; /* Power is off*/
+
+ if ( time > gb_apu.last_time )
+ gb_apu_run_until_( time );
+
+ if ( addr >= WAVE_RAM )
+ {
+ gb_apu.wave.write( addr, data );
+ }
+ else
+ {
+ int old_data = gb_apu.regs [reg];
+ gb_apu.regs [reg] = data;
+
+ if ( addr < VOL_REG )
+ gb_apu_write_osc( reg / 5, reg, old_data, data ); /* Oscillator*/
+ else if ( addr == VOL_REG && data != old_data )
+ {
+ /* Master volume*/
+ for ( int i = OSC_COUNT; --i >= 0; )
+ gb_apu_silence_osc( *gb_apu.oscs [i] );
+
+ gb_apu_apply_volume();
+ }
+ else if ( addr == STEREO_REG )
+ {
+ /* Stereo panning*/
+ for ( int i = OSC_COUNT; --i >= 0; )
+ {
+ Gb_Osc& o = *gb_apu.oscs [i];
+ Blip_Buffer* out = o.outputs [gb_apu_calc_output( i )];
+ if ( o.output != out )
+ {
+ gb_apu_silence_osc( o );
+ o.output = out;
+ }
+ } }
+ else if ( addr == STATUS_REG && (data ^ old_data) & POWER_MASK )
+ {
+ /* Power control*/
+ gb_apu.frame_phase = 0;
+ for ( int i = OSC_COUNT; --i >= 0; )
+ gb_apu_silence_osc( *gb_apu.oscs [i] );
+
+ for ( int i = 0; i < 32; i++ )
+ gb_apu.regs [i] = 0;
+
+ gb_apu.square1.reset();
+ gb_apu.square2.reset();
+ gb_apu.wave .reset();
+ gb_apu.noise .reset();
+
+ gb_apu_apply_volume();
+
+ gb_apu.square1.length_ctr = 64;
+ gb_apu.square2.length_ctr = 64;
+ gb_apu.wave .length_ctr = 256;
+ gb_apu.noise .length_ctr = 64;
+
+ gb_apu.regs [STATUS_REG - START_ADDR] = data;
+ }
+ }
+}
+
+void gb_apu_reset( uint32_t mode, bool agb_wave )
+{
+ /* Hardware mode*/
+ mode = MODE_AGB; /* using AGB wave features implies AGB hardware*/
+ gb_apu.wave.agb_mask = 0xFF;
+ gb_apu.oscs [0]->mode = mode;
+ gb_apu.oscs [1]->mode = mode;
+ gb_apu.oscs [2]->mode = mode;
+ gb_apu.oscs [3]->mode = mode;
+ gb_apu_reduce_clicks( gb_apu.reduce_clicks_ );
+
+ /* Reset state*/
+ gb_apu.frame_time = 0;
+ gb_apu.last_time = 0;
+ gb_apu.frame_phase = 0;
+
+ for ( int i = 0; i < 32; i++ )
+ gb_apu.regs [i] = 0;
+
+ gb_apu.square1.reset();
+ gb_apu.square2.reset();
+ gb_apu.wave .reset();
+ gb_apu.noise .reset();
+
+ gb_apu_apply_volume();
+
+ gb_apu.square1.length_ctr = 64;
+ gb_apu.square2.length_ctr = 64;
+ gb_apu.wave .length_ctr = 256;
+ gb_apu.noise .length_ctr = 64;
+
+ /* Load initial wave RAM*/
+ static unsigned char const initial_wave [2] [16] = {
+ {0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA},
+ {0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF},
+ };
+ for ( int b = 2; --b >= 0; )
+ {
+ /* Init both banks (does nothing if not in AGB mode)*/
+ gb_apu_write_register( 0, 0xFF1A, b * 0x40 );
+ for ( unsigned i = 0; i < sizeof initial_wave [0]; i++ )
+ gb_apu_write_register( 0, i + WAVE_RAM, initial_wave [1] [i] );
+ }
+}
+
+void gb_apu_new(void)
+{
+ int i;
+
+ gb_apu.wave.wave_ram = &gb_apu.regs [WAVE_RAM - START_ADDR];
+
+ gb_apu.oscs [0] = &gb_apu.square1;
+ gb_apu.oscs [1] = &gb_apu.square2;
+ gb_apu.oscs [2] = &gb_apu.wave;
+ gb_apu.oscs [3] = &gb_apu.noise;
+
+ for ( i = OSC_COUNT; --i >= 0; )
+ {
+ Gb_Osc& o = *gb_apu.oscs [i];
+ o.regs = &gb_apu.regs [i * 5];
+ o.output = 0;
+ o.outputs [0] = 0;
+ o.outputs [1] = 0;
+ o.outputs [2] = 0;
+ o.outputs [3] = 0;
+ o.good_synth = &gb_apu.good_synth;
+ o.med_synth = &gb_apu.med_synth;
+ }
+
+ gb_apu.reduce_clicks_ = false;
+ gb_apu.frame_period = 4194304 / 512; /* 512 Hz*/
+
+ gb_apu.volume_ = 1.0;
+ gb_apu_reset(MODE_AGB, false);
+}
+
+
+
+void gb_apu_set_output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right, int osc )
+{
+ int i;
+
+ i = osc;
+ do
+ {
+ Gb_Osc& o = *gb_apu.oscs [i];
+ o.outputs [1] = right;
+ o.outputs [2] = left;
+ o.outputs [3] = center;
+ o.output = o.outputs [gb_apu_calc_output( i )];
+ ++i;
+ }
+ while ( i < osc );
+}
+
+void gb_apu_volume( double v )
+{
+ if ( gb_apu.volume_ != v )
+ {
+ gb_apu.volume_ = v;
+ gb_apu_apply_volume();
+ }
+}
+
+void gb_apu_apply_stereo (void)
+{
+ int i;
+
+ for ( i = OSC_COUNT; --i >= 0; )
+ {
+ Gb_Osc& o = *gb_apu.oscs [i];
+ Blip_Buffer* out = o.outputs [gb_apu_calc_output( i )];
+ if ( o.output != out )
+ {
+ gb_apu_silence_osc( o );
+ o.output = out;
+ }
+ }
+}
+
+
+/*============================================================
+ GB OSCS
+============================================================ */
+
+
+/*============================================================
+ BLIP BUFFER
+============================================================ */
+
+/* Blip_Buffer 0.4.1. http://www.slack.net/~ant */
+
+#define FIXED_SHIFT 12
+#define SAL_FIXED_SHIFT 4096
+#define TO_FIXED( f ) int ((f) * SAL_FIXED_SHIFT)
+#define FROM_FIXED( f ) ((f) >> FIXED_SHIFT)
+
+
+
+/*============================================================
+ STEREO BUFFER
+============================================================ */
+
+/* Uses three buffers (one for center) and outputs stereo sample pairs. */
+
+#define STEREO_BUFFER_SAMPLES_AVAILABLE() ((long)(bufs_buffer[0].offset_ - mixer_samples_read) << 1)
+#define stereo_buffer_samples_avail() ((((bufs_buffer [0].offset_ >> BLIP_BUFFER_ACCURACY) - mixer_samples_read) << 1))
+
+
+const char * stereo_buffer_set_sample_rate( long rate, int msec )
+{
+ mixer_samples_read = 0;
+ for ( int i = BUFS_SIZE; --i >= 0; )
+ RETURN_ERR( bufs_buffer [i].set_sample_rate( rate, msec ) );
+ return 0;
+}
+
+void stereo_buffer_clock_rate( long rate )
+{
+ bufs_buffer[2].factor_ = bufs_buffer [2].clock_rate_factor( rate );
+ bufs_buffer[1].factor_ = bufs_buffer [1].clock_rate_factor( rate );
+ bufs_buffer[0].factor_ = bufs_buffer [0].clock_rate_factor( rate );
+}
+
+void stereo_buffer_clear (void)
+{
+ mixer_samples_read = 0;
+ bufs_buffer [2].clear();
+ bufs_buffer [1].clear();
+ bufs_buffer [0].clear();
+}
+
+/* mixers use a single index value to improve performance on register-challenged processors
+ * offset goes from negative to zero*/
+
+INLINE void stereo_buffer_mixer_read_pairs( int16_t* out, int count )
+{
+ /* TODO: if caller never marks buffers as modified, uses mono*/
+ /* except that buffer isn't cleared, so caller can encounter*/
+ /* subtle problems and not realize the cause.*/
+ mixer_samples_read += count;
+ int16_t* outtemp = out + count * STEREO;
+
+ /* do left + center and right + center separately to reduce register load*/
+ Blip_Buffer* buf = &bufs_buffer [2];
+ {
+ --buf;
+ --outtemp;
+
+ BLIP_READER_BEGIN( side, *buf );
+ BLIP_READER_BEGIN( center, bufs_buffer[2] );
+
+ BLIP_READER_ADJ_( side, mixer_samples_read );
+ BLIP_READER_ADJ_( center, mixer_samples_read );
+
+ int offset = -count;
+ do
+ {
+ int s = (center_reader_accum + side_reader_accum) >> 14;
+ BLIP_READER_NEXT_IDX_( side, offset );
+ BLIP_READER_NEXT_IDX_( center, offset );
+ BLIP_CLAMP( s, s );
+
+ ++offset; /* before write since out is decremented to slightly before end*/
+ outtemp [offset * STEREO] = (int16_t) s;
+ }while ( offset );
+
+ BLIP_READER_END( side, *buf );
+ }
+ {
+ --buf;
+ --outtemp;
+
+ BLIP_READER_BEGIN( side, *buf );
+ BLIP_READER_BEGIN( center, bufs_buffer[2] );
+
+ BLIP_READER_ADJ_( side, mixer_samples_read );
+ BLIP_READER_ADJ_( center, mixer_samples_read );
+
+ int offset = -count;
+ do
+ {
+ int s = (center_reader_accum + side_reader_accum) >> 14;
+ BLIP_READER_NEXT_IDX_( side, offset );
+ BLIP_READER_NEXT_IDX_( center, offset );
+ BLIP_CLAMP( s, s );
+
+ ++offset; /* before write since out is decremented to slightly before end*/
+ outtemp [offset * STEREO] = (int16_t) s;
+ }while ( offset );
+
+ BLIP_READER_END( side, *buf );
+
+ /* only end center once*/
+ BLIP_READER_END( center, bufs_buffer[2] );
+ }
+}
+
+void blip_buffer_remove_all_samples( long count )
+{
+ uint32_t new_offset = (uint32_t)count << BLIP_BUFFER_ACCURACY;
+ /* BLIP BUFFER #1 */
+ bufs_buffer[0].offset_ -= new_offset;
+ bufs_buffer[1].offset_ -= new_offset;
+ bufs_buffer[2].offset_ -= new_offset;
+
+ /* copy remaining samples to beginning and clear old samples*/
+ long remain = (bufs_buffer[0].offset_ >> BLIP_BUFFER_ACCURACY) + BLIP_BUFFER_EXTRA_;
+ memmove( bufs_buffer[0].buffer_, bufs_buffer[0].buffer_ + count, remain * sizeof *bufs_buffer[0].buffer_ );
+ memset( bufs_buffer[0].buffer_ + remain, 0, count * sizeof(*bufs_buffer[0].buffer_));
+
+ remain = (bufs_buffer[1].offset_ >> BLIP_BUFFER_ACCURACY) + BLIP_BUFFER_EXTRA_;
+ memmove( bufs_buffer[1].buffer_, bufs_buffer[1].buffer_ + count, remain * sizeof *bufs_buffer[1].buffer_ );
+ memset( bufs_buffer[1].buffer_ + remain, 0, count * sizeof(*bufs_buffer[1].buffer_));
+
+ remain = (bufs_buffer[2].offset_ >> BLIP_BUFFER_ACCURACY) + BLIP_BUFFER_EXTRA_;
+ memmove( bufs_buffer[2].buffer_, bufs_buffer[2].buffer_ + count, remain * sizeof *bufs_buffer[2].buffer_ );
+ memset( bufs_buffer[2].buffer_ + remain, 0, count * sizeof(*bufs_buffer[2].buffer_));
+}
+
+long stereo_buffer_read_samples( int16_t * out, long out_size )
+{
+ int pair_count;
+
+ out_size = (STEREO_BUFFER_SAMPLES_AVAILABLE() < out_size) ? STEREO_BUFFER_SAMPLES_AVAILABLE() : out_size;
+
+ pair_count = int (out_size >> 1);
+ if ( pair_count )
+ {
+ stereo_buffer_mixer_read_pairs( out, pair_count );
+ blip_buffer_remove_all_samples( mixer_samples_read );
+ mixer_samples_read = 0;
+ }
+ return out_size;
+}
+
+void gba_to_gb_sound_parallel( int * __restrict addr, int * __restrict addr2 )
+{
+ uint32_t addr1_table = *addr - 0x60;
+ uint32_t addr2_table = *addr2 - 0x60;
+ *addr = table [addr1_table];
+ *addr2 = table [addr2_table];
+}
+
+void pcm_fifo_write_control( int data, int data2)
+{
+ pcm[0].enabled = (data & 0x0300) ? true : false;
+ pcm[0].timer = (data & 0x0400) ? 1 : 0;
+
+ if ( data & 0x0800 )
+ {
+ // Reset
+ pcm[0].writeIndex = 0;
+ pcm[0].readIndex = 0;
+ pcm[0].count = 0;
+ pcm[0].dac = 0;
+ memset(pcm[0].fifo, 0, sizeof(pcm[0].fifo));
+ }
+
+ gba_pcm_apply_control( 0, pcm[0].which );
+
+ if(pcm[0].pcm.output)
+ {
+ int time = SOUND_CLOCK_TICKS - soundTicks;
+
+ pcm[0].dac = (int8_t)pcm[0].dac >> pcm[0].pcm.shift;
+ int delta = pcm[0].dac - pcm[0].pcm.last_amp;
+ if ( delta )
+ {
+ pcm[0].pcm.last_amp = pcm[0].dac;
+ pcm_synth.offset( time, delta, pcm[0].pcm.output );
+ }
+ pcm[0].pcm.last_time = time;
+ }
+
+ pcm[1].enabled = (data2 & 0x0300) ? true : false;
+ pcm[1].timer = (data2 & 0x0400) ? 1 : 0;
+
+ if ( data2 & 0x0800 )
+ {
+ // Reset
+ pcm[1].writeIndex = 0;
+ pcm[1].readIndex = 0;
+ pcm[1].count = 0;
+ pcm[1].dac = 0;
+ memset( pcm[1].fifo, 0, sizeof(pcm[1].fifo));
+ }
+
+ gba_pcm_apply_control( 1, pcm[1].which );
+
+ if(pcm[1].pcm.output)
+ {
+ int time = SOUND_CLOCK_TICKS - soundTicks;
+
+ pcm[1].dac = (int8_t)pcm[1].dac >> pcm[1].pcm.shift;
+ int delta = pcm[1].dac - pcm[1].pcm.last_amp;
+ if ( delta )
+ {
+ pcm[1].pcm.last_amp = pcm[1].dac;
+ pcm_synth.offset( time, delta, pcm[1].pcm.output );
+ }
+ pcm[1].pcm.last_time = time;
+ }
+}
+
+void soundEvent_u16_parallel(uint32_t address[])
+{
+ for(int i = 0; i < 8; i++)
+ {
+ switch ( address[i] )
+ {
+ case SGCNT0_H:
+ //Begin of Write SGCNT0_H
+ WRITE16LE( &ioMem [SGCNT0_H], 0 & 0x770F );
+ pcm_fifo_write_control(0, 0);
+
+ gb_apu_volume( apu_vols [ioMem [SGCNT0_H] & 3] );
+ //End of SGCNT0_H
+ break;
+
+ case FIFOA_L:
+ case FIFOA_H:
+ pcm[0].fifo [pcm[0].writeIndex ] = 0;
+ pcm[0].fifo [pcm[0].writeIndex+1] = 0;
+ pcm[0].count += 2;
+ pcm[0].writeIndex = (pcm[0].writeIndex + 2) & 31;
+ WRITE16LE( &ioMem[address[i]], 0 );
+ break;
+
+ case FIFOB_L:
+ case FIFOB_H:
+ pcm[1].fifo [pcm[1].writeIndex ] = 0;
+ pcm[1].fifo [pcm[1].writeIndex+1] = 0;
+ pcm[1].count += 2;
+ pcm[1].writeIndex = (pcm[1].writeIndex + 2) & 31;
+ WRITE16LE( &ioMem[address[i]], 0 );
+ break;
+
+ case 0x88:
+ WRITE16LE( &ioMem[address[i]], 0 );
+ break;
+
+ default:
+ {
+ int gb_addr[2] = {address[i] & ~1, address[i] | 1};
+ uint32_t address_array[2] = {address[i] & ~ 1, address[i] | 1};
+ uint8_t data_array[2] = {0};
+ gba_to_gb_sound_parallel(&gb_addr[0], &gb_addr[1]);
+ soundEvent_u8_parallel(gb_addr, address_array, data_array);
+ break;
+ }
+ }
+ }
+}
+
+void gba_pcm_fifo_timer_overflowed( unsigned pcm_idx )
+{
+ if ( pcm[pcm_idx].count <= 16 )
+ {
+ // Need to fill FIFO
+ CPUCheckDMA( 3, pcm[pcm_idx].which ? 4 : 2 );
+
+ if ( pcm[pcm_idx].count <= 16 )
+ {
+ // Not filled by DMA, so fill with 16 bytes of silence
+ int reg = pcm[pcm_idx].which ? FIFOB_L : FIFOA_L;
+
+ uint32_t address_array[8] = {reg, reg+2, reg, reg+2, reg, reg+2, reg, reg+2};
+ soundEvent_u16_parallel(address_array);
+ }
+ }
+
+ // Read next sample from FIFO
+ pcm[pcm_idx].count--;
+ pcm[pcm_idx].dac = pcm[pcm_idx].fifo [pcm[pcm_idx].readIndex];
+ pcm[pcm_idx].readIndex = (pcm[pcm_idx].readIndex + 1) & 31;
+
+ if(pcm[pcm_idx].pcm.output)
+ {
+ int time = SOUND_CLOCK_TICKS - soundTicks;
+
+ pcm[pcm_idx].dac = (int8_t)pcm[pcm_idx].dac >> pcm[pcm_idx].pcm.shift;
+ int delta = pcm[pcm_idx].dac - pcm[pcm_idx].pcm.last_amp;
+ if ( delta )
+ {
+ pcm[pcm_idx].pcm.last_amp = pcm[pcm_idx].dac;
+ pcm_synth.offset( time, delta, pcm[pcm_idx].pcm.output );
+ }
+ pcm[pcm_idx].pcm.last_time = time;
+ }
+}
+
+void soundEvent_u8_parallel(int gb_addr[], uint32_t address[], uint8_t data[])
+{
+ for(uint32_t i = 0; i < 2; i++)
+ {
+ ioMem[address[i]] = data[i];
+ gb_apu_write_register( SOUND_CLOCK_TICKS - soundTicks, gb_addr[i], data[i] );
+
+ if ( address[i] == NR52 )
+ {
+ gba_pcm_apply_control(0, 0 );
+ gba_pcm_apply_control(1, 1 );
+ }
+ // TODO: what about byte writes to SGCNT0_H etc.?
+ }
+}
+
+void soundEvent_u8(int gb_addr, uint32_t address, uint8_t data)
+{
+ ioMem[address] = data;
+ gb_apu_write_register( SOUND_CLOCK_TICKS - soundTicks, gb_addr, data );
+
+ if ( address == NR52 )
+ {
+ gba_pcm_apply_control(0, 0 );
+ gba_pcm_apply_control(1, 1 );
+ }
+ // TODO: what about byte writes to SGCNT0_H etc.?
+}
+
+
+void soundEvent_u16(uint32_t address, uint16_t data)
+{
+ switch ( address )
+ {
+ case SGCNT0_H:
+ //Begin of Write SGCNT0_H
+ WRITE16LE( &ioMem [SGCNT0_H], data & 0x770F );
+ pcm_fifo_write_control( data, data >> 4);
+
+ gb_apu_volume( apu_vols [ioMem [SGCNT0_H] & 3] );
+ //End of SGCNT0_H
+ break;
+
+ case FIFOA_L:
+ case FIFOA_H:
+ pcm[0].fifo [pcm[0].writeIndex ] = data & 0xFF;
+ pcm[0].fifo [pcm[0].writeIndex+1] = data >> 8;
+ pcm[0].count += 2;
+ pcm[0].writeIndex = (pcm[0].writeIndex + 2) & 31;
+ WRITE16LE( &ioMem[address], data );
+ break;
+
+ case FIFOB_L:
+ case FIFOB_H:
+ pcm[1].fifo [pcm[1].writeIndex ] = data & 0xFF;
+ pcm[1].fifo [pcm[1].writeIndex+1] = data >> 8;
+ pcm[1].count += 2;
+ pcm[1].writeIndex = (pcm[1].writeIndex + 2) & 31;
+ WRITE16LE( &ioMem[address], data );
+ break;
+
+ case 0x88:
+ data &= 0xC3FF;
+ WRITE16LE( &ioMem[address], data );
+ break;
+
+ default:
+ {
+ int gb_addr[2] = {address & ~1, address | 1};
+ uint32_t address_array[2] = {address & ~ 1, address | 1};
+ uint8_t data_array[2] = {(uint8_t)data, (uint8_t)(data >> 8)};
+ gba_to_gb_sound_parallel(&gb_addr[0], &gb_addr[1]);
+ soundEvent_u8_parallel(gb_addr, address_array, data_array);
+ break;
+ }
+ }
+}
+
+void soundTimerOverflow(int timer)
+{
+ if ( timer == pcm[0].timer && pcm[0].enabled )
+ gba_pcm_fifo_timer_overflowed(0);
+ if ( timer == pcm[1].timer && pcm[1].enabled )
+ gba_pcm_fifo_timer_overflowed(1);
+}
+
+void process_sound_tick_fn (void)
+{
+ // Run sound hardware to present
+ pcm[0].pcm.last_time -= SOUND_CLOCK_TICKS;
+ if ( pcm[0].pcm.last_time < -2048 )
+ pcm[0].pcm.last_time = -2048;
+
+ pcm[1].pcm.last_time -= SOUND_CLOCK_TICKS;
+ if ( pcm[1].pcm.last_time < -2048 )
+ pcm[1].pcm.last_time = -2048;
+
+ /* Emulates sound hardware up to a specified time, ends current time
+ frame, then starts a new frame at time 0 */
+
+ if(SOUND_CLOCK_TICKS > gb_apu.last_time)
+ gb_apu_run_until_( SOUND_CLOCK_TICKS );
+
+ gb_apu.frame_time -= SOUND_CLOCK_TICKS;
+ gb_apu.last_time -= SOUND_CLOCK_TICKS;
+
+ bufs_buffer[2].offset_ += SOUND_CLOCK_TICKS * bufs_buffer[2].factor_;
+ bufs_buffer[1].offset_ += SOUND_CLOCK_TICKS * bufs_buffer[1].factor_;
+ bufs_buffer[0].offset_ += SOUND_CLOCK_TICKS * bufs_buffer[0].factor_;
+
+
+ // dump all the samples available
+ // VBA will only ever store 1 frame worth of samples
+ int numSamples = stereo_buffer_read_samples( (int16_t*) soundFinalWave, stereo_buffer_samples_avail());
+ systemOnWriteDataToSoundBuffer(soundFinalWave, numSamples);
+}
+
+void apply_muting (void)
+{
+ // PCM
+ gba_pcm_apply_control(1, 0 );
+ gba_pcm_apply_control(1, 1 );
+
+ // APU
+ gb_apu_set_output( &bufs_buffer[2], &bufs_buffer[0], &bufs_buffer[1], 0 );
+ gb_apu_set_output( &bufs_buffer[2], &bufs_buffer[0], &bufs_buffer[1], 1 );
+ gb_apu_set_output( &bufs_buffer[2], &bufs_buffer[0], &bufs_buffer[1], 2 );
+ gb_apu_set_output( &bufs_buffer[2], &bufs_buffer[0], &bufs_buffer[1], 3 );
+}
+
+
+void remake_stereo_buffer (void)
+{
+ if ( !ioMem )
+ return;
+
+ // Clears pointers kept to old stereo_buffer
+ gba_pcm_init();
+
+ // Stereo_Buffer
+
+ mixer_samples_read = 0;
+ stereo_buffer_set_sample_rate( soundSampleRate, BLIP_DEFAULT_LENGTH );
+ stereo_buffer_clock_rate( CLOCK_RATE );
+
+ // PCM
+ pcm [0].which = 0;
+ pcm [1].which = 1;
+
+ // APU
+ gb_apu_new();
+ gb_apu_reset( MODE_AGB, true );
+
+ stereo_buffer_clear();
+
+ soundTicks = SOUND_CLOCK_TICKS;
+
+ apply_muting();
+
+ gb_apu_volume(apu_vols [ioMem [SGCNT0_H] & 3] );
+
+ pcm_synth.volume( 0.66 / 256 * SOUNDVOLUME_ );
+}
+
+void soundReset (void)
+{
+ remake_stereo_buffer();
+ //Begin of Reset APU
+ gb_apu_reset( MODE_AGB, true );
+
+ stereo_buffer_clear();
+
+ soundTicks = SOUND_CLOCK_TICKS;
+ //End of Reset APU
+
+ SOUND_CLOCK_TICKS = SOUND_CLOCK_TICKS_;
+ soundTicks = SOUND_CLOCK_TICKS_;
+
+ // Sound Event (NR52)
+ int gb_addr = table[NR52 - 0x60];
+ if ( gb_addr )
+ {
+ ioMem[NR52] = 0x80;
+ gb_apu_write_register( SOUND_CLOCK_TICKS - soundTicks, gb_addr, 0x80 );
+
+ gba_pcm_apply_control(0, 0 );
+ gba_pcm_apply_control(1, 1 );
+ }
+
+ // TODO: what about byte writes to SGCNT0_H etc.?
+ // End of Sound Event (NR52)
+}
+/*
+void soundSetSampleRate(long sampleRate)
+{
+ if ( soundSampleRate != sampleRate )
+ {
+ soundSampleRate = sampleRate;
+ remake_stereo_buffer();
+ }
+}
+*/
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// END SOUND.CPP
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// BEGIN GBA.CPP
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*============================================================
+ GBA INLINE
+============================================================ */
+
+#define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value);
+#define ARM_PREFETCH_NEXT cpuPrefetch[1] = CPUReadMemoryQuick(bus.armNextPC+4);
+#define THUMB_PREFETCH_NEXT cpuPrefetch[1] = CPUReadHalfWordQuick(bus.armNextPC+2);
+
+#define ARM_PREFETCH \
+ {\
+ cpuPrefetch[0] = CPUReadMemoryQuick(bus.armNextPC);\
+ cpuPrefetch[1] = CPUReadMemoryQuick(bus.armNextPC+4);\
+ }
+
+#define THUMB_PREFETCH \
+ {\
+ cpuPrefetch[0] = CPUReadHalfWordQuick(bus.armNextPC);\
+ cpuPrefetch[1] = CPUReadHalfWordQuick(bus.armNextPC+2);\
+ }
+
+#ifdef USE_SWITICKS
+extern int SWITicks;
+#endif
+int cpuNextEvent; // = 0;
+bool holdState; // = false;
+uint32_t cpuPrefetch[2];
+int cpuTotalTicks; // = 0;
+uint8_t memoryWait[16];
+uint8_t memoryWaitSeq[16];
+uint8_t memoryWait32[16];
+uint8_t memoryWaitSeq32[16];
+
+uint8_t biosProtected[4];
+uint8_t cpuBitsSet[256];
+
+bool N_FLAG; // = 0;
+bool C_FLAG; // = 0;
+bool Z_FLAG; // = 0;
+bool V_FLAG; // = 0;
+bool armState; // = true;
+bool armIrqEnable; // = true;
+int armMode; // = 0x1f;
+
+typedef enum
+{
+ REG_DISPCNT = 0x000,
+ REG_DISPSTAT = 0x002,
+ REG_VCOUNT = 0x003,
+ REG_BG0CNT = 0x004,
+ REG_BG1CNT = 0x005,
+ REG_BG2CNT = 0x006,
+ REG_BG3CNT = 0x007,
+ REG_BG0HOFS = 0x08,
+ REG_BG0VOFS = 0x09,
+ REG_BG1HOFS = 0x0A,
+ REG_BG1VOFS = 0x0B,
+ REG_BG2HOFS = 0x0C,
+ REG_BG2VOFS = 0x0D,
+ REG_BG3HOFS = 0x0E,
+ REG_BG3VOFS = 0x0F,
+ REG_BG2PA = 0x10,
+ REG_BG2PB = 0x11,
+ REG_BG2PC = 0x12,
+ REG_BG2PD = 0x13,
+ REG_BG2X_L = 0x14,
+ REG_BG2X_H = 0x15,
+ REG_BG2Y_L = 0x16,
+ REG_BG2Y_H = 0x17,
+ REG_BG3PA = 0x18,
+ REG_BG3PB = 0x19,
+ REG_BG3PC = 0x1A,
+ REG_BG3PD = 0x1B,
+ REG_BG3X_L = 0x1C,
+ REG_BG3X_H = 0x1D,
+ REG_BG3Y_L = 0x1E,
+ REG_BG3Y_H = 0x1F,
+ REG_WIN0H = 0x20,
+ REG_WIN1H = 0x21,
+ REG_WIN0V = 0x22,
+ REG_WIN1V = 0x23,
+ REG_WININ = 0x24,
+ REG_WINOUT = 0x25,
+ REG_BLDCNT = 0x28,
+ REG_BLDALPHA = 0x29,
+ REG_BLDY = 0x2A,
+ REG_TM0D = 0x80,
+ REG_TM0CNT = 0x81,
+ REG_TM1D = 0x82,
+ REG_TM1CNT = 0x83,
+ REG_TM2D = 0x84,
+ REG_TM2CNT = 0x85,
+ REG_TM3D = 0x86,
+ REG_TM3CNT = 0x87,
+ REG_P1 = 0x098,
+ REG_P1CNT = 0x099,
+ REG_RCNT = 0x9A,
+ REG_IE = 0x100,
+ REG_IF = 0x101,
+ REG_IME = 0x104,
+ REG_HALTCNT = 0x180
+} hardware_register;
+
+uint16_t io_registers[1024 * 16];
+
+u16 MOSAIC;
+
+uint16_t BG2X_L ;
+uint16_t BG2X_H ;
+uint16_t BG2Y_L ;
+uint16_t BG2Y_H ;
+uint16_t BG3X_L ;
+uint16_t BG3X_H ;
+uint16_t BG3Y_L ;
+uint16_t BG3Y_H ;
+uint16_t BLDMOD ;
+uint16_t COLEV ;
+uint16_t COLY ;
+uint16_t DM0SAD_L ;
+uint16_t DM0SAD_H ;
+uint16_t DM0DAD_L ;
+uint16_t DM0DAD_H ;
+uint16_t DM0CNT_L ;
+uint16_t DM0CNT_H ;
+uint16_t DM1SAD_L ;
+uint16_t DM1SAD_H ;
+uint16_t DM1DAD_L ;
+uint16_t DM1DAD_H ;
+uint16_t DM1CNT_L ;
+uint16_t DM1CNT_H ;
+uint16_t DM2SAD_L ;
+uint16_t DM2SAD_H ;
+uint16_t DM2DAD_L ;
+uint16_t DM2DAD_H ;
+uint16_t DM2CNT_L ;
+uint16_t DM2CNT_H ;
+uint16_t DM3SAD_L ;
+uint16_t DM3SAD_H ;
+uint16_t DM3DAD_L ;
+uint16_t DM3DAD_H ;
+uint16_t DM3CNT_L ;
+uint16_t DM3CNT_H ;
+
+uint8_t timerOnOffDelay ;
+uint16_t timer0Value ;
+uint32_t dma0Source ;
+uint32_t dma0Dest ;
+uint32_t dma1Source ;
+uint32_t dma1Dest ;
+uint32_t dma2Source ;
+uint32_t dma2Dest ;
+uint32_t dma3Source ;
+uint32_t dma3Dest ;
+void (Gigazoid::*cpuSaveGameFunc)(uint32_t,uint8_t);
+bool fxOn ;
+bool windowOn ;
+
+int cpuDmaTicksToUpdate;
+
+int IRQTicks;
+bool intState;
+
+bus_t bus;
+graphics_t graphics;
+
+memoryMap map[256];
+int clockTicks;
+
+int romSize; // = 0x2000000;
+uint32_t line[6][240];
+bool gfxInWin[2][240];
+int lineOBJpixleft[128];
+int joy;
+
+int gfxBG2Changed;
+int gfxBG3Changed;
+
+int gfxBG2X;
+int gfxBG2Y;
+int gfxBG3X;
+int gfxBG3Y;
+
+bool ioReadable[0x400];
+int gbaSaveType; // used to remember the save type on reset
+
+//int gfxLastVCOUNT = 0;
+
+// Waitstates when accessing data
+
+#define DATATICKS_ACCESS_BUS_PREFETCH(address, value) \
+ int addr = (address >> 24) & 15; \
+ if ((addr>=0x08) || (addr < 0x02)) \
+ { \
+ bus.busPrefetchCount=0; \
+ bus.busPrefetch=false; \
+ } \
+ else if (bus.busPrefetch) \
+ { \
+ int waitState = value; \
+ waitState = (1 & ~waitState) | (waitState & waitState); \
+ bus.busPrefetchCount = ((bus.busPrefetchCount+1)<> 24) & 15])
+#define DATATICKS_ACCESS_32BIT_SEQ(address) (memoryWaitSeq32[(address >> 24) & 15])
+#define DATATICKS_ACCESS_16BIT(address) (memoryWait[(address >> 24) & 15])
+#define DATATICKS_ACCESS_16BIT_SEQ(address) (memoryWaitSeq[(address >> 24) & 15])
+
+// Waitstates when executing opcode
+INLINE int codeTicksAccess(u32 address, u8 bit32) // THUMB NON SEQ
+{
+ int addr, ret;
+
+ addr = (address>>24) & 15;
+
+ if (unsigned(addr - 0x08) <= 5)
+ {
+ if (bus.busPrefetchCount&0x1)
+ {
+ if (bus.busPrefetchCount&0x2)
+ {
+ bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>2) | (bus.busPrefetchCount&0xFFFFFF00);
+ return 0;
+ }
+ bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>1) | (bus.busPrefetchCount&0xFFFFFF00);
+ return memoryWaitSeq[addr]-1;
+ }
+ }
+ bus.busPrefetchCount = 0;
+
+ if(bit32) /* ARM NON SEQ */
+ ret = memoryWait32[addr];
+ else /* THUMB NON SEQ */
+ ret = memoryWait[addr];
+
+ return ret;
+}
+
+INLINE int codeTicksAccessSeq16(u32 address) // THUMB SEQ
+{
+ int addr = (address>>24) & 15;
+
+ if (unsigned(addr - 0x08) <= 5)
+ {
+ if (bus.busPrefetchCount&0x1)
+ {
+ bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>1) | (bus.busPrefetchCount&0xFFFFFF00);
+ return 0;
+ }
+ else if (bus.busPrefetchCount>0xFF)
+ {
+ bus.busPrefetchCount=0;
+ return memoryWait[addr];
+ }
+ }
+ else
+ bus.busPrefetchCount = 0;
+
+ return memoryWaitSeq[addr];
+}
+
+INLINE int codeTicksAccessSeq32(u32 address) // ARM SEQ
+{
+ int addr = (address>>24)&15;
+
+ if (unsigned(addr - 0x08) <= 5)
+ {
+ if (bus.busPrefetchCount&0x1)
+ {
+ if (bus.busPrefetchCount&0x2)
+ {
+ bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>2) | (bus.busPrefetchCount&0xFFFFFF00);
+ return 0;
+ }
+ bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>1) | (bus.busPrefetchCount&0xFFFFFF00);
+ return memoryWaitSeq[addr];
+ }
+ else if (bus.busPrefetchCount > 0xFF)
+ {
+ bus.busPrefetchCount=0;
+ return memoryWait32[addr];
+ }
+ }
+ return memoryWaitSeq32[addr];
+}
+
+#define CPUReadByteQuick(addr) map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]
+#define CPUReadHalfWordQuick(addr) READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]))
+#define CPUReadMemoryQuick(addr) READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]))
+
+bool stopState;
+#ifdef USE_MOTION_SENSOR
+extern bool cpuEEPROMSensorEnabled;
+#endif
+bool timer0On ;
+int timer0Ticks ;
+int timer0Reload ;
+int timer0ClockReload ;
+uint16_t timer1Value ;
+bool timer1On ;
+int timer1Ticks ;
+int timer1Reload ;
+int timer1ClockReload ;
+uint16_t timer2Value ;
+bool timer2On ;
+int timer2Ticks ;
+int timer2Reload ;
+int timer2ClockReload ;
+uint16_t timer3Value ;
+bool timer3On ;
+int timer3Ticks ;
+int timer3Reload ;
+int timer3ClockReload ;
+
+INLINE u32 CPUReadMemory(u32 address)
+{
+ u32 value;
+ switch(address >> 24)
+ {
+ case 0:
+ /* BIOS */
+ if(bus.reg[15].I >> 24)
+ {
+ if(address < 0x4000)
+ value = READ32LE(((u32 *)&biosProtected));
+ else goto unreadable;
+ }
+ else
+ value = READ32LE(((u32 *)&bios[address & 0x3FFC]));
+ break;
+ case 0x02:
+ /* external work RAM */
+ value = READ32LE(((u32 *)&workRAM[address & 0x3FFFC]));
+ break;
+ case 0x03:
+ /* internal work RAM */
+ value = READ32LE(((u32 *)&internalRAM[address & 0x7ffC]));
+ break;
+ case 0x04:
+ /* I/O registers */
+ if((address < 0x4000400) && ioReadable[address & 0x3fc])
+ {
+ if(ioReadable[(address & 0x3fc) + 2])
+ value = READ32LE(((u32 *)&ioMem[address & 0x3fC]));
+ else
+ value = READ16LE(((u16 *)&ioMem[address & 0x3fc]));
+ }
+ else
+ goto unreadable;
+ break;
+ case 0x05:
+ /* palette RAM */
+ value = READ32LE(((u32 *)&graphics.paletteRAM[address & 0x3fC]));
+ break;
+ case 0x06:
+ /* VRAM */
+ address = (address & 0x1fffc);
+ if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000))
+ {
+ value = 0;
+ break;
+ }
+ if ((address & 0x18000) == 0x18000)
+ address &= 0x17fff;
+ value = READ32LE(((u32 *)&vram[address]));
+ break;
+ case 0x07:
+ /* OAM RAM */
+ value = READ32LE(((u32 *)&oam[address & 0x3FC]));
+ break;
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ /* gamepak ROM */
+ value = READ32LE(((u32 *)&rom[address&0x1FFFFFC]));
+ break;
+ case 0x0D:
+ value = eepromRead();
+ break;
+ case 14:
+ case 15:
+ value = flashRead(address) * 0x01010101;
+ break;
+ default:
+unreadable:
+ if(armState)
+ value = CPUReadHalfWordQuick(bus.reg[15].I + (address & 2));
+ else
+ value = CPUReadHalfWordQuick(bus.reg[15].I);
+ }
+
+ if(address & 3) {
+ int shift = (address & 3) << 3;
+ value = (value >> shift) | (value << (32 - shift));
+ }
+ return value;
+}
+
+INLINE u32 CPUReadHalfWord(u32 address)
+{
+ u32 value;
+
+ switch(address >> 24)
+ {
+ case 0:
+ if (bus.reg[15].I >> 24)
+ {
+ if(address < 0x4000)
+ value = READ16LE(((u16 *)&biosProtected[address&2]));
+ else
+ goto unreadable;
+ }
+ else
+ value = READ16LE(((u16 *)&bios[address & 0x3FFE]));
+ break;
+ case 2:
+ value = READ16LE(((u16 *)&workRAM[address & 0x3FFFE]));
+ break;
+ case 3:
+ value = READ16LE(((u16 *)&internalRAM[address & 0x7ffe]));
+ break;
+ case 4:
+ if((address < 0x4000400) && ioReadable[address & 0x3fe])
+ {
+ value = READ16LE(((u16 *)&ioMem[address & 0x3fe]));
+ if (((address & 0x3fe)>0xFF) && ((address & 0x3fe)<0x10E))
+ {
+ if (((address & 0x3fe) == 0x100) && timer0On)
+ value = 0xFFFF - ((timer0Ticks-cpuTotalTicks) >> timer0ClockReload);
+ else
+ if (((address & 0x3fe) == 0x104) && timer1On && !(io_registers[REG_TM1CNT] & 4))
+ value = 0xFFFF - ((timer1Ticks-cpuTotalTicks) >> timer1ClockReload);
+ else
+ if (((address & 0x3fe) == 0x108) && timer2On && !(io_registers[REG_TM2CNT] & 4))
+ value = 0xFFFF - ((timer2Ticks-cpuTotalTicks) >> timer2ClockReload);
+ else
+ if (((address & 0x3fe) == 0x10C) && timer3On && !(io_registers[REG_TM3CNT] & 4))
+ value = 0xFFFF - ((timer3Ticks-cpuTotalTicks) >> timer3ClockReload);
+ }
+ }
+ else goto unreadable;
+ break;
+ case 5:
+ value = READ16LE(((u16 *)&graphics.paletteRAM[address & 0x3fe]));
+ break;
+ case 6:
+ address = (address & 0x1fffe);
+ if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000))
+ {
+ value = 0;
+ break;
+ }
+ if ((address & 0x18000) == 0x18000)
+ address &= 0x17fff;
+ value = READ16LE(((u16 *)&vram[address]));
+ break;
+ case 7:
+ value = READ16LE(((u16 *)&oam[address & 0x3fe]));
+ break;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8)
+ value = rtcRead(address);
+ else
+ value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE]));
+ break;
+ case 13:
+ value = eepromRead();
+ break;
+ case 14:
+ value = flashRead(address) * 0x0101;
+ break;
+ default:
+unreadable:
+ {
+ int param = bus.reg[15].I;
+ if(armState)
+ param += (address & 2);
+ value = CPUReadHalfWordQuick(param);
+ }
+ break;
+ }
+
+ if(address & 1)
+ value = (value >> 8) | (value << 24);
+
+ return value;
+}
+
+INLINE u16 CPUReadHalfWordSigned(u32 address)
+{
+ u16 value = CPUReadHalfWord(address);
+ if((address & 1))
+ value = (s8)value;
+ return value;
+}
+
+INLINE u8 CPUReadByte(u32 address)
+{
+ switch(address >> 24)
+ {
+ case 0:
+ if (bus.reg[15].I >> 24)
+ {
+ if(address < 0x4000)
+ return biosProtected[address & 3];
+ else
+ goto unreadable;
+ }
+ return bios[address & 0x3FFF];
+ case 2:
+ return workRAM[address & 0x3FFFF];
+ case 3:
+ return internalRAM[address & 0x7fff];
+ case 4:
+ if((address < 0x4000400) && ioReadable[address & 0x3ff])
+ return ioMem[address & 0x3ff];
+ else goto unreadable;
+ case 5:
+ return graphics.paletteRAM[address & 0x3ff];
+ case 6:
+ address = (address & 0x1ffff);
+ if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000))
+ return 0;
+ if ((address & 0x18000) == 0x18000)
+ address &= 0x17fff;
+ return vram[address];
+ case 7:
+ return oam[address & 0x3ff];
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ return rom[address & 0x1FFFFFF];
+ case 13:
+ return eepromRead();
+ case 14:
+#ifdef USE_MOTION_SENSOR
+ if(cpuEEPROMSensorEnabled)
+ {
+ switch(address & 0x00008f00)
+ {
+ case 0x8200:
+ return systemGetSensorX() & 255;
+ case 0x8300:
+ return (systemGetSensorX() >> 8)|0x80;
+ case 0x8400:
+ return systemGetSensorY() & 255;
+ case 0x8500:
+ return systemGetSensorY() >> 8;
+ }
+ }
+#endif
+ return flashRead(address);
+ default:
+unreadable:
+ if(armState)
+ return CPUReadByteQuick(bus.reg[15].I+(address & 3));
+ else
+ return CPUReadByteQuick(bus.reg[15].I+(address & 1));
+ }
+}
+
+INLINE void CPUWriteMemory(u32 address, u32 value)
+{
+ switch(address >> 24)
+ {
+ case 0x02:
+ WRITE32LE(((u32 *)&workRAM[address & 0x3FFFC]), value);
+ break;
+ case 0x03:
+ WRITE32LE(((u32 *)&internalRAM[address & 0x7ffC]), value);
+ break;
+ case 0x04:
+ if(address < 0x4000400)
+ {
+ CPUUpdateRegister((address & 0x3FC), value & 0xFFFF);
+ CPUUpdateRegister((address & 0x3FC) + 2, (value >> 16));
+ }
+ break;
+ case 0x05:
+ WRITE32LE(((u32 *)&graphics.paletteRAM[address & 0x3FC]), value);
+ break;
+ case 0x06:
+ address = (address & 0x1fffc);
+ if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000))
+ return;
+ if ((address & 0x18000) == 0x18000)
+ address &= 0x17fff;
+
+
+ WRITE32LE(((u32 *)&vram[address]), value);
+ break;
+ case 0x07:
+ WRITE32LE(((u32 *)&oam[address & 0x3fc]), value);
+ break;
+ case 0x0D:
+ if(cpuEEPROMEnabled) {
+ eepromWrite(value);
+ break;
+ }
+ break;
+ case 0x0E:
+ if((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled)
+ (this->*cpuSaveGameFunc)(address, (u8)value);
+ break;
+ default:
+ break;
+ }
+}
+
+INLINE void CPUWriteHalfWord(u32 address, u16 value)
+{
+ switch(address >> 24)
+ {
+ case 2:
+ WRITE16LE(((u16 *)&workRAM[address & 0x3FFFE]),value);
+ break;
+ case 3:
+ WRITE16LE(((u16 *)&internalRAM[address & 0x7ffe]), value);
+ break;
+ case 4:
+ if(address < 0x4000400)
+ CPUUpdateRegister(address & 0x3fe, value);
+ break;
+ case 5:
+ WRITE16LE(((u16 *)&graphics.paletteRAM[address & 0x3fe]), value);
+ break;
+ case 6:
+ address = (address & 0x1fffe);
+ if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000))
+ return;
+ if ((address & 0x18000) == 0x18000)
+ address &= 0x17fff;
+ WRITE16LE(((u16 *)&vram[address]), value);
+ break;
+ case 7:
+ WRITE16LE(((u16 *)&oam[address & 0x3fe]), value);
+ break;
+ case 8:
+ case 9:
+ if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8)
+ if(!rtcWrite(address, value))
+ break;
+ break;
+ case 13:
+ if(cpuEEPROMEnabled)
+ eepromWrite((u8)value);
+ break;
+ case 14:
+ if((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled)
+ (this->*cpuSaveGameFunc)(address, (u8)value);
+ break;
+ default:
+ break;
+ }
+}
+
+INLINE void CPUWriteByte(u32 address, u8 b)
+{
+ switch(address >> 24)
+ {
+ case 2:
+ workRAM[address & 0x3FFFF] = b;
+ break;
+ case 3:
+ internalRAM[address & 0x7fff] = b;
+ break;
+ case 4:
+ if(address < 0x4000400)
+ {
+ switch(address & 0x3FF)
+ {
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x68:
+ case 0x69:
+ case 0x6c:
+ case 0x6d:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x78:
+ case 0x79:
+ case 0x7c:
+ case 0x7d:
+ case 0x80:
+ case 0x81:
+ case 0x84:
+ case 0x85:
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97:
+ case 0x98:
+ case 0x99:
+ case 0x9a:
+ case 0x9b:
+ case 0x9c:
+ case 0x9d:
+ case 0x9e:
+ case 0x9f:
+ {
+ int gb_addr = table[(address & 0xFF) - 0x60];
+ soundEvent_u8(gb_addr, address&0xFF, b);
+ }
+ break;
+ case 0x301: // HALTCNT, undocumented
+ if(b == 0x80)
+ stopState = true;
+ holdState = 1;
+ cpuNextEvent = cpuTotalTicks;
+ break;
+ default: // every other register
+ {
+ u32 lowerBits = address & 0x3fe;
+ uint16_t param;
+ if(address & 1)
+ param = (READ16LE(&ioMem[lowerBits]) & 0x00FF) | (b << 8);
+ else
+ param = (READ16LE(&ioMem[lowerBits]) & 0xFF00) | b;
+
+ CPUUpdateRegister(lowerBits, param);
+ }
+ break;
+ }
+ }
+ break;
+ case 5:
+ // no need to switch
+ *((u16 *)&graphics.paletteRAM[address & 0x3FE]) = (b << 8) | b;
+ break;
+ case 6:
+ address = (address & 0x1fffe);
+ if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000))
+ return;
+ if ((address & 0x18000) == 0x18000)
+ address &= 0x17fff;
+
+ // no need to switch
+ // byte writes to OBJ VRAM are ignored
+ if ((address) < objTilesAddress[((io_registers[REG_DISPCNT] & 7)+1)>>2])
+ *((u16 *)&vram[address]) = (b << 8) | b;
+ break;
+ case 7:
+ // no need to switch
+ // byte writes to OAM are ignored
+ // *((u16 *)&oam[address & 0x3FE]) = (b << 8) | b;
+ break;
+ case 13:
+ if(cpuEEPROMEnabled)
+ eepromWrite(b);
+ break;
+ case 14:
+ if ((saveType != 5) && ((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled))
+ {
+ (this->*cpuSaveGameFunc)(address, b);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+
+/*============================================================
+ BIOS
+============================================================ */
+
+void BIOS_RegisterRamReset(u32 flags)
+{
+ // no need to trace here. this is only called directly from GBA.cpp
+ // to emulate bios initialization
+
+ CPUUpdateRegister(0x0, 0x80);
+
+ if(flags)
+ {
+ if(flags & 0x01)
+ memset(workRAM, 0, 0x40000); // clear work RAM
+
+ if(flags & 0x02)
+ memset(internalRAM, 0, 0x7e00); // don't clear 0x7e00-0x7fff, clear internal RAM
+
+ if(flags & 0x04)
+ memset(graphics.paletteRAM, 0, 0x400); // clear palette RAM
+
+ if(flags & 0x08)
+ memset(vram, 0, 0x18000); // clear VRAM
+
+ if(flags & 0x10)
+ memset(oam, 0, 0x400); // clean OAM
+
+ if(flags & 0x80) {
+ int i;
+ for(i = 0; i < 0x10; i++)
+ CPUUpdateRegister(0x200+i*2, 0);
+
+ for(i = 0; i < 0xF; i++)
+ CPUUpdateRegister(0x4+i*2, 0);
+
+ for(i = 0; i < 0x20; i++)
+ CPUUpdateRegister(0x20+i*2, 0);
+
+ for(i = 0; i < 0x18; i++)
+ CPUUpdateRegister(0xb0+i*2, 0);
+
+ CPUUpdateRegister(0x130, 0);
+ CPUUpdateRegister(0x20, 0x100);
+ CPUUpdateRegister(0x30, 0x100);
+ CPUUpdateRegister(0x26, 0x100);
+ CPUUpdateRegister(0x36, 0x100);
+ }
+
+ if(flags & 0x20) {
+ int i;
+ for(i = 0; i < 8; i++)
+ CPUUpdateRegister(0x110+i*2, 0);
+ CPUUpdateRegister(0x134, 0x8000);
+ for(i = 0; i < 7; i++)
+ CPUUpdateRegister(0x140+i*2, 0);
+ }
+
+ if(flags & 0x40) {
+ int i;
+ CPUWriteByte(0x4000084, 0);
+ CPUWriteByte(0x4000084, 0x80);
+ CPUWriteMemory(0x4000080, 0x880e0000);
+ CPUUpdateRegister(0x88, CPUReadHalfWord(0x4000088)&0x3ff);
+ CPUWriteByte(0x4000070, 0x70);
+ for(i = 0; i < 8; i++)
+ CPUUpdateRegister(0x90+i*2, 0);
+ CPUWriteByte(0x4000070, 0);
+ for(i = 0; i < 8; i++)
+ CPUUpdateRegister(0x90+i*2, 0);
+ CPUWriteByte(0x4000084, 0);
+ }
+ }
+}
+
+void BIOS_SoftReset (void)
+{
+ armState = true;
+ armMode = 0x1F;
+ armIrqEnable = false;
+ C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false;
+ bus.reg[13].I = 0x03007F00;
+ bus.reg[14].I = 0x00000000;
+ bus.reg[16].I = 0x00000000;
+ bus.reg[R13_IRQ].I = 0x03007FA0;
+ bus.reg[R14_IRQ].I = 0x00000000;
+ bus.reg[SPSR_IRQ].I = 0x00000000;
+ bus.reg[R13_SVC].I = 0x03007FE0;
+ bus.reg[R14_SVC].I = 0x00000000;
+ bus.reg[SPSR_SVC].I = 0x00000000;
+ u8 b = internalRAM[0x7ffa];
+
+ memset(&internalRAM[0x7e00], 0, 0x200);
+
+ if(b) {
+ bus.armNextPC = 0x02000000;
+ bus.reg[15].I = 0x02000004;
+ } else {
+ bus.armNextPC = 0x08000000;
+ bus.reg[15].I = 0x08000004;
+ }
+}
+
+#define BIOS_GET_BIOS_CHECKSUM() bus.reg[0].I=0xBAAE187F;
+
+#define BIOS_REGISTER_RAM_RESET() BIOS_RegisterRamReset(bus.reg[0].I);
+
+#define CPU_UPDATE_CPSR() \
+{ \
+ uint32_t CPSR; \
+ CPSR = bus.reg[16].I & 0x40; \
+ if(N_FLAG) \
+ CPSR |= 0x80000000; \
+ if(Z_FLAG) \
+ CPSR |= 0x40000000; \
+ if(C_FLAG) \
+ CPSR |= 0x20000000; \
+ if(V_FLAG) \
+ CPSR |= 0x10000000; \
+ if(!armState) \
+ CPSR |= 0x00000020; \
+ if(!armIrqEnable) \
+ CPSR |= 0x80; \
+ CPSR |= (armMode & 0x1F); \
+ bus.reg[16].I = CPSR; \
+}
+
+#define CPU_SOFTWARE_INTERRUPT() \
+{ \
+ uint32_t PC = bus.reg[15].I; \
+ bool savedArmState = armState; \
+ if(armMode != 0x13) \
+ CPUSwitchMode(0x13, true, false); \
+ bus.reg[14].I = PC - (savedArmState ? 4 : 2); \
+ bus.reg[15].I = 0x08; \
+ armState = true; \
+ armIrqEnable = false; \
+ bus.armNextPC = 0x08; \
+ ARM_PREFETCH; \
+ bus.reg[15].I += 4; \
+}
+
+void CPUUpdateFlags(bool breakLoop)
+{
+ uint32_t CPSR = bus.reg[16].I;
+
+ N_FLAG = (CPSR & 0x80000000) ? true: false;
+ Z_FLAG = (CPSR & 0x40000000) ? true: false;
+ C_FLAG = (CPSR & 0x20000000) ? true: false;
+ V_FLAG = (CPSR & 0x10000000) ? true: false;
+ armState = (CPSR & 0x20) ? false : true;
+ armIrqEnable = (CPSR & 0x80) ? false : true;
+ if (breakLoop && armIrqEnable && (io_registers[REG_IF] & io_registers[REG_IE]) && (io_registers[REG_IME] & 1))
+ cpuNextEvent = cpuTotalTicks;
+}
+
+void CPUSoftwareInterrupt(int comment)
+{
+ if(armState)
+ comment >>= 16;
+
+ CPU_SOFTWARE_INTERRUPT();
+}
+
+
+/*============================================================
+ GBA ARM CORE
+============================================================ */
+
+#ifdef _MSC_VER
+ // Disable "empty statement" warnings
+ #pragma warning(disable: 4390)
+ // Visual C's inline assembler treats "offset" as a reserved word, so we
+ // tell it otherwise. If you want to use it, write "OFFSET" in capitals.
+ #define offset offset_
+#endif
+
+void armUnknownInsn(u32 opcode)
+{
+ u32 PC = bus.reg[15].I;
+ bool savedArmState = armState;
+ if(armMode != 0x1b )
+ CPUSwitchMode(0x1b, true, false);
+ bus.reg[14].I = PC - (savedArmState ? 4 : 2);
+ bus.reg[15].I = 0x04;
+ armState = true;
+ armIrqEnable = false;
+ bus.armNextPC = 0x04;
+ ARM_PREFETCH;
+ bus.reg[15].I += 4;
+}
+
+// Common macros //////////////////////////////////////////////////////////
+
+#define NEG(i) ((i) >> 31)
+#define POS(i) ((~(i)) >> 31)
+
+// The following macros are used for optimization; any not defined for a
+// particular compiler/CPU combination default to the C core versions.
+//
+// ALU_INIT_C: Used at the beginning of ALU instructions (AND/EOR/...).
+// (ALU_INIT_NC) Can consist of variable declarations, like the C core,
+// or the start of a continued assembly block, like the
+// x86-optimized version. The _C version is used when the
+// carry flag from the shift operation is needed (logical
+// operations that set condition codes, like ANDS); the
+// _NC version is used when the carry result is ignored.
+// VALUE_XXX: Retrieve the second operand's value for an ALU instruction.
+// The _C and _NC versions are used the same way as ALU_INIT.
+// OP_XXX: ALU operations. XXX is the instruction name.
+// SETCOND_NONE: Used in multiply instructions in place of SETCOND_MUL
+// when the condition codes are not set. Usually empty.
+// SETCOND_MUL: Used in multiply instructions to set the condition codes.
+// ROR_IMM_MSR: Used to rotate the immediate operand for MSR.
+// ROR_OFFSET: Used to rotate the `offset' parameter for LDR and STR
+// instructions.
+// RRX_OFFSET: Used to rotate (RRX) the `offset' parameter for LDR and
+// STR instructions.
+
+// C core
+
+#define C_SETCOND_LOGICAL \
+ N_FLAG = ((s32)res < 0) ? true : false; \
+ Z_FLAG = (res == 0) ? true : false; \
+ C_FLAG = C_OUT;
+#define C_SETCOND_ADD \
+ N_FLAG = ((s32)res < 0) ? true : false; \
+ Z_FLAG = (res == 0) ? true : false; \
+ V_FLAG = ((NEG(lhs) & NEG(rhs) & POS(res)) | \
+ (POS(lhs) & POS(rhs) & NEG(res))) ? true : false;\
+ C_FLAG = ((NEG(lhs) & NEG(rhs)) | \
+ (NEG(lhs) & POS(res)) | \
+ (NEG(rhs) & POS(res))) ? true : false;
+#define C_SETCOND_SUB \
+ N_FLAG = ((s32)res < 0) ? true : false; \
+ Z_FLAG = (res == 0) ? true : false; \
+ V_FLAG = ((NEG(lhs) & POS(rhs) & POS(res)) | \
+ (POS(lhs) & NEG(rhs) & NEG(res))) ? true : false;\
+ C_FLAG = ((NEG(lhs) & POS(rhs)) | \
+ (NEG(lhs) & POS(res)) | \
+ (POS(rhs) & POS(res))) ? true : false;
+
+#ifndef ALU_INIT_C
+ #define ALU_INIT_C \
+ int dest = (opcode>>12) & 15; \
+ bool C_OUT = C_FLAG; \
+ u32 value;
+#endif
+// OP Rd,Rb,Rm LSL #
+#ifndef VALUE_LSL_IMM_C
+ #define VALUE_LSL_IMM_C \
+ unsigned int shift = (opcode >> 7) & 0x1F; \
+ if (!shift) { /* LSL #0 most common? */ \
+ value = bus.reg[opcode & 0x0F].I; \
+ } else { \
+ u32 v = bus.reg[opcode & 0x0F].I; \
+ C_OUT = (v >> (32 - shift)) & 1 ? true : false; \
+ value = v << shift; \
+ }
+#endif
+// OP Rd,Rb,Rm LSL Rs
+#ifndef VALUE_LSL_REG_C
+ #define VALUE_LSL_REG_C \
+ unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \
+ if (shift) { \
+ if (shift == 32) { \
+ value = 0; \
+ C_OUT = (bus.reg[opcode & 0x0F].I & 1 ? true : false);\
+ } else if (shift < 32) { \
+ u32 v = bus.reg[opcode & 0x0F].I; \
+ C_OUT = (v >> (32 - shift)) & 1 ? true : false;\
+ value = v << shift; \
+ } else { \
+ value = 0; \
+ C_OUT = false; \
+ } \
+ } else { \
+ value = bus.reg[opcode & 0x0F].I; \
+ }
+#endif
+// OP Rd,Rb,Rm LSR #
+#ifndef VALUE_LSR_IMM_C
+ #define VALUE_LSR_IMM_C \
+ unsigned int shift = (opcode >> 7) & 0x1F; \
+ if (shift) { \
+ u32 v = bus.reg[opcode & 0x0F].I; \
+ C_OUT = (v >> (shift - 1)) & 1 ? true : false; \
+ value = v >> shift; \
+ } else { \
+ value = 0; \
+ C_OUT = (bus.reg[opcode & 0x0F].I & 0x80000000) ? true : false;\
+ }
+#endif
+// OP Rd,Rb,Rm LSR Rs
+#ifndef VALUE_LSR_REG_C
+ #define VALUE_LSR_REG_C \
+ unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \
+ if (shift) { \
+ if (shift == 32) { \
+ value = 0; \
+ C_OUT = (bus.reg[opcode & 0x0F].I & 0x80000000 ? true : false);\
+ } else if (shift < 32) { \
+ u32 v = bus.reg[opcode & 0x0F].I; \
+ C_OUT = (v >> (shift - 1)) & 1 ? true : false;\
+ value = v >> shift; \
+ } else { \
+ value = 0; \
+ C_OUT = false; \
+ } \
+ } else { \
+ value = bus.reg[opcode & 0x0F].I; \
+ }
+#endif
+// OP Rd,Rb,Rm ASR #
+#ifndef VALUE_ASR_IMM_C
+ #define VALUE_ASR_IMM_C \
+ unsigned int shift = (opcode >> 7) & 0x1F; \
+ if (shift) { \
+ s32 v = bus.reg[opcode & 0x0F].I; \
+ C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;\
+ value = v >> (int)shift; \
+ } else { \
+ if (bus.reg[opcode & 0x0F].I & 0x80000000) { \
+ value = 0xFFFFFFFF; \
+ C_OUT = true; \
+ } else { \
+ value = 0; \
+ C_OUT = false; \
+ } \
+ }
+#endif
+// OP Rd,Rb,Rm ASR Rs
+#ifndef VALUE_ASR_REG_C
+ #define VALUE_ASR_REG_C \
+ unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \
+ if (shift < 32) { \
+ if (shift) { \
+ s32 v = bus.reg[opcode & 0x0F].I; \
+ C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;\
+ value = v >> (int)shift; \
+ } else { \
+ value = bus.reg[opcode & 0x0F].I; \
+ } \
+ } else { \
+ if (bus.reg[opcode & 0x0F].I & 0x80000000) { \
+ value = 0xFFFFFFFF; \
+ C_OUT = true; \
+ } else { \
+ value = 0; \
+ C_OUT = false; \
+ } \
+ }
+#endif
+// OP Rd,Rb,Rm ROR #
+#ifndef VALUE_ROR_IMM_C
+ #define VALUE_ROR_IMM_C \
+ unsigned int shift = (opcode >> 7) & 0x1F; \
+ if (shift) { \
+ u32 v = bus.reg[opcode & 0x0F].I; \
+ C_OUT = (v >> (shift - 1)) & 1 ? true : false; \
+ value = ((v << (32 - shift)) | \
+ (v >> shift)); \
+ } else { \
+ u32 v = bus.reg[opcode & 0x0F].I; \
+ C_OUT = (v & 1) ? true : false; \
+ value = ((v >> 1) | \
+ (C_FLAG << 31)); \
+ }
+#endif
+// OP Rd,Rb,Rm ROR Rs
+#ifndef VALUE_ROR_REG_C
+ #define VALUE_ROR_REG_C \
+ unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \
+ if (shift & 0x1F) { \
+ u32 v = bus.reg[opcode & 0x0F].I; \
+ C_OUT = (v >> (shift - 1)) & 1 ? true : false; \
+ value = ((v << (32 - shift)) | \
+ (v >> shift)); \
+ } else { \
+ value = bus.reg[opcode & 0x0F].I; \
+ if (shift) \
+ C_OUT = (value & 0x80000000 ? true : false);\
+ }
+#endif
+// OP Rd,Rb,# ROR #
+#ifndef VALUE_IMM_C
+ #define VALUE_IMM_C \
+ int shift = (opcode & 0xF00) >> 7; \
+ if (shift) { \
+ u32 v = opcode & 0xFF; \
+ C_OUT = (v >> (shift - 1)) & 1 ? true : false; \
+ value = ((v << (32 - shift)) | \
+ (v >> shift)); \
+ } else { \
+ value = opcode & 0xFF; \
+ }
+#endif
+
+// Make the non-carry versions default to the carry versions
+// (this is fine for C--the compiler will optimize the dead code out)
+#ifndef ALU_INIT_NC
+ #define ALU_INIT_NC ALU_INIT_C
+#endif
+#ifndef VALUE_LSL_IMM_NC
+ #define VALUE_LSL_IMM_NC VALUE_LSL_IMM_C
+#endif
+#ifndef VALUE_LSL_REG_NC
+ #define VALUE_LSL_REG_NC VALUE_LSL_REG_C
+#endif
+#ifndef VALUE_LSR_IMM_NC
+ #define VALUE_LSR_IMM_NC VALUE_LSR_IMM_C
+#endif
+#ifndef VALUE_LSR_REG_NC
+ #define VALUE_LSR_REG_NC VALUE_LSR_REG_C
+#endif
+#ifndef VALUE_ASR_IMM_NC
+ #define VALUE_ASR_IMM_NC VALUE_ASR_IMM_C
+#endif
+#ifndef VALUE_ASR_REG_NC
+ #define VALUE_ASR_REG_NC VALUE_ASR_REG_C
+#endif
+#ifndef VALUE_ROR_IMM_NC
+ #define VALUE_ROR_IMM_NC VALUE_ROR_IMM_C
+#endif
+#ifndef VALUE_ROR_REG_NC
+ #define VALUE_ROR_REG_NC VALUE_ROR_REG_C
+#endif
+#ifndef VALUE_IMM_NC
+ #define VALUE_IMM_NC VALUE_IMM_C
+#endif
+
+#define C_CHECK_PC(SETCOND) if (dest != 15) { SETCOND }
+#ifndef OP_AND
+ #define OP_AND \
+ u32 res = bus.reg[(opcode>>16)&15].I & value; \
+ bus.reg[dest].I = res;
+#endif
+#ifndef OP_ANDS
+ #define OP_ANDS OP_AND C_CHECK_PC(C_SETCOND_LOGICAL)
+#endif
+#ifndef OP_EOR
+ #define OP_EOR \
+ u32 res = bus.reg[(opcode>>16)&15].I ^ value; \
+ bus.reg[dest].I = res;
+#endif
+#ifndef OP_EORS
+ #define OP_EORS OP_EOR C_CHECK_PC(C_SETCOND_LOGICAL)
+#endif
+#ifndef OP_SUB
+ #define OP_SUB \
+ u32 lhs = bus.reg[(opcode>>16)&15].I; \
+ u32 rhs = value; \
+ u32 res = lhs - rhs; \
+ bus.reg[dest].I = res;
+#endif
+#ifndef OP_SUBS
+ #define OP_SUBS OP_SUB C_CHECK_PC(C_SETCOND_SUB)
+#endif
+#ifndef OP_RSB
+ #define OP_RSB \
+ u32 lhs = bus.reg[(opcode>>16)&15].I; \
+ u32 rhs = value; \
+ u32 res = rhs - lhs; \
+ bus.reg[dest].I = res;
+#endif
+#ifndef OP_RSBS
+ #define OP_RSBS OP_RSB C_CHECK_PC(C_SETCOND_SUB)
+#endif
+#ifndef OP_ADD
+ #define OP_ADD \
+ u32 lhs = bus.reg[(opcode>>16)&15].I; \
+ u32 rhs = value; \
+ u32 res = lhs + rhs; \
+ bus.reg[dest].I = res;
+#endif
+#ifndef OP_ADDS
+ #define OP_ADDS OP_ADD C_CHECK_PC(C_SETCOND_ADD)
+#endif
+#ifndef OP_ADC
+ #define OP_ADC \
+ u32 lhs = bus.reg[(opcode>>16)&15].I; \
+ u32 rhs = value; \
+ u32 res = lhs + rhs + (u32)C_FLAG; \
+ bus.reg[dest].I = res;
+#endif
+#ifndef OP_ADCS
+ #define OP_ADCS OP_ADC C_CHECK_PC(C_SETCOND_ADD)
+#endif
+#ifndef OP_SBC
+ #define OP_SBC \
+ u32 lhs = bus.reg[(opcode>>16)&15].I; \
+ u32 rhs = value; \
+ u32 res = lhs - rhs - !((u32)C_FLAG); \
+ bus.reg[dest].I = res;
+#endif
+#ifndef OP_SBCS
+ #define OP_SBCS OP_SBC C_CHECK_PC(C_SETCOND_SUB)
+#endif
+#ifndef OP_RSC
+ #define OP_RSC \
+ u32 lhs = bus.reg[(opcode>>16)&15].I; \
+ u32 rhs = value; \
+ u32 res = rhs - lhs - !((u32)C_FLAG); \
+ bus.reg[dest].I = res;
+#endif
+#ifndef OP_RSCS
+ #define OP_RSCS OP_RSC C_CHECK_PC(C_SETCOND_SUB)
+#endif
+#ifndef OP_TST
+ #define OP_TST \
+ u32 res = bus.reg[(opcode >> 16) & 0x0F].I & value; \
+ C_SETCOND_LOGICAL;
+#endif
+#ifndef OP_TEQ
+ #define OP_TEQ \
+ u32 res = bus.reg[(opcode >> 16) & 0x0F].I ^ value; \
+ C_SETCOND_LOGICAL;
+#endif
+#ifndef OP_CMP
+ #define OP_CMP \
+ u32 lhs = bus.reg[(opcode>>16)&15].I; \
+ u32 rhs = value; \
+ u32 res = lhs - rhs; \
+ C_SETCOND_SUB;
+#endif
+#ifndef OP_CMN
+ #define OP_CMN \
+ u32 lhs = bus.reg[(opcode>>16)&15].I; \
+ u32 rhs = value; \
+ u32 res = lhs + rhs; \
+ C_SETCOND_ADD;
+#endif
+#ifndef OP_ORR
+ #define OP_ORR \
+ u32 res = bus.reg[(opcode >> 16) & 0x0F].I | value; \
+ bus.reg[dest].I = res;
+#endif
+#ifndef OP_ORRS
+ #define OP_ORRS OP_ORR C_CHECK_PC(C_SETCOND_LOGICAL)
+#endif
+#ifndef OP_MOV
+ #define OP_MOV \
+ u32 res = value; \
+ bus.reg[dest].I = res;
+#endif
+#ifndef OP_MOVS
+ #define OP_MOVS OP_MOV C_CHECK_PC(C_SETCOND_LOGICAL)
+#endif
+#ifndef OP_BIC
+ #define OP_BIC \
+ u32 res = bus.reg[(opcode >> 16) & 0x0F].I & (~value); \
+ bus.reg[dest].I = res;
+#endif
+#ifndef OP_BICS
+ #define OP_BICS OP_BIC C_CHECK_PC(C_SETCOND_LOGICAL)
+#endif
+#ifndef OP_MVN
+ #define OP_MVN \
+ u32 res = ~value; \
+ bus.reg[dest].I = res;
+#endif
+#ifndef OP_MVNS
+ #define OP_MVNS OP_MVN C_CHECK_PC(C_SETCOND_LOGICAL)
+#endif
+
+#ifndef SETCOND_NONE
+ #define SETCOND_NONE /*nothing*/
+#endif
+#ifndef SETCOND_MUL
+ #define SETCOND_MUL \
+ N_FLAG = ((s32)bus.reg[dest].I < 0) ? true : false; \
+ Z_FLAG = bus.reg[dest].I ? false : true;
+#endif
+#ifndef SETCOND_MULL
+ #define SETCOND_MULL \
+ N_FLAG = (bus.reg[dest].I & 0x80000000) ? true : false;\
+ Z_FLAG = bus.reg[dest].I || bus.reg[acc].I ? false : true;
+#endif
+
+#ifndef ROR_IMM_MSR
+ #define ROR_IMM_MSR \
+ u32 v = opcode & 0xff; \
+ value = ((v << (32 - shift)) | (v >> shift));
+#endif
+#ifndef ROR_OFFSET
+ #define ROR_OFFSET \
+ offset = ((offset << (32 - shift)) | (offset >> shift));
+#endif
+#ifndef RRX_OFFSET
+ #define RRX_OFFSET \
+ offset = ((offset >> 1) | ((int)C_FLAG << 31));
+#endif
+
+// ALU ops (except multiply) //////////////////////////////////////////////
+
+// ALU_INIT: init code (ALU_INIT_C or ALU_INIT_NC)
+// GETVALUE: load value and shift/rotate (VALUE_XXX)
+// OP: ALU operation (OP_XXX)
+// MODECHANGE: MODECHANGE_NO or MODECHANGE_YES
+// ISREGSHIFT: 1 for insns of the form ...,Rn LSL/etc Rs; 0 otherwise
+// ALU_INIT, GETVALUE and OP are concatenated in order.
+#define ALU_INSN(ALU_INIT, GETVALUE, OP, MODECHANGE, ISREGSHIFT) \
+ ALU_INIT GETVALUE OP; \
+ if ((opcode & 0x0000F000) != 0x0000F000) { \
+ clockTicks = 1 + ISREGSHIFT \
+ + codeTicksAccessSeq32(bus.armNextPC); \
+ } else { \
+ MODECHANGE; \
+ if (armState) { \
+ bus.reg[15].I &= 0xFFFFFFFC; \
+ bus.armNextPC = bus.reg[15].I; \
+ bus.reg[15].I += 4; \
+ ARM_PREFETCH; \
+ } else { \
+ bus.reg[15].I &= 0xFFFFFFFE; \
+ bus.armNextPC = bus.reg[15].I; \
+ bus.reg[15].I += 2; \
+ THUMB_PREFETCH; \
+ } \
+ clockTicks = 3 + ISREGSHIFT \
+ + codeTicksAccess(bus.armNextPC, BITS_32) \
+ + ((codeTicksAccessSeq32(bus.armNextPC)) << 1); \
+ }
+
+#define MODECHANGE_NO /*nothing*/
+#define MODECHANGE_YES if(armMode != (bus.reg[17].I & 0x1f)) CPUSwitchMode(bus.reg[17].I & 0x1f, false, true);
+
+#define DEFINE_ALU_INSN_C(CODE1, CODE2, OP, MODECHANGE) \
+ void arm##CODE1##0(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\
+ void arm##CODE1##1(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\
+ void arm##CODE1##2(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\
+ void arm##CODE1##3(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\
+ void arm##CODE1##4(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\
+ void arm##CODE1##5(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\
+ void arm##CODE1##6(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\
+ void arm##CODE1##7(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\
+ void arm##CODE2##0(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }
+#define DEFINE_ALU_INSN_NC(CODE1, CODE2, OP, MODECHANGE) \
+ void arm##CODE1##0(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\
+ void arm##CODE1##1(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\
+ void arm##CODE1##2(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\
+ void arm##CODE1##3(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\
+ void arm##CODE1##4(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\
+ void arm##CODE1##5(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\
+ void arm##CODE1##6(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\
+ void arm##CODE1##7(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\
+ void arm##CODE2##0(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }
+
+// AND
+DEFINE_ALU_INSN_NC(00, 20, AND, NO)
+// ANDS
+DEFINE_ALU_INSN_C (01, 21, ANDS, YES)
+
+// EOR
+DEFINE_ALU_INSN_NC(02, 22, EOR, NO)
+// EORS
+DEFINE_ALU_INSN_C (03, 23, EORS, YES)
+
+// SUB
+DEFINE_ALU_INSN_NC(04, 24, SUB, NO)
+// SUBS
+DEFINE_ALU_INSN_NC(05, 25, SUBS, YES)
+
+// RSB
+DEFINE_ALU_INSN_NC(06, 26, RSB, NO)
+// RSBS
+DEFINE_ALU_INSN_NC(07, 27, RSBS, YES)
+
+// ADD
+DEFINE_ALU_INSN_NC(08, 28, ADD, NO)
+// ADDS
+DEFINE_ALU_INSN_NC(09, 29, ADDS, YES)
+
+// ADC
+DEFINE_ALU_INSN_NC(0A, 2A, ADC, NO)
+// ADCS
+DEFINE_ALU_INSN_NC(0B, 2B, ADCS, YES)
+
+// SBC
+DEFINE_ALU_INSN_NC(0C, 2C, SBC, NO)
+// SBCS
+DEFINE_ALU_INSN_NC(0D, 2D, SBCS, YES)
+
+// RSC
+DEFINE_ALU_INSN_NC(0E, 2E, RSC, NO)
+// RSCS
+DEFINE_ALU_INSN_NC(0F, 2F, RSCS, YES)
+
+// TST
+DEFINE_ALU_INSN_C (11, 31, TST, NO)
+
+// TEQ
+DEFINE_ALU_INSN_C (13, 33, TEQ, NO)
+
+// CMP
+DEFINE_ALU_INSN_NC(15, 35, CMP, NO)
+
+// CMN
+DEFINE_ALU_INSN_NC(17, 37, CMN, NO)
+
+// ORR
+DEFINE_ALU_INSN_NC(18, 38, ORR, NO)
+// ORRS
+DEFINE_ALU_INSN_C (19, 39, ORRS, YES)
+
+// MOV
+DEFINE_ALU_INSN_NC(1A, 3A, MOV, NO)
+// MOVS
+DEFINE_ALU_INSN_C (1B, 3B, MOVS, YES)
+
+// BIC
+DEFINE_ALU_INSN_NC(1C, 3C, BIC, NO)
+// BICS
+DEFINE_ALU_INSN_C (1D, 3D, BICS, YES)
+
+// MVN
+DEFINE_ALU_INSN_NC(1E, 3E, MVN, NO)
+// MVNS
+DEFINE_ALU_INSN_C (1F, 3F, MVNS, YES)
+
+// Multiply instructions //////////////////////////////////////////////////
+
+// OP: OP_MUL, OP_MLA etc.
+// SETCOND: SETCOND_NONE, SETCOND_MUL, or SETCOND_MULL
+// CYCLES: base cycle count (1, 2, or 3)
+#define MUL_INSN(OP, SETCOND, CYCLES) \
+ int mult = (opcode & 0x0F); \
+ u32 rs = bus.reg[(opcode >> 8) & 0x0F].I; \
+ int acc = (opcode >> 12) & 0x0F; /* or destLo */ \
+ int dest = (opcode >> 16) & 0x0F; /* or destHi */ \
+ OP; \
+ SETCOND; \
+ if ((s32)rs < 0) \
+ rs = ~rs; \
+ if ((rs & 0xFFFF0000) == 0) \
+ clockTicks += 1; \
+ else if ((rs & 0xFF000000) == 0) \
+ clockTicks += 2; \
+ else \
+ clockTicks += 3; \
+ if (bus.busPrefetchCount == 0) \
+ bus.busPrefetchCount = ((bus.busPrefetchCount+1)<> 32);
+#define OP_MLAL(SIGN) \
+ SIGN##64 res = ((SIGN##64)bus.reg[dest].I<<32 | bus.reg[acc].I)\
+ + ((SIGN##64)(SIGN##32)bus.reg[mult].I \
+ * (SIGN##64)(SIGN##32)rs); \
+ bus.reg[acc].I = (u32)res; \
+ bus.reg[dest].I = (u32)(res >> 32);
+#define OP_UMULL OP_MULL(u)
+#define OP_UMLAL OP_MLAL(u)
+#define OP_SMULL OP_MULL(s)
+#define OP_SMLAL OP_MLAL(s)
+
+// MUL Rd, Rm, Rs
+ void arm009(u32 opcode) { MUL_INSN(OP_MUL, SETCOND_NONE, 1); }
+// MULS Rd, Rm, Rs
+ void arm019(u32 opcode) { MUL_INSN(OP_MUL, SETCOND_MUL, 1); }
+
+// MLA Rd, Rm, Rs, Rn
+ void arm029(u32 opcode) { MUL_INSN(OP_MLA, SETCOND_NONE, 2); }
+// MLAS Rd, Rm, Rs, Rn
+ void arm039(u32 opcode) { MUL_INSN(OP_MLA, SETCOND_MUL, 2); }
+
+// UMULL RdLo, RdHi, Rn, Rs
+ void arm089(u32 opcode) { MUL_INSN(OP_UMULL, SETCOND_NONE, 2); }
+// UMULLS RdLo, RdHi, Rn, Rs
+ void arm099(u32 opcode) { MUL_INSN(OP_UMULL, SETCOND_MULL, 2); }
+
+// UMLAL RdLo, RdHi, Rn, Rs
+ void arm0A9(u32 opcode) { MUL_INSN(OP_UMLAL, SETCOND_NONE, 3); }
+// UMLALS RdLo, RdHi, Rn, Rs
+ void arm0B9(u32 opcode) { MUL_INSN(OP_UMLAL, SETCOND_MULL, 3); }
+
+// SMULL RdLo, RdHi, Rm, Rs
+ void arm0C9(u32 opcode) { MUL_INSN(OP_SMULL, SETCOND_NONE, 2); }
+// SMULLS RdLo, RdHi, Rm, Rs
+ void arm0D9(u32 opcode) { MUL_INSN(OP_SMULL, SETCOND_MULL, 2); }
+
+// SMLAL RdLo, RdHi, Rm, Rs
+ void arm0E9(u32 opcode) { MUL_INSN(OP_SMLAL, SETCOND_NONE, 3); }
+// SMLALS RdLo, RdHi, Rm, Rs
+ void arm0F9(u32 opcode) { MUL_INSN(OP_SMLAL, SETCOND_MULL, 3); }
+
+// Misc instructions //////////////////////////////////////////////////////
+
+// SWP Rd, Rm, [Rn]
+ void arm109(u32 opcode)
+{
+ u32 address = bus.reg[(opcode >> 16) & 15].I;
+ u32 temp = CPUReadMemory(address);
+ CPUWriteMemory(address, bus.reg[opcode&15].I);
+ bus.reg[(opcode >> 12) & 15].I = temp;
+ int dataticks_value = DATATICKS_ACCESS_32BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = 4 + (dataticks_value << 1) + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// SWPB Rd, Rm, [Rn]
+ void arm149(u32 opcode)
+{
+ u32 address = bus.reg[(opcode >> 16) & 15].I;
+ u32 temp = CPUReadByte(address);
+ CPUWriteByte(address, bus.reg[opcode&15].B.B0);
+ bus.reg[(opcode>>12)&15].I = temp;
+ u32 dataticks_value = DATATICKS_ACCESS_32BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = 4 + (dataticks_value << 1) + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// MRS Rd, CPSR
+ void arm100(u32 opcode)
+{
+ if ((opcode & 0x0FFF0FFF) == 0x010F0000)
+ {
+ CPU_UPDATE_CPSR();
+ bus.reg[(opcode >> 12) & 0x0F].I = bus.reg[16].I;
+ }
+ else
+ armUnknownInsn(opcode);
+}
+
+// MRS Rd, SPSR
+ void arm140(u32 opcode)
+{
+ if ((opcode & 0x0FFF0FFF) == 0x014F0000)
+ bus.reg[(opcode >> 12) & 0x0F].I = bus.reg[17].I;
+ else
+ armUnknownInsn(opcode);
+}
+
+// MSR CPSR_fields, Rm
+ void arm120(u32 opcode)
+{
+ if ((opcode & 0x0FF0FFF0) == 0x0120F000)
+ {
+ CPU_UPDATE_CPSR();
+ u32 value = bus.reg[opcode & 15].I;
+ u32 newValue = bus.reg[16].I;
+ if (armMode > 0x10) {
+ if (opcode & 0x00010000)
+ newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF);
+ if (opcode & 0x00020000)
+ newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00);
+ if (opcode & 0x00040000)
+ newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000);
+ }
+ if (opcode & 0x00080000)
+ newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000);
+ newValue |= 0x10;
+ if(armMode != (newValue & 0x1F))
+ CPUSwitchMode(newValue & 0x1F, false, true);
+ bus.reg[16].I = newValue;
+ CPUUpdateFlags(1);
+ if (!armState) { // this should not be allowed, but it seems to work
+ THUMB_PREFETCH;
+ bus.reg[15].I = bus.armNextPC + 2;
+ }
+ }
+ else
+ armUnknownInsn(opcode);
+}
+
+// MSR SPSR_fields, Rm
+ void arm160(u32 opcode)
+{
+ if ((opcode & 0x0FF0FFF0) == 0x0160F000)
+ {
+ u32 value = bus.reg[opcode & 15].I;
+ if (armMode > 0x10 && armMode < 0x1F)
+ {
+ if (opcode & 0x00010000)
+ bus.reg[17].I = (bus.reg[17].I & 0xFFFFFF00) | (value & 0x000000FF);
+ if (opcode & 0x00020000)
+ bus.reg[17].I = (bus.reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00);
+ if (opcode & 0x00040000)
+ bus.reg[17].I = (bus.reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000);
+ if (opcode & 0x00080000)
+ bus.reg[17].I = (bus.reg[17].I & 0x00FFFFFF) | (value & 0xFF000000);
+ }
+ }
+ else
+ armUnknownInsn(opcode);
+}
+
+// MSR CPSR_fields, #
+ void arm320(u32 opcode)
+{
+ if ((opcode & 0x0FF0F000) == 0x0320F000)
+ {
+ CPU_UPDATE_CPSR();
+ u32 value = opcode & 0xFF;
+ int shift = (opcode & 0xF00) >> 7;
+ if (shift) {
+ ROR_IMM_MSR;
+ }
+ u32 newValue = bus.reg[16].I;
+ if (armMode > 0x10) {
+ if (opcode & 0x00010000)
+ newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF);
+ if (opcode & 0x00020000)
+ newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00);
+ if (opcode & 0x00040000)
+ newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000);
+ }
+ if (opcode & 0x00080000)
+ newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000);
+
+ newValue |= 0x10;
+
+ if(armMode != (newValue & 0x1F))
+ CPUSwitchMode(newValue & 0x1F, false, true);
+ bus.reg[16].I = newValue;
+ CPUUpdateFlags(1);
+ if (!armState) { // this should not be allowed, but it seems to work
+ THUMB_PREFETCH;
+ bus.reg[15].I = bus.armNextPC + 2;
+ }
+ }
+ else
+ armUnknownInsn(opcode);
+}
+
+// MSR SPSR_fields, #
+ void arm360(u32 opcode)
+{
+ if ((opcode & 0x0FF0F000) == 0x0360F000) {
+ if (armMode > 0x10 && armMode < 0x1F) {
+ u32 value = opcode & 0xFF;
+ int shift = (opcode & 0xF00) >> 7;
+ if (shift) {
+ ROR_IMM_MSR;
+ }
+ if (opcode & 0x00010000)
+ bus.reg[17].I = (bus.reg[17].I & 0xFFFFFF00) | (value & 0x000000FF);
+ if (opcode & 0x00020000)
+ bus.reg[17].I = (bus.reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00);
+ if (opcode & 0x00040000)
+ bus.reg[17].I = (bus.reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000);
+ if (opcode & 0x00080000)
+ bus.reg[17].I = (bus.reg[17].I & 0x00FFFFFF) | (value & 0xFF000000);
+ }
+ }
+ else
+ armUnknownInsn(opcode);
+}
+
+// BX Rm
+ void arm121(u32 opcode)
+{
+ if ((opcode & 0x0FFFFFF0) == 0x012FFF10) {
+ int base = opcode & 0x0F;
+ bus.busPrefetchCount = 0;
+ armState = bus.reg[base].I & 1 ? false : true;
+ if (armState) {
+ bus.reg[15].I = bus.reg[base].I & 0xFFFFFFFC;
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 4;
+ ARM_PREFETCH;
+ clockTicks = 3 + (codeTicksAccessSeq32(bus.armNextPC)<<1)
+ + codeTicksAccess(bus.armNextPC, BITS_32);
+ } else {
+ bus.reg[15].I = bus.reg[base].I & 0xFFFFFFFE;
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 2;
+ THUMB_PREFETCH;
+ clockTicks = 3 + (codeTicksAccessSeq16(bus.armNextPC)<<1)
+ + codeTicksAccess(bus.armNextPC, BITS_16);
+ }
+ }
+ else
+ armUnknownInsn(opcode);
+}
+
+// Load/store /////////////////////////////////////////////////////////////
+
+#define OFFSET_IMM \
+ int offset = opcode & 0xFFF;
+#define OFFSET_IMM8 \
+ int offset = ((opcode & 0x0F) | ((opcode>>4) & 0xF0));
+#define OFFSET_REG \
+ int offset = bus.reg[opcode & 15].I;
+#define OFFSET_LSL \
+ int offset = bus.reg[opcode & 15].I << ((opcode>>7) & 31);
+#define OFFSET_LSR \
+ int shift = (opcode >> 7) & 31; \
+ int offset = shift ? bus.reg[opcode & 15].I >> shift : 0;
+#define OFFSET_ASR \
+ int shift = (opcode >> 7) & 31; \
+ int offset; \
+ if (shift) \
+ offset = (int)((s32)bus.reg[opcode & 15].I >> shift);\
+ else if (bus.reg[opcode & 15].I & 0x80000000) \
+ offset = 0xFFFFFFFF; \
+ else \
+ offset = 0;
+#define OFFSET_ROR \
+ int shift = (opcode >> 7) & 31; \
+ u32 offset = bus.reg[opcode & 15].I; \
+ if (shift) { \
+ ROR_OFFSET; \
+ } else { \
+ RRX_OFFSET; \
+ }
+
+#define ADDRESS_POST (bus.reg[base].I)
+#define ADDRESS_PREDEC (bus.reg[base].I - offset)
+#define ADDRESS_PREINC (bus.reg[base].I + offset)
+
+#define OP_STR CPUWriteMemory(address, bus.reg[dest].I)
+#define OP_STRH CPUWriteHalfWord(address, bus.reg[dest].W.W0)
+#define OP_STRB CPUWriteByte(address, bus.reg[dest].B.B0)
+#define OP_LDR bus.reg[dest].I = CPUReadMemory(address)
+#define OP_LDRH bus.reg[dest].I = CPUReadHalfWord(address)
+#define OP_LDRB bus.reg[dest].I = CPUReadByte(address)
+#define OP_LDRSH bus.reg[dest].I = (s16)CPUReadHalfWordSigned(address)
+#define OP_LDRSB bus.reg[dest].I = (s8)CPUReadByte(address)
+
+#define WRITEBACK_NONE /*nothing*/
+#define WRITEBACK_PRE bus.reg[base].I = address
+#define WRITEBACK_POSTDEC bus.reg[base].I = address - offset
+#define WRITEBACK_POSTINC bus.reg[base].I = address + offset
+
+#define LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS) \
+ if (bus.busPrefetchCount == 0) \
+ bus.busPrefetch = bus.busPrefetchEnable; \
+ int dest = (opcode >> 12) & 15; \
+ int base = (opcode >> 16) & 15; \
+ CALC_OFFSET; \
+ u32 address = CALC_ADDRESS;
+
+#define STR(CALC_OFFSET, CALC_ADDRESS, STORE_DATA, WRITEBACK1, WRITEBACK2, SIZE) \
+ LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS); \
+ WRITEBACK1; \
+ STORE_DATA; \
+ WRITEBACK2; \
+ int dataticks_val; \
+ if(SIZE == 32) \
+ dataticks_val = DATATICKS_ACCESS_32BIT(address); \
+ else \
+ dataticks_val = DATATICKS_ACCESS_16BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_val); \
+ clockTicks = 2 + dataticks_val + codeTicksAccess(bus.armNextPC, BITS_32);
+
+#define LDR(CALC_OFFSET, CALC_ADDRESS, LOAD_DATA, WRITEBACK, SIZE) \
+ LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS); \
+ LOAD_DATA; \
+ if (dest != base) \
+ { \
+ WRITEBACK; \
+ } \
+ clockTicks = 0; \
+ int dataticks_value; \
+ if (dest == 15) { \
+ bus.reg[15].I &= 0xFFFFFFFC; \
+ bus.armNextPC = bus.reg[15].I; \
+ bus.reg[15].I += 4; \
+ ARM_PREFETCH; \
+ dataticks_value = DATATICKS_ACCESS_32BIT_SEQ(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \
+ clockTicks += 2 + (dataticks_value << 1);\
+ } \
+ if(SIZE == 32) \
+ dataticks_value = DATATICKS_ACCESS_32BIT(address); \
+ else \
+ dataticks_value = DATATICKS_ACCESS_16BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \
+ clockTicks += 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_32);
+#define STR_POSTDEC(CALC_OFFSET, STORE_DATA, SIZE) \
+ STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTDEC, SIZE)
+#define STR_POSTINC(CALC_OFFSET, STORE_DATA, SIZE) \
+ STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTINC, SIZE)
+#define STR_PREDEC(CALC_OFFSET, STORE_DATA, SIZE) \
+ STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE)
+#define STR_PREDEC_WB(CALC_OFFSET, STORE_DATA, SIZE) \
+ STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE)
+#define STR_PREINC(CALC_OFFSET, STORE_DATA, SIZE) \
+ STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE)
+#define STR_PREINC_WB(CALC_OFFSET, STORE_DATA, SIZE) \
+ STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE)
+#define LDR_POSTDEC(CALC_OFFSET, LOAD_DATA, SIZE) \
+ LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTDEC, SIZE)
+#define LDR_POSTINC(CALC_OFFSET, LOAD_DATA, SIZE) \
+ LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTINC, SIZE)
+#define LDR_PREDEC(CALC_OFFSET, LOAD_DATA, SIZE) \
+ LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_NONE, SIZE)
+#define LDR_PREDEC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \
+ LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_PRE, SIZE)
+#define LDR_PREINC(CALC_OFFSET, LOAD_DATA, SIZE) \
+ LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_NONE, SIZE)
+#define LDR_PREINC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \
+ LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_PRE, SIZE)
+
+// STRH Rd, [Rn], -Rm
+ void arm00B(u32 opcode) { STR_POSTDEC(OFFSET_REG, OP_STRH, 16); }
+// STRH Rd, [Rn], #-offset
+ void arm04B(u32 opcode) { STR_POSTDEC(OFFSET_IMM8, OP_STRH, 16); }
+// STRH Rd, [Rn], Rm
+ void arm08B(u32 opcode) { STR_POSTINC(OFFSET_REG, OP_STRH, 16); }
+// STRH Rd, [Rn], #offset
+ void arm0CB(u32 opcode) { STR_POSTINC(OFFSET_IMM8, OP_STRH, 16); }
+// STRH Rd, [Rn, -Rm]
+ void arm10B(u32 opcode) { STR_PREDEC(OFFSET_REG, OP_STRH, 16); }
+// STRH Rd, [Rn, -Rm]!
+ void arm12B(u32 opcode) { STR_PREDEC_WB(OFFSET_REG, OP_STRH, 16); }
+// STRH Rd, [Rn, -#offset]
+ void arm14B(u32 opcode) { STR_PREDEC(OFFSET_IMM8, OP_STRH, 16); }
+// STRH Rd, [Rn, -#offset]!
+ void arm16B(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM8, OP_STRH, 16); }
+// STRH Rd, [Rn, Rm]
+ void arm18B(u32 opcode) { STR_PREINC(OFFSET_REG, OP_STRH, 16); }
+// STRH Rd, [Rn, Rm]!
+ void arm1AB(u32 opcode) { STR_PREINC_WB(OFFSET_REG, OP_STRH, 16); }
+// STRH Rd, [Rn, #offset]
+ void arm1CB(u32 opcode) { STR_PREINC(OFFSET_IMM8, OP_STRH, 16); }
+// STRH Rd, [Rn, #offset]!
+ void arm1EB(u32 opcode) { STR_PREINC_WB(OFFSET_IMM8, OP_STRH, 16); }
+
+// LDRH Rd, [Rn], -Rm
+ void arm01B(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRH, 16); }
+// LDRH Rd, [Rn], #-offset
+ void arm05B(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRH, 16); }
+// LDRH Rd, [Rn], Rm
+ void arm09B(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRH, 16); }
+// LDRH Rd, [Rn], #offset
+ void arm0DB(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRH, 16); }
+// LDRH Rd, [Rn, -Rm]
+ void arm11B(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRH, 16); }
+// LDRH Rd, [Rn, -Rm]!
+ void arm13B(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRH, 16); }
+// LDRH Rd, [Rn, -#offset]
+ void arm15B(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRH, 16); }
+// LDRH Rd, [Rn, -#offset]!
+ void arm17B(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRH, 16); }
+// LDRH Rd, [Rn, Rm]
+ void arm19B(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRH, 16); }
+// LDRH Rd, [Rn, Rm]!
+ void arm1BB(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRH, 16); }
+// LDRH Rd, [Rn, #offset]
+ void arm1DB(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRH, 16); }
+// LDRH Rd, [Rn, #offset]!
+ void arm1FB(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRH, 16); }
+
+// LDRSB Rd, [Rn], -Rm
+ void arm01D(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSB, 16); }
+// LDRSB Rd, [Rn], #-offset
+ void arm05D(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSB, 16); }
+// LDRSB Rd, [Rn], Rm
+ void arm09D(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSB, 16); }
+// LDRSB Rd, [Rn], #offset
+ void arm0DD(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSB, 16); }
+// LDRSB Rd, [Rn, -Rm]
+ void arm11D(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSB, 16); }
+// LDRSB Rd, [Rn, -Rm]!
+ void arm13D(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSB, 16); }
+// LDRSB Rd, [Rn, -#offset]
+ void arm15D(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSB, 16); }
+// LDRSB Rd, [Rn, -#offset]!
+ void arm17D(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSB, 16); }
+// LDRSB Rd, [Rn, Rm]
+ void arm19D(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSB, 16); }
+// LDRSB Rd, [Rn, Rm]!
+ void arm1BD(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSB, 16); }
+// LDRSB Rd, [Rn, #offset]
+ void arm1DD(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSB, 16); }
+// LDRSB Rd, [Rn, #offset]!
+ void arm1FD(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSB, 16); }
+
+// LDRSH Rd, [Rn], -Rm
+ void arm01F(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSH, 16); }
+// LDRSH Rd, [Rn], #-offset
+ void arm05F(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSH, 16); }
+// LDRSH Rd, [Rn], Rm
+ void arm09F(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSH, 16); }
+// LDRSH Rd, [Rn], #offset
+ void arm0DF(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSH, 16); }
+// LDRSH Rd, [Rn, -Rm]
+ void arm11F(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSH, 16); }
+// LDRSH Rd, [Rn, -Rm]!
+ void arm13F(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSH, 16); }
+// LDRSH Rd, [Rn, -#offset]
+ void arm15F(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSH, 16); }
+// LDRSH Rd, [Rn, -#offset]!
+ void arm17F(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSH, 16); }
+// LDRSH Rd, [Rn, Rm]
+ void arm19F(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSH, 16); }
+// LDRSH Rd, [Rn, Rm]!
+ void arm1BF(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSH, 16); }
+// LDRSH Rd, [Rn, #offset]
+ void arm1DF(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSH, 16); }
+// LDRSH Rd, [Rn, #offset]!
+ void arm1FF(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSH, 16); }
+
+// STR[T] Rd, [Rn], -#
+// Note: STR and STRT do the same thing on the GBA (likewise for LDR/LDRT etc)
+ void arm400(u32 opcode) { STR_POSTDEC(OFFSET_IMM, OP_STR, 32); }
+// LDR[T] Rd, [Rn], -#
+ void arm410(u32 opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDR, 32); }
+// STRB[T] Rd, [Rn], -#
+ void arm440(u32 opcode) { STR_POSTDEC(OFFSET_IMM, OP_STRB, 16); }
+// LDRB[T] Rd, [Rn], -#
+ void arm450(u32 opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDRB, 16); }
+// STR[T] Rd, [Rn], #
+ void arm480(u32 opcode) { STR_POSTINC(OFFSET_IMM, OP_STR, 32); }
+// LDR Rd, [Rn], #
+ void arm490(u32 opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDR, 32); }
+// STRB[T] Rd, [Rn], #
+ void arm4C0(u32 opcode) { STR_POSTINC(OFFSET_IMM, OP_STRB, 16); }
+// LDRB[T] Rd, [Rn], #
+ void arm4D0(u32 opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDRB, 16); }
+// STR Rd, [Rn, -#]
+ void arm500(u32 opcode) { STR_PREDEC(OFFSET_IMM, OP_STR, 32); }
+// LDR Rd, [Rn, -#]
+ void arm510(u32 opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDR, 32); }
+// STR Rd, [Rn, -#]!
+ void arm520(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STR, 32); }
+// LDR Rd, [Rn, -#]!
+ void arm530(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDR, 32); }
+// STRB Rd, [Rn, -#]
+ void arm540(u32 opcode) { STR_PREDEC(OFFSET_IMM, OP_STRB, 16); }
+// LDRB Rd, [Rn, -#]
+ void arm550(u32 opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDRB, 16); }
+// STRB Rd, [Rn, -#]!
+ void arm560(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STRB, 16); }
+// LDRB Rd, [Rn, -#]!
+ void arm570(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDRB, 16); }
+// STR Rd, [Rn, #]
+ void arm580(u32 opcode) { STR_PREINC(OFFSET_IMM, OP_STR, 32); }
+// LDR Rd, [Rn, #]
+ void arm590(u32 opcode) { LDR_PREINC(OFFSET_IMM, OP_LDR, 32); }
+// STR Rd, [Rn, #]!
+ void arm5A0(u32 opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STR, 32); }
+// LDR Rd, [Rn, #]!
+ void arm5B0(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDR, 32); }
+// STRB Rd, [Rn, #]
+ void arm5C0(u32 opcode) { STR_PREINC(OFFSET_IMM, OP_STRB, 16); }
+// LDRB Rd, [Rn, #]
+ void arm5D0(u32 opcode) { LDR_PREINC(OFFSET_IMM, OP_LDRB, 16); }
+// STRB Rd, [Rn, #]!
+ void arm5E0(u32 opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STRB, 16); }
+// LDRB Rd, [Rn, #]!
+ void arm5F0(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDRB, 16); }
+
+// STR[T] Rd, [Rn], -Rm, LSL #
+ void arm600(u32 opcode) { STR_POSTDEC(OFFSET_LSL, OP_STR, 32); }
+// STR[T] Rd, [Rn], -Rm, LSR #
+ void arm602(u32 opcode) { STR_POSTDEC(OFFSET_LSR, OP_STR, 32); }
+// STR[T] Rd, [Rn], -Rm, ASR #
+ void arm604(u32 opcode) { STR_POSTDEC(OFFSET_ASR, OP_STR, 32); }
+// STR[T] Rd, [Rn], -Rm, ROR #
+ void arm606(u32 opcode) { STR_POSTDEC(OFFSET_ROR, OP_STR, 32); }
+// LDR[T] Rd, [Rn], -Rm, LSL #
+ void arm610(u32 opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDR, 32); }
+// LDR[T] Rd, [Rn], -Rm, LSR #
+ void arm612(u32 opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDR, 32); }
+// LDR[T] Rd, [Rn], -Rm, ASR #
+ void arm614(u32 opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDR, 32); }
+// LDR[T] Rd, [Rn], -Rm, ROR #
+ void arm616(u32 opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDR, 32); }
+// STRB[T] Rd, [Rn], -Rm, LSL #
+ void arm640(u32 opcode) { STR_POSTDEC(OFFSET_LSL, OP_STRB, 16); }
+// STRB[T] Rd, [Rn], -Rm, LSR #
+ void arm642(u32 opcode) { STR_POSTDEC(OFFSET_LSR, OP_STRB, 16); }
+// STRB[T] Rd, [Rn], -Rm, ASR #
+ void arm644(u32 opcode) { STR_POSTDEC(OFFSET_ASR, OP_STRB, 16); }
+// STRB[T] Rd, [Rn], -Rm, ROR #
+ void arm646(u32 opcode) { STR_POSTDEC(OFFSET_ROR, OP_STRB, 16); }
+// LDRB[T] Rd, [Rn], -Rm, LSL #
+ void arm650(u32 opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDRB, 16); }
+// LDRB[T] Rd, [Rn], -Rm, LSR #
+ void arm652(u32 opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDRB, 16); }
+// LDRB[T] Rd, [Rn], -Rm, ASR #
+ void arm654(u32 opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDRB, 16); }
+// LDRB Rd, [Rn], -Rm, ROR #
+ void arm656(u32 opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDRB, 16); }
+// STR[T] Rd, [Rn], Rm, LSL #
+ void arm680(u32 opcode) { STR_POSTINC(OFFSET_LSL, OP_STR, 32); }
+// STR[T] Rd, [Rn], Rm, LSR #
+ void arm682(u32 opcode) { STR_POSTINC(OFFSET_LSR, OP_STR, 32); }
+// STR[T] Rd, [Rn], Rm, ASR #
+ void arm684(u32 opcode) { STR_POSTINC(OFFSET_ASR, OP_STR, 32); }
+// STR[T] Rd, [Rn], Rm, ROR #
+ void arm686(u32 opcode) { STR_POSTINC(OFFSET_ROR, OP_STR, 32); }
+// LDR[T] Rd, [Rn], Rm, LSL #
+ void arm690(u32 opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDR, 32); }
+// LDR[T] Rd, [Rn], Rm, LSR #
+ void arm692(u32 opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDR, 32); }
+// LDR[T] Rd, [Rn], Rm, ASR #
+ void arm694(u32 opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDR, 32); }
+// LDR[T] Rd, [Rn], Rm, ROR #
+ void arm696(u32 opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDR, 32); }
+// STRB[T] Rd, [Rn], Rm, LSL #
+ void arm6C0(u32 opcode) { STR_POSTINC(OFFSET_LSL, OP_STRB, 16); }
+// STRB[T] Rd, [Rn], Rm, LSR #
+ void arm6C2(u32 opcode) { STR_POSTINC(OFFSET_LSR, OP_STRB, 16); }
+// STRB[T] Rd, [Rn], Rm, ASR #
+ void arm6C4(u32 opcode) { STR_POSTINC(OFFSET_ASR, OP_STRB, 16); }
+// STRB[T] Rd, [Rn], Rm, ROR #
+ void arm6C6(u32 opcode) { STR_POSTINC(OFFSET_ROR, OP_STRB, 16); }
+// LDRB[T] Rd, [Rn], Rm, LSL #
+ void arm6D0(u32 opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDRB, 16); }
+// LDRB[T] Rd, [Rn], Rm, LSR #
+ void arm6D2(u32 opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDRB, 16); }
+// LDRB[T] Rd, [Rn], Rm, ASR #
+ void arm6D4(u32 opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDRB, 16); }
+// LDRB[T] Rd, [Rn], Rm, ROR #
+ void arm6D6(u32 opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDRB, 16); }
+// STR Rd, [Rn, -Rm, LSL #]
+ void arm700(u32 opcode) { STR_PREDEC(OFFSET_LSL, OP_STR, 32); }
+// STR Rd, [Rn, -Rm, LSR #]
+ void arm702(u32 opcode) { STR_PREDEC(OFFSET_LSR, OP_STR, 32); }
+// STR Rd, [Rn, -Rm, ASR #]
+ void arm704(u32 opcode) { STR_PREDEC(OFFSET_ASR, OP_STR, 32); }
+// STR Rd, [Rn, -Rm, ROR #]
+ void arm706(u32 opcode) { STR_PREDEC(OFFSET_ROR, OP_STR, 32); }
+// LDR Rd, [Rn, -Rm, LSL #]
+ void arm710(u32 opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDR, 32); }
+// LDR Rd, [Rn, -Rm, LSR #]
+ void arm712(u32 opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDR, 32); }
+// LDR Rd, [Rn, -Rm, ASR #]
+ void arm714(u32 opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDR, 32); }
+// LDR Rd, [Rn, -Rm, ROR #]
+ void arm716(u32 opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDR, 32); }
+// STR Rd, [Rn, -Rm, LSL #]!
+ void arm720(u32 opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STR, 32); }
+// STR Rd, [Rn, -Rm, LSR #]!
+ void arm722(u32 opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STR, 32); }
+// STR Rd, [Rn, -Rm, ASR #]!
+ void arm724(u32 opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STR, 32); }
+// STR Rd, [Rn, -Rm, ROR #]!
+ void arm726(u32 opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STR, 32); }
+// LDR Rd, [Rn, -Rm, LSL #]!
+ void arm730(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDR, 32); }
+// LDR Rd, [Rn, -Rm, LSR #]!
+ void arm732(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDR, 32); }
+// LDR Rd, [Rn, -Rm, ASR #]!
+ void arm734(u32 opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDR, 32); }
+// LDR Rd, [Rn, -Rm, ROR #]!
+ void arm736(u32 opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDR, 32); }
+// STRB Rd, [Rn, -Rm, LSL #]
+ void arm740(u32 opcode) { STR_PREDEC(OFFSET_LSL, OP_STRB, 16); }
+// STRB Rd, [Rn, -Rm, LSR #]
+ void arm742(u32 opcode) { STR_PREDEC(OFFSET_LSR, OP_STRB, 16); }
+// STRB Rd, [Rn, -Rm, ASR #]
+ void arm744(u32 opcode) { STR_PREDEC(OFFSET_ASR, OP_STRB, 16); }
+// STRB Rd, [Rn, -Rm, ROR #]
+ void arm746(u32 opcode) { STR_PREDEC(OFFSET_ROR, OP_STRB, 16); }
+// LDRB Rd, [Rn, -Rm, LSL #]
+ void arm750(u32 opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDRB, 16); }
+// LDRB Rd, [Rn, -Rm, LSR #]
+ void arm752(u32 opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDRB, 16); }
+// LDRB Rd, [Rn, -Rm, ASR #]
+ void arm754(u32 opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDRB, 16); }
+// LDRB Rd, [Rn, -Rm, ROR #]
+ void arm756(u32 opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDRB, 16); }
+// STRB Rd, [Rn, -Rm, LSL #]!
+ void arm760(u32 opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STRB, 16); }
+// STRB Rd, [Rn, -Rm, LSR #]!
+ void arm762(u32 opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STRB, 16); }
+// STRB Rd, [Rn, -Rm, ASR #]!
+ void arm764(u32 opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STRB, 16); }
+// STRB Rd, [Rn, -Rm, ROR #]!
+ void arm766(u32 opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STRB, 16); }
+// LDRB Rd, [Rn, -Rm, LSL #]!
+ void arm770(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDRB, 16); }
+// LDRB Rd, [Rn, -Rm, LSR #]!
+ void arm772(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDRB, 16); }
+// LDRB Rd, [Rn, -Rm, ASR #]!
+ void arm774(u32 opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDRB, 16); }
+// LDRB Rd, [Rn, -Rm, ROR #]!
+ void arm776(u32 opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDRB, 16); }
+// STR Rd, [Rn, Rm, LSL #]
+ void arm780(u32 opcode) { STR_PREINC(OFFSET_LSL, OP_STR, 32); }
+// STR Rd, [Rn, Rm, LSR #]
+ void arm782(u32 opcode) { STR_PREINC(OFFSET_LSR, OP_STR, 32); }
+// STR Rd, [Rn, Rm, ASR #]
+ void arm784(u32 opcode) { STR_PREINC(OFFSET_ASR, OP_STR, 32); }
+// STR Rd, [Rn, Rm, ROR #]
+ void arm786(u32 opcode) { STR_PREINC(OFFSET_ROR, OP_STR, 32); }
+// LDR Rd, [Rn, Rm, LSL #]
+ void arm790(u32 opcode) { LDR_PREINC(OFFSET_LSL, OP_LDR, 32); }
+// LDR Rd, [Rn, Rm, LSR #]
+ void arm792(u32 opcode) { LDR_PREINC(OFFSET_LSR, OP_LDR, 32); }
+// LDR Rd, [Rn, Rm, ASR #]
+ void arm794(u32 opcode) { LDR_PREINC(OFFSET_ASR, OP_LDR, 32); }
+// LDR Rd, [Rn, Rm, ROR #]
+ void arm796(u32 opcode) { LDR_PREINC(OFFSET_ROR, OP_LDR, 32); }
+// STR Rd, [Rn, Rm, LSL #]!
+ void arm7A0(u32 opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STR, 32); }
+// STR Rd, [Rn, Rm, LSR #]!
+ void arm7A2(u32 opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STR, 32); }
+// STR Rd, [Rn, Rm, ASR #]!
+ void arm7A4(u32 opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STR, 32); }
+// STR Rd, [Rn, Rm, ROR #]!
+ void arm7A6(u32 opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STR, 32); }
+// LDR Rd, [Rn, Rm, LSL #]!
+ void arm7B0(u32 opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDR, 32); }
+// LDR Rd, [Rn, Rm, LSR #]!
+ void arm7B2(u32 opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDR, 32); }
+// LDR Rd, [Rn, Rm, ASR #]!
+ void arm7B4(u32 opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDR, 32); }
+// LDR Rd, [Rn, Rm, ROR #]!
+ void arm7B6(u32 opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDR, 32); }
+// STRB Rd, [Rn, Rm, LSL #]
+ void arm7C0(u32 opcode) { STR_PREINC(OFFSET_LSL, OP_STRB, 16); }
+// STRB Rd, [Rn, Rm, LSR #]
+ void arm7C2(u32 opcode) { STR_PREINC(OFFSET_LSR, OP_STRB, 16); }
+// STRB Rd, [Rn, Rm, ASR #]
+ void arm7C4(u32 opcode) { STR_PREINC(OFFSET_ASR, OP_STRB, 16); }
+// STRB Rd, [Rn, Rm, ROR #]
+ void arm7C6(u32 opcode) { STR_PREINC(OFFSET_ROR, OP_STRB, 16); }
+// LDRB Rd, [Rn, Rm, LSL #]
+ void arm7D0(u32 opcode) { LDR_PREINC(OFFSET_LSL, OP_LDRB, 16); }
+// LDRB Rd, [Rn, Rm, LSR #]
+ void arm7D2(u32 opcode) { LDR_PREINC(OFFSET_LSR, OP_LDRB, 16); }
+// LDRB Rd, [Rn, Rm, ASR #]
+ void arm7D4(u32 opcode) { LDR_PREINC(OFFSET_ASR, OP_LDRB, 16); }
+// LDRB Rd, [Rn, Rm, ROR #]
+ void arm7D6(u32 opcode) { LDR_PREINC(OFFSET_ROR, OP_LDRB, 16); }
+// STRB Rd, [Rn, Rm, LSL #]!
+ void arm7E0(u32 opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STRB, 16); }
+// STRB Rd, [Rn, Rm, LSR #]!
+ void arm7E2(u32 opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STRB, 16); }
+// STRB Rd, [Rn, Rm, ASR #]!
+ void arm7E4(u32 opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STRB, 16); }
+// STRB Rd, [Rn, Rm, ROR #]!
+ void arm7E6(u32 opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STRB, 16); }
+// LDRB Rd, [Rn, Rm, LSL #]!
+ void arm7F0(u32 opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDRB, 16); }
+// LDRB Rd, [Rn, Rm, LSR #]!
+ void arm7F2(u32 opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDRB, 16); }
+// LDRB Rd, [Rn, Rm, ASR #]!
+ void arm7F4(u32 opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDRB, 16); }
+// LDRB Rd, [Rn, Rm, ROR #]!
+ void arm7F6(u32 opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDRB, 16); }
+
+// STM/LDM ////////////////////////////////////////////////////////////////
+
+#define STM_REG(bit,num) \
+ if (opcode & (1U<<(bit))) { \
+ CPUWriteMemory(address, bus.reg[(num)].I); \
+ int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \
+ clockTicks += 1 + dataticks_value; \
+ count++; \
+ address += 4; \
+ }
+#define STMW_REG(bit,num) \
+ if (opcode & (1U<<(bit))) { \
+ CPUWriteMemory(address, bus.reg[(num)].I); \
+ int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \
+ clockTicks += 1 + dataticks_value; \
+ bus.reg[base].I = temp; \
+ count++; \
+ address += 4; \
+ }
+#define LDM_REG(bit,num) \
+ if (opcode & (1U<<(bit))) { \
+ int dataticks_value; \
+ bus.reg[(num)].I = CPUReadMemory(address); \
+ dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \
+ clockTicks += 1 + dataticks_value; \
+ count++; \
+ address += 4; \
+ }
+#define STM_LOW(STORE_REG) \
+ STORE_REG(0, 0); \
+ STORE_REG(1, 1); \
+ STORE_REG(2, 2); \
+ STORE_REG(3, 3); \
+ STORE_REG(4, 4); \
+ STORE_REG(5, 5); \
+ STORE_REG(6, 6); \
+ STORE_REG(7, 7);
+#define STM_HIGH(STORE_REG) \
+ STORE_REG(8, 8); \
+ STORE_REG(9, 9); \
+ STORE_REG(10, 10); \
+ STORE_REG(11, 11); \
+ STORE_REG(12, 12); \
+ STORE_REG(13, 13); \
+ STORE_REG(14, 14);
+#define STM_HIGH_2(STORE_REG) \
+ if (armMode == 0x11) { \
+ STORE_REG(8, R8_FIQ); \
+ STORE_REG(9, R9_FIQ); \
+ STORE_REG(10, R10_FIQ); \
+ STORE_REG(11, R11_FIQ); \
+ STORE_REG(12, R12_FIQ); \
+ } else { \
+ STORE_REG(8, 8); \
+ STORE_REG(9, 9); \
+ STORE_REG(10, 10); \
+ STORE_REG(11, 11); \
+ STORE_REG(12, 12); \
+ } \
+ if (armMode != 0x10 && armMode != 0x1F) { \
+ STORE_REG(13, R13_USR); \
+ STORE_REG(14, R14_USR); \
+ } else { \
+ STORE_REG(13, 13); \
+ STORE_REG(14, 14); \
+ }
+#define STM_PC \
+ if (opcode & (1U<<15)) { \
+ CPUWriteMemory(address, bus.reg[15].I+4); \
+ int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \
+ clockTicks += 1 + dataticks_value; \
+ count++; \
+ }
+#define STMW_PC \
+ if (opcode & (1U<<15)) { \
+ CPUWriteMemory(address, bus.reg[15].I+4); \
+ int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \
+ clockTicks += 1 + dataticks_value; \
+ bus.reg[base].I = temp; \
+ count++; \
+ }
+#define LDM_LOW \
+ LDM_REG(0, 0); \
+ LDM_REG(1, 1); \
+ LDM_REG(2, 2); \
+ LDM_REG(3, 3); \
+ LDM_REG(4, 4); \
+ LDM_REG(5, 5); \
+ LDM_REG(6, 6); \
+ LDM_REG(7, 7);
+#define LDM_HIGH \
+ LDM_REG(8, 8); \
+ LDM_REG(9, 9); \
+ LDM_REG(10, 10); \
+ LDM_REG(11, 11); \
+ LDM_REG(12, 12); \
+ LDM_REG(13, 13); \
+ LDM_REG(14, 14);
+#define LDM_HIGH_2 \
+ if (armMode == 0x11) { \
+ LDM_REG(8, R8_FIQ); \
+ LDM_REG(9, R9_FIQ); \
+ LDM_REG(10, R10_FIQ); \
+ LDM_REG(11, R11_FIQ); \
+ LDM_REG(12, R12_FIQ); \
+ } else { \
+ LDM_REG(8, 8); \
+ LDM_REG(9, 9); \
+ LDM_REG(10, 10); \
+ LDM_REG(11, 11); \
+ LDM_REG(12, 12); \
+ } \
+ if (armMode != 0x10 && armMode != 0x1F) { \
+ LDM_REG(13, R13_USR); \
+ LDM_REG(14, R14_USR); \
+ } else { \
+ LDM_REG(13, 13); \
+ LDM_REG(14, 14); \
+ }
+#define STM_ALL \
+ STM_LOW(STM_REG); \
+ STM_HIGH(STM_REG); \
+ STM_PC;
+#define STMW_ALL \
+ STM_LOW(STMW_REG); \
+ STM_HIGH(STMW_REG); \
+ STMW_PC;
+#define LDM_ALL \
+ LDM_LOW; \
+ LDM_HIGH; \
+ if (opcode & (1U<<15)) { \
+ bus.reg[15].I = CPUReadMemory(address); \
+ int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \
+ clockTicks += 1 + dataticks_value; \
+ count++; \
+ } \
+ if (opcode & (1U<<15)) { \
+ bus.armNextPC = bus.reg[15].I; \
+ bus.reg[15].I += 4; \
+ ARM_PREFETCH; \
+ clockTicks += 1 + codeTicksAccessSeq32(bus.armNextPC);\
+ }
+#define STM_ALL_2 \
+ STM_LOW(STM_REG); \
+ STM_HIGH_2(STM_REG); \
+ STM_PC;
+#define STMW_ALL_2 \
+ STM_LOW(STMW_REG); \
+ STM_HIGH_2(STMW_REG); \
+ STMW_PC;
+#define LDM_ALL_2 \
+ LDM_LOW; \
+ if (opcode & (1U<<15)) { \
+ LDM_HIGH; \
+ bus.reg[15].I = CPUReadMemory(address); \
+ int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \
+ clockTicks += 1 + dataticks_value; \
+ count++; \
+ } else { \
+ LDM_HIGH_2; \
+ }
+#define LDM_ALL_2B \
+ if (opcode & (1U<<15)) { \
+ if(armMode != (bus.reg[17].I & 0x1F)) \
+ CPUSwitchMode(bus.reg[17].I & 0x1F, false, true); \
+ if (armState) { \
+ bus.armNextPC = bus.reg[15].I & 0xFFFFFFFC; \
+ bus.reg[15].I = bus.armNextPC + 4; \
+ ARM_PREFETCH; \
+ } else { \
+ bus.armNextPC = bus.reg[15].I & 0xFFFFFFFE; \
+ bus.reg[15].I = bus.armNextPC + 2; \
+ THUMB_PREFETCH; \
+ } \
+ clockTicks += 1 + codeTicksAccessSeq32(bus.armNextPC);\
+ }
+
+
+// STMDA Rn, {Rlist}
+ void arm800(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = (temp + 4) & 0xFFFFFFFC;
+ int count = 0;
+ STM_ALL;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMDA Rn, {Rlist}
+ void arm810(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = (temp + 4) & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// STMDA Rn!, {Rlist}
+ void arm820(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = (temp+4) & 0xFFFFFFFC;
+ int count = 0;
+ STMW_ALL;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMDA Rn!, {Rlist}
+ void arm830(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = (temp + 4) & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+ if (!(opcode & (1U << base)))
+ bus.reg[base].I = temp;
+}
+
+// STMDA Rn, {Rlist}^
+ void arm840(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = (temp+4) & 0xFFFFFFFC;
+ int count = 0;
+ STM_ALL_2;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMDA Rn, {Rlist}^
+ void arm850(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = (temp + 4) & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL_2;
+ LDM_ALL_2B;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// STMDA Rn!, {Rlist}^
+ void arm860(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = (temp+4) & 0xFFFFFFFC;
+ int count = 0;
+ STMW_ALL_2;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMDA Rn!, {Rlist}^
+ void arm870(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = (temp + 4) & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL_2;
+ if (!(opcode & (1U << base)))
+ bus.reg[base].I = temp;
+ LDM_ALL_2B;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// STMIA Rn, {Rlist}
+ void arm880(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 address = bus.reg[base].I & 0xFFFFFFFC;
+ int count = 0;
+ STM_ALL;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMIA Rn, {Rlist}
+ void arm890(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 address = bus.reg[base].I & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// STMIA Rn!, {Rlist}
+ void arm8A0(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 address = bus.reg[base].I & 0xFFFFFFFC;
+ int count = 0;
+ u32 temp = bus.reg[base].I +
+ 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]);
+ STMW_ALL;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMIA Rn!, {Rlist}
+ void arm8B0(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I +
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = bus.reg[base].I & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+ if (!(opcode & (1U << base)))
+ bus.reg[base].I = temp;
+}
+
+// STMIA Rn, {Rlist}^
+ void arm8C0(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 address = bus.reg[base].I & 0xFFFFFFFC;
+ int count = 0;
+ STM_ALL_2;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMIA Rn, {Rlist}^
+ void arm8D0(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 address = bus.reg[base].I & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL_2;
+ LDM_ALL_2B;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// STMIA Rn!, {Rlist}^
+ void arm8E0(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 address = bus.reg[base].I & 0xFFFFFFFC;
+ int count = 0;
+ u32 temp = bus.reg[base].I +
+ 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]);
+ STMW_ALL_2;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMIA Rn!, {Rlist}^
+ void arm8F0(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I +
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = bus.reg[base].I & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL_2;
+ if (!(opcode & (1U << base)))
+ bus.reg[base].I = temp;
+ LDM_ALL_2B;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// STMDB Rn, {Rlist}
+ void arm900(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = temp & 0xFFFFFFFC;
+ int count = 0;
+ STM_ALL;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMDB Rn, {Rlist}
+ void arm910(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = temp & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// STMDB Rn!, {Rlist}
+ void arm920(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = temp & 0xFFFFFFFC;
+ int count = 0;
+ STMW_ALL;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMDB Rn!, {Rlist}
+ void arm930(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = temp & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+ if (!(opcode & (1U << base)))
+ bus.reg[base].I = temp;
+}
+
+// STMDB Rn, {Rlist}^
+ void arm940(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = temp & 0xFFFFFFFC;
+ int count = 0;
+ STM_ALL_2;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMDB Rn, {Rlist}^
+ void arm950(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = temp & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL_2;
+ LDM_ALL_2B;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// STMDB Rn!, {Rlist}^
+ void arm960(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = temp & 0xFFFFFFFC;
+ int count = 0;
+ STMW_ALL_2;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMDB Rn!, {Rlist}^
+ void arm970(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I -
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = temp & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL_2;
+ if (!(opcode & (1U << base)))
+ bus.reg[base].I = temp;
+ LDM_ALL_2B;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// STMIB Rn, {Rlist}
+ void arm980(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC;
+ int count = 0;
+ STM_ALL;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMIB Rn, {Rlist}
+ void arm990(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// STMIB Rn!, {Rlist}
+ void arm9A0(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC;
+ int count = 0;
+ u32 temp = bus.reg[base].I +
+ 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]);
+ STMW_ALL;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMIB Rn!, {Rlist}
+ void arm9B0(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I +
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+ if (!(opcode & (1U << base)))
+ bus.reg[base].I = temp;
+}
+
+// STMIB Rn, {Rlist}^
+ void arm9C0(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC;
+ int count = 0;
+ STM_ALL_2;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMIB Rn, {Rlist}^
+ void arm9D0(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL_2;
+ LDM_ALL_2B;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// STMIB Rn!, {Rlist}^
+ void arm9E0(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC;
+ int count = 0;
+ u32 temp = bus.reg[base].I +
+ 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]);
+ STMW_ALL_2;
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// LDMIB Rn!, {Rlist}^
+ void arm9F0(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int base = (opcode & 0x000F0000) >> 16;
+ u32 temp = bus.reg[base].I +
+ 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
+ u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC;
+ int count = 0;
+ LDM_ALL_2;
+ if (!(opcode & (1U << base)))
+ bus.reg[base].I = temp;
+ LDM_ALL_2B;
+ clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32);
+}
+
+// B/BL/SWI and (unimplemented) coproc support ////////////////////////////
+
+// B
+ void armA00(u32 opcode)
+{
+ int codeTicksVal = 0;
+ int ct = 0;
+ int offset = opcode & 0x00FFFFFF;
+ if (offset & 0x00800000)
+ offset |= 0xFF000000; // negative offset
+ bus.reg[15].I += offset<<2;
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 4;
+ ARM_PREFETCH;
+
+ codeTicksVal = codeTicksAccessSeq32(bus.armNextPC);
+ ct = codeTicksVal + 3;
+ ct += 2 + codeTicksAccess(bus.armNextPC, BITS_32) + codeTicksVal;
+
+ bus.busPrefetchCount = 0;
+ clockTicks = ct;
+}
+
+// BL
+ void armB00(u32 opcode)
+{
+ int codeTicksVal = 0;
+ int ct = 0;
+
+ int offset = opcode & 0x00FFFFFF;
+ if (offset & 0x00800000)
+ offset |= 0xFF000000; // negative offset
+ bus.reg[14].I = bus.reg[15].I - 4;
+ bus.reg[15].I += offset<<2;
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 4;
+ ARM_PREFETCH;
+
+ codeTicksVal = codeTicksAccessSeq32(bus.armNextPC);
+ ct = codeTicksVal + 3;
+ ct += 2 + codeTicksAccess(bus.armNextPC, BITS_32) + codeTicksVal;
+
+ bus.busPrefetchCount = 0;
+ clockTicks = ct;
+}
+
+#define armE01 armUnknownInsn
+
+// SWI
+ void armF00(u32 opcode)
+{
+ int codeTicksVal = 0;
+ int ct = 0;
+
+ codeTicksVal = codeTicksAccessSeq32(bus.armNextPC);
+ ct = codeTicksVal + 3;
+ ct += 2 + codeTicksAccess(bus.armNextPC, BITS_32) + codeTicksVal;
+
+ bus.busPrefetchCount = 0;
+
+ clockTicks = ct;
+ CPUSoftwareInterrupt(opcode & 0x00FFFFFF);
+
+}
+
+// Instruction table //////////////////////////////////////////////////////
+
+static void (Gigazoid::*const armInsnTable[4096])(u32 opcode);
+
+// Wrapper routine (execution loop) ///////////////////////////////////////
+int armExecute (void)
+{
+ CACHE_PREFETCH(clockTicks);
+
+ u32 cond1;
+ u32 cond2;
+
+ int ct = 0;
+
+ do
+ {
+
+ clockTicks = 0;
+
+ if ((bus.armNextPC & 0x0803FFFF) == 0x08020000)
+ bus.busPrefetchCount = 0x100;
+
+ u32 opcode = cpuPrefetch[0];
+ cpuPrefetch[0] = cpuPrefetch[1];
+
+ bus.busPrefetch = false;
+ int32_t busprefetch_mask = ((bus.busPrefetchCount & 0xFFFFFE00) | -(bus.busPrefetchCount & 0xFFFFFE00)) >> 31;
+ bus.busPrefetchCount = (0x100 | (bus.busPrefetchCount & 0xFF) & busprefetch_mask) | (bus.busPrefetchCount & ~busprefetch_mask);
+#if 0
+ if (bus.busPrefetchCount & 0xFFFFFE00)
+ bus.busPrefetchCount = 0x100 | (bus.busPrefetchCount & 0xFF);
+#endif
+
+
+ int oldArmNextPC = bus.armNextPC;
+
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 4;
+ ARM_PREFETCH_NEXT;
+
+ int cond = opcode >> 28;
+ bool cond_res = true;
+ if (cond != 0x0E) { // most opcodes are AL (always)
+ switch(cond) {
+ case 0x00: // EQ
+ cond_res = Z_FLAG;
+ break;
+ case 0x01: // NE
+ cond_res = !Z_FLAG;
+ break;
+ case 0x02: // CS
+ cond_res = C_FLAG;
+ break;
+ case 0x03: // CC
+ cond_res = !C_FLAG;
+ break;
+ case 0x04: // MI
+ cond_res = N_FLAG;
+ break;
+ case 0x05: // PL
+ cond_res = !N_FLAG;
+ break;
+ case 0x06: // VS
+ cond_res = V_FLAG;
+ break;
+ case 0x07: // VC
+ cond_res = !V_FLAG;
+ break;
+ case 0x08: // HI
+ cond_res = C_FLAG && !Z_FLAG;
+ break;
+ case 0x09: // LS
+ cond_res = !C_FLAG || Z_FLAG;
+ break;
+ case 0x0A: // GE
+ cond_res = N_FLAG == V_FLAG;
+ break;
+ case 0x0B: // LT
+ cond_res = N_FLAG != V_FLAG;
+ break;
+ case 0x0C: // GT
+ cond_res = !Z_FLAG &&(N_FLAG == V_FLAG);
+ break;
+ case 0x0D: // LE
+ cond_res = Z_FLAG || (N_FLAG != V_FLAG);
+ break;
+ case 0x0E: // AL (impossible, checked above)
+ cond_res = true;
+ break;
+ case 0x0F:
+ default:
+ // ???
+ cond_res = false;
+ break;
+ }
+ }
+
+ if (cond_res)
+ {
+ cond1 = (opcode>>16)&0xFF0;
+ cond2 = (opcode>>4)&0x0F;
+
+ (this->*armInsnTable[(cond1| cond2)])(opcode);
+
+ }
+ ct = clockTicks;
+
+ if (ct < 0)
+ return 0;
+
+ /// better pipelining
+
+ if (ct == 0)
+ clockTicks = 1 + codeTicksAccessSeq32(oldArmNextPC);
+
+ cpuTotalTicks += clockTicks;
+
+#ifdef USE_SWITICKS
+ } while (cpuTotalTicks> 31)
+#define POS(i) ((~(i)) >> 31)
+
+// C core
+#ifndef ADDCARRY
+ #define ADDCARRY(a, b, c) \
+ C_FLAG = ((NEG(a) & NEG(b)) |\
+ (NEG(a) & POS(c)) |\
+ (NEG(b) & POS(c))) ? true : false;
+#endif
+
+#ifndef ADDOVERFLOW
+ #define ADDOVERFLOW(a, b, c) \
+ V_FLAG = ((NEG(a) & NEG(b) & POS(c)) |\
+ (POS(a) & POS(b) & NEG(c))) ? true : false;
+#endif
+
+#ifndef SUBCARRY
+ #define SUBCARRY(a, b, c) \
+ C_FLAG = ((NEG(a) & POS(b)) |\
+ (NEG(a) & POS(c)) |\
+ (POS(b) & POS(c))) ? true : false;
+#endif
+
+#ifndef SUBOVERFLOW
+ #define SUBOVERFLOW(a, b, c)\
+ V_FLAG = ((NEG(a) & POS(b) & POS(c)) |\
+ (POS(a) & NEG(b) & NEG(c))) ? true : false;
+#endif
+
+#ifndef ADD_RD_RS_RN
+ #define ADD_RD_RS_RN(N) \
+ {\
+ u32 lhs = bus.reg[source].I;\
+ u32 rhs = bus.reg[N].I;\
+ u32 res = lhs + rhs;\
+ bus.reg[dest].I = res;\
+ Z_FLAG = (res == 0) ? true : false;\
+ N_FLAG = NEG(res) ? true : false;\
+ ADDCARRY(lhs, rhs, res);\
+ ADDOVERFLOW(lhs, rhs, res);\
+ }
+#endif
+
+#ifndef ADD_RD_RS_O3
+ #define ADD_RD_RS_O3(N) \
+ {\
+ u32 lhs = bus.reg[source].I;\
+ u32 rhs = N;\
+ u32 res = lhs + rhs;\
+ bus.reg[dest].I = res;\
+ Z_FLAG = (res == 0) ? true : false;\
+ N_FLAG = NEG(res) ? true : false;\
+ ADDCARRY(lhs, rhs, res);\
+ ADDOVERFLOW(lhs, rhs, res);\
+ }
+#endif
+
+#ifndef ADD_RD_RS_O3_0
+# define ADD_RD_RS_O3_0 ADD_RD_RS_O3
+#endif
+
+#ifndef ADD_RN_O8
+ #define ADD_RN_O8(d) \
+ {\
+ u32 lhs = bus.reg[(d)].I;\
+ u32 rhs = (opcode & 255);\
+ u32 res = lhs + rhs;\
+ bus.reg[(d)].I = res;\
+ Z_FLAG = (res == 0) ? true : false;\
+ N_FLAG = NEG(res) ? true : false;\
+ ADDCARRY(lhs, rhs, res);\
+ ADDOVERFLOW(lhs, rhs, res);\
+ }
+#endif
+
+#ifndef CMN_RD_RS
+ #define CMN_RD_RS \
+ {\
+ u32 lhs = bus.reg[dest].I;\
+ u32 rhs = value;\
+ u32 res = lhs + rhs;\
+ Z_FLAG = (res == 0) ? true : false;\
+ N_FLAG = NEG(res) ? true : false;\
+ ADDCARRY(lhs, rhs, res);\
+ ADDOVERFLOW(lhs, rhs, res);\
+ }
+#endif
+
+#ifndef ADC_RD_RS
+ #define ADC_RD_RS \
+ {\
+ u32 lhs = bus.reg[dest].I;\
+ u32 rhs = value;\
+ u32 res = lhs + rhs + (u32)C_FLAG;\
+ bus.reg[dest].I = res;\
+ Z_FLAG = (res == 0) ? true : false;\
+ N_FLAG = NEG(res) ? true : false;\
+ ADDCARRY(lhs, rhs, res);\
+ ADDOVERFLOW(lhs, rhs, res);\
+ }
+#endif
+
+#ifndef SUB_RD_RS_RN
+ #define SUB_RD_RS_RN(N) \
+ {\
+ u32 lhs = bus.reg[source].I;\
+ u32 rhs = bus.reg[N].I;\
+ u32 res = lhs - rhs;\
+ bus.reg[dest].I = res;\
+ Z_FLAG = (res == 0) ? true : false;\
+ N_FLAG = NEG(res) ? true : false;\
+ SUBCARRY(lhs, rhs, res);\
+ SUBOVERFLOW(lhs, rhs, res);\
+ }
+#endif
+
+#ifndef SUB_RD_RS_O3
+ #define SUB_RD_RS_O3(N) \
+ {\
+ u32 lhs = bus.reg[source].I;\
+ u32 rhs = N;\
+ u32 res = lhs - rhs;\
+ bus.reg[dest].I = res;\
+ Z_FLAG = (res == 0) ? true : false;\
+ N_FLAG = NEG(res) ? true : false;\
+ SUBCARRY(lhs, rhs, res);\
+ SUBOVERFLOW(lhs, rhs, res);\
+ }
+#endif
+
+#ifndef SUB_RD_RS_O3_0
+# define SUB_RD_RS_O3_0 SUB_RD_RS_O3
+#endif
+#ifndef SUB_RN_O8
+ #define SUB_RN_O8(d) \
+ {\
+ u32 lhs = bus.reg[(d)].I;\
+ u32 rhs = (opcode & 255);\
+ u32 res = lhs - rhs;\
+ bus.reg[(d)].I = res;\
+ Z_FLAG = (res == 0) ? true : false;\
+ N_FLAG = NEG(res) ? true : false;\
+ SUBCARRY(lhs, rhs, res);\
+ SUBOVERFLOW(lhs, rhs, res);\
+ }
+#endif
+#ifndef MOV_RN_O8
+ #define MOV_RN_O8(d) \
+ {\
+ u32 val;\
+ val = (opcode & 255);\
+ bus.reg[d].I = val;\
+ N_FLAG = false;\
+ Z_FLAG = (val ? false : true);\
+ }
+#endif
+#ifndef CMP_RN_O8
+ #define CMP_RN_O8(d) \
+ {\
+ u32 lhs = bus.reg[(d)].I;\
+ u32 rhs = (opcode & 255);\
+ u32 res = lhs - rhs;\
+ Z_FLAG = (res == 0) ? true : false;\
+ N_FLAG = NEG(res) ? true : false;\
+ SUBCARRY(lhs, rhs, res);\
+ SUBOVERFLOW(lhs, rhs, res);\
+ }
+#endif
+#ifndef SBC_RD_RS
+ #define SBC_RD_RS \
+ {\
+ u32 lhs = bus.reg[dest].I;\
+ u32 rhs = value;\
+ u32 res = lhs - rhs - !((u32)C_FLAG);\
+ bus.reg[dest].I = res;\
+ Z_FLAG = (res == 0) ? true : false;\
+ N_FLAG = NEG(res) ? true : false;\
+ SUBCARRY(lhs, rhs, res);\
+ SUBOVERFLOW(lhs, rhs, res);\
+ }
+#endif
+#ifndef LSL_RD_RM_I5
+ #define LSL_RD_RM_I5 \
+ {\
+ C_FLAG = (bus.reg[source].I >> (32 - shift)) & 1 ? true : false;\
+ value = bus.reg[source].I << shift;\
+ }
+#endif
+#ifndef LSL_RD_RS
+ #define LSL_RD_RS \
+ {\
+ C_FLAG = (bus.reg[dest].I >> (32 - value)) & 1 ? true : false;\
+ value = bus.reg[dest].I << value;\
+ }
+#endif
+#ifndef LSR_RD_RM_I5
+ #define LSR_RD_RM_I5 \
+ {\
+ C_FLAG = (bus.reg[source].I >> (shift - 1)) & 1 ? true : false;\
+ value = bus.reg[source].I >> shift;\
+ }
+#endif
+#ifndef LSR_RD_RS
+ #define LSR_RD_RS \
+ {\
+ C_FLAG = (bus.reg[dest].I >> (value - 1)) & 1 ? true : false;\
+ value = bus.reg[dest].I >> value;\
+ }
+#endif
+#ifndef ASR_RD_RM_I5
+ #define ASR_RD_RM_I5 \
+ {\
+ C_FLAG = ((s32)bus.reg[source].I >> (int)(shift - 1)) & 1 ? true : false;\
+ value = (s32)bus.reg[source].I >> (int)shift;\
+ }
+#endif
+#ifndef ASR_RD_RS
+ #define ASR_RD_RS \
+ {\
+ C_FLAG = ((s32)bus.reg[dest].I >> (int)(value - 1)) & 1 ? true : false;\
+ value = (s32)bus.reg[dest].I >> (int)value;\
+ }
+#endif
+#ifndef ROR_RD_RS
+ #define ROR_RD_RS \
+ {\
+ C_FLAG = (bus.reg[dest].I >> (value - 1)) & 1 ? true : false;\
+ value = ((bus.reg[dest].I << (32 - value)) |\
+ (bus.reg[dest].I >> value));\
+ }
+#endif
+#ifndef NEG_RD_RS
+ #define NEG_RD_RS \
+ {\
+ u32 lhs = bus.reg[source].I;\
+ u32 rhs = 0;\
+ u32 res = rhs - lhs;\
+ bus.reg[dest].I = res;\
+ Z_FLAG = (res == 0) ? true : false;\
+ N_FLAG = NEG(res) ? true : false;\
+ SUBCARRY(rhs, lhs, res);\
+ SUBOVERFLOW(rhs, lhs, res);\
+ }
+#endif
+#ifndef CMP_RD_RS
+ #define CMP_RD_RS \
+ {\
+ u32 lhs = bus.reg[dest].I;\
+ u32 rhs = value;\
+ u32 res = lhs - rhs;\
+ Z_FLAG = (res == 0) ? true : false;\
+ N_FLAG = NEG(res) ? true : false;\
+ SUBCARRY(lhs, rhs, res);\
+ SUBOVERFLOW(lhs, rhs, res);\
+ }
+#endif
+#ifndef IMM5_INSN
+ #define IMM5_INSN(OP,N) \
+ int dest = opcode & 0x07;\
+ int source = (opcode >> 3) & 0x07;\
+ u32 value;\
+ OP(N);\
+ bus.reg[dest].I = value;\
+ N_FLAG = (value & 0x80000000 ? true : false);\
+ Z_FLAG = (value ? false : true);
+ #define IMM5_INSN_0(OP) \
+ int dest = opcode & 0x07;\
+ int source = (opcode >> 3) & 0x07;\
+ u32 value;\
+ OP;\
+ bus.reg[dest].I = value;\
+ N_FLAG = (value & 0x80000000 ? true : false);\
+ Z_FLAG = (value ? false : true);
+ #define IMM5_LSL(N) \
+ int shift = N;\
+ LSL_RD_RM_I5;
+ #define IMM5_LSL_0 \
+ value = bus.reg[source].I;
+ #define IMM5_LSR(N) \
+ int shift = N;\
+ LSR_RD_RM_I5;
+ #define IMM5_LSR_0 \
+ C_FLAG = bus.reg[source].I & 0x80000000 ? true : false;\
+ value = 0;
+ #define IMM5_ASR(N) \
+ int shift = N;\
+ ASR_RD_RM_I5;
+ #define IMM5_ASR_0 \
+ if(bus.reg[source].I & 0x80000000) {\
+ value = 0xFFFFFFFF;\
+ C_FLAG = true;\
+ } else {\
+ value = 0;\
+ C_FLAG = false;\
+ }
+#endif
+#ifndef THREEARG_INSN
+ #define THREEARG_INSN(OP,N) \
+ int dest = opcode & 0x07; \
+ int source = (opcode >> 3) & 0x07; \
+ OP(N);
+#endif
+
+// Shift instructions /////////////////////////////////////////////////////
+
+#define DEFINE_IMM5_INSN(OP,BASE) \
+ void thumb##BASE##_00(u32 opcode) { IMM5_INSN_0(OP##_0); } \
+ void thumb##BASE##_01(u32 opcode) { IMM5_INSN(OP, 1); } \
+ void thumb##BASE##_02(u32 opcode) { IMM5_INSN(OP, 2); } \
+ void thumb##BASE##_03(u32 opcode) { IMM5_INSN(OP, 3); } \
+ void thumb##BASE##_04(u32 opcode) { IMM5_INSN(OP, 4); } \
+ void thumb##BASE##_05(u32 opcode) { IMM5_INSN(OP, 5); } \
+ void thumb##BASE##_06(u32 opcode) { IMM5_INSN(OP, 6); } \
+ void thumb##BASE##_07(u32 opcode) { IMM5_INSN(OP, 7); } \
+ void thumb##BASE##_08(u32 opcode) { IMM5_INSN(OP, 8); } \
+ void thumb##BASE##_09(u32 opcode) { IMM5_INSN(OP, 9); } \
+ void thumb##BASE##_0A(u32 opcode) { IMM5_INSN(OP,10); } \
+ void thumb##BASE##_0B(u32 opcode) { IMM5_INSN(OP,11); } \
+ void thumb##BASE##_0C(u32 opcode) { IMM5_INSN(OP,12); } \
+ void thumb##BASE##_0D(u32 opcode) { IMM5_INSN(OP,13); } \
+ void thumb##BASE##_0E(u32 opcode) { IMM5_INSN(OP,14); } \
+ void thumb##BASE##_0F(u32 opcode) { IMM5_INSN(OP,15); } \
+ void thumb##BASE##_10(u32 opcode) { IMM5_INSN(OP,16); } \
+ void thumb##BASE##_11(u32 opcode) { IMM5_INSN(OP,17); } \
+ void thumb##BASE##_12(u32 opcode) { IMM5_INSN(OP,18); } \
+ void thumb##BASE##_13(u32 opcode) { IMM5_INSN(OP,19); } \
+ void thumb##BASE##_14(u32 opcode) { IMM5_INSN(OP,20); } \
+ void thumb##BASE##_15(u32 opcode) { IMM5_INSN(OP,21); } \
+ void thumb##BASE##_16(u32 opcode) { IMM5_INSN(OP,22); } \
+ void thumb##BASE##_17(u32 opcode) { IMM5_INSN(OP,23); } \
+ void thumb##BASE##_18(u32 opcode) { IMM5_INSN(OP,24); } \
+ void thumb##BASE##_19(u32 opcode) { IMM5_INSN(OP,25); } \
+ void thumb##BASE##_1A(u32 opcode) { IMM5_INSN(OP,26); } \
+ void thumb##BASE##_1B(u32 opcode) { IMM5_INSN(OP,27); } \
+ void thumb##BASE##_1C(u32 opcode) { IMM5_INSN(OP,28); } \
+ void thumb##BASE##_1D(u32 opcode) { IMM5_INSN(OP,29); } \
+ void thumb##BASE##_1E(u32 opcode) { IMM5_INSN(OP,30); } \
+ void thumb##BASE##_1F(u32 opcode) { IMM5_INSN(OP,31); }
+
+// LSL Rd, Rm, #Imm 5
+DEFINE_IMM5_INSN(IMM5_LSL,00)
+// LSR Rd, Rm, #Imm 5
+DEFINE_IMM5_INSN(IMM5_LSR,08)
+// ASR Rd, Rm, #Imm 5
+DEFINE_IMM5_INSN(IMM5_ASR,10)
+
+// 3-argument ADD/SUB /////////////////////////////////////////////////////
+
+#define DEFINE_REG3_INSN(OP,BASE) \
+ void thumb##BASE##_0(u32 opcode) { THREEARG_INSN(OP,0); } \
+ void thumb##BASE##_1(u32 opcode) { THREEARG_INSN(OP,1); } \
+ void thumb##BASE##_2(u32 opcode) { THREEARG_INSN(OP,2); } \
+ void thumb##BASE##_3(u32 opcode) { THREEARG_INSN(OP,3); } \
+ void thumb##BASE##_4(u32 opcode) { THREEARG_INSN(OP,4); } \
+ void thumb##BASE##_5(u32 opcode) { THREEARG_INSN(OP,5); } \
+ void thumb##BASE##_6(u32 opcode) { THREEARG_INSN(OP,6); } \
+ void thumb##BASE##_7(u32 opcode) { THREEARG_INSN(OP,7); }
+
+#define DEFINE_IMM3_INSN(OP,BASE) \
+ void thumb##BASE##_0(u32 opcode) { THREEARG_INSN(OP##_0,0); } \
+ void thumb##BASE##_1(u32 opcode) { THREEARG_INSN(OP,1); } \
+ void thumb##BASE##_2(u32 opcode) { THREEARG_INSN(OP,2); } \
+ void thumb##BASE##_3(u32 opcode) { THREEARG_INSN(OP,3); } \
+ void thumb##BASE##_4(u32 opcode) { THREEARG_INSN(OP,4); } \
+ void thumb##BASE##_5(u32 opcode) { THREEARG_INSN(OP,5); } \
+ void thumb##BASE##_6(u32 opcode) { THREEARG_INSN(OP,6); } \
+ void thumb##BASE##_7(u32 opcode) { THREEARG_INSN(OP,7); }
+
+// ADD Rd, Rs, Rn
+DEFINE_REG3_INSN(ADD_RD_RS_RN,18)
+// SUB Rd, Rs, Rn
+DEFINE_REG3_INSN(SUB_RD_RS_RN,1A)
+// ADD Rd, Rs, #Offset3
+DEFINE_IMM3_INSN(ADD_RD_RS_O3,1C)
+// SUB Rd, Rs, #Offset3
+DEFINE_IMM3_INSN(SUB_RD_RS_O3,1E)
+
+// MOV/CMP/ADD/SUB immediate //////////////////////////////////////////////
+
+// MOV R0, #Offset8
+ void thumb20(u32 opcode) { MOV_RN_O8(0); }
+// MOV R1, #Offset8
+ void thumb21(u32 opcode) { MOV_RN_O8(1); }
+// MOV R2, #Offset8
+ void thumb22(u32 opcode) { MOV_RN_O8(2); }
+// MOV R3, #Offset8
+ void thumb23(u32 opcode) { MOV_RN_O8(3); }
+// MOV R4, #Offset8
+ void thumb24(u32 opcode) { MOV_RN_O8(4); }
+// MOV R5, #Offset8
+ void thumb25(u32 opcode) { MOV_RN_O8(5); }
+// MOV R6, #Offset8
+ void thumb26(u32 opcode) { MOV_RN_O8(6); }
+// MOV R7, #Offset8
+ void thumb27(u32 opcode) { MOV_RN_O8(7); }
+
+// CMP R0, #Offset8
+ void thumb28(u32 opcode) { CMP_RN_O8(0); }
+// CMP R1, #Offset8
+ void thumb29(u32 opcode) { CMP_RN_O8(1); }
+// CMP R2, #Offset8
+ void thumb2A(u32 opcode) { CMP_RN_O8(2); }
+// CMP R3, #Offset8
+ void thumb2B(u32 opcode) { CMP_RN_O8(3); }
+// CMP R4, #Offset8
+ void thumb2C(u32 opcode) { CMP_RN_O8(4); }
+// CMP R5, #Offset8
+ void thumb2D(u32 opcode) { CMP_RN_O8(5); }
+// CMP R6, #Offset8
+ void thumb2E(u32 opcode) { CMP_RN_O8(6); }
+// CMP R7, #Offset8
+ void thumb2F(u32 opcode) { CMP_RN_O8(7); }
+
+// ADD R0,#Offset8
+ void thumb30(u32 opcode) { ADD_RN_O8(0); }
+// ADD R1,#Offset8
+ void thumb31(u32 opcode) { ADD_RN_O8(1); }
+// ADD R2,#Offset8
+ void thumb32(u32 opcode) { ADD_RN_O8(2); }
+// ADD R3,#Offset8
+ void thumb33(u32 opcode) { ADD_RN_O8(3); }
+// ADD R4,#Offset8
+ void thumb34(u32 opcode) { ADD_RN_O8(4); }
+// ADD R5,#Offset8
+ void thumb35(u32 opcode) { ADD_RN_O8(5); }
+// ADD R6,#Offset8
+ void thumb36(u32 opcode) { ADD_RN_O8(6); }
+// ADD R7,#Offset8
+ void thumb37(u32 opcode) { ADD_RN_O8(7); }
+
+// SUB R0,#Offset8
+ void thumb38(u32 opcode) { SUB_RN_O8(0); }
+// SUB R1,#Offset8
+ void thumb39(u32 opcode) { SUB_RN_O8(1); }
+// SUB R2,#Offset8
+ void thumb3A(u32 opcode) { SUB_RN_O8(2); }
+// SUB R3,#Offset8
+ void thumb3B(u32 opcode) { SUB_RN_O8(3); }
+// SUB R4,#Offset8
+ void thumb3C(u32 opcode) { SUB_RN_O8(4); }
+// SUB R5,#Offset8
+ void thumb3D(u32 opcode) { SUB_RN_O8(5); }
+// SUB R6,#Offset8
+ void thumb3E(u32 opcode) { SUB_RN_O8(6); }
+// SUB R7,#Offset8
+ void thumb3F(u32 opcode) { SUB_RN_O8(7); }
+
+// ALU operations /////////////////////////////////////////////////////////
+
+// AND Rd, Rs
+ void thumb40_0(u32 opcode)
+{
+ int dest = opcode & 7;
+ u32 val = (bus.reg[dest].I & bus.reg[(opcode >> 3)&7].I);
+
+ //bus.reg[dest].I &= bus.reg[(opcode >> 3)&7].I;
+ N_FLAG = val & 0x80000000 ? true : false;
+ Z_FLAG = val ? false : true;
+
+ bus.reg[dest].I = val;
+
+}
+
+// EOR Rd, Rs
+ void thumb40_1(u32 opcode)
+{
+ int dest = opcode & 7;
+ bus.reg[dest].I ^= bus.reg[(opcode >> 3)&7].I;
+ N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false;
+ Z_FLAG = bus.reg[dest].I ? false : true;
+}
+
+// LSL Rd, Rs
+ void thumb40_2(u32 opcode)
+{
+ int dest = opcode & 7;
+ u32 value = bus.reg[(opcode >> 3)&7].B.B0;
+ u32 val = value;
+ if(val) {
+ if(val == 32) {
+ value = 0;
+ C_FLAG = (bus.reg[dest].I & 1 ? true : false);
+ } else if(val < 32) {
+ LSL_RD_RS;
+ } else {
+ value = 0;
+ C_FLAG = false;
+ }
+ bus.reg[dest].I = value;
+ }
+ N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false;
+ Z_FLAG = bus.reg[dest].I ? false : true;
+ clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2;
+}
+
+// LSR Rd, Rs
+ void thumb40_3(u32 opcode)
+{
+ int dest = opcode & 7;
+ u32 value = bus.reg[(opcode >> 3)&7].B.B0;
+ u32 val = value;
+ if(val) {
+ if(val == 32) {
+ value = 0;
+ C_FLAG = (bus.reg[dest].I & 0x80000000 ? true : false);
+ } else if(val < 32) {
+ LSR_RD_RS;
+ } else {
+ value = 0;
+ C_FLAG = false;
+ }
+ bus.reg[dest].I = value;
+ }
+ N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false;
+ Z_FLAG = bus.reg[dest].I ? false : true;
+ clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2;
+}
+
+// ASR Rd, Rs
+ void thumb41_0(u32 opcode)
+{
+ int dest = opcode & 7;
+ u32 value = bus.reg[(opcode >> 3)&7].B.B0;
+
+ if(value) {
+ if(value < 32) {
+ ASR_RD_RS;
+ bus.reg[dest].I = value;
+ } else {
+ if(bus.reg[dest].I & 0x80000000){
+ bus.reg[dest].I = 0xFFFFFFFF;
+ C_FLAG = true;
+ } else {
+ bus.reg[dest].I = 0x00000000;
+ C_FLAG = false;
+ }
+ }
+ }
+ N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false;
+ Z_FLAG = bus.reg[dest].I ? false : true;
+ clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2;
+}
+
+// ADC Rd, Rs
+ void thumb41_1(u32 opcode)
+{
+ int dest = opcode & 0x07;
+ u32 value = bus.reg[(opcode >> 3)&7].I;
+ ADC_RD_RS;
+}
+
+// SBC Rd, Rs
+ void thumb41_2(u32 opcode)
+{
+ int dest = opcode & 0x07;
+ u32 value = bus.reg[(opcode >> 3)&7].I;
+ SBC_RD_RS;
+}
+
+// ROR Rd, Rs
+ void thumb41_3(u32 opcode)
+{
+ int dest = opcode & 7;
+ u32 value = bus.reg[(opcode >> 3)&7].B.B0;
+ u32 val = value;
+ if(val) {
+ value = value & 0x1f;
+ if(val == 0) {
+ C_FLAG = (bus.reg[dest].I & 0x80000000 ? true : false);
+ } else {
+ ROR_RD_RS;
+ bus.reg[dest].I = value;
+ }
+ }
+ clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2;
+ N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false;
+ Z_FLAG = bus.reg[dest].I ? false : true;
+}
+
+// TST Rd, Rs
+ void thumb42_0(u32 opcode)
+{
+ u32 value = bus.reg[opcode & 7].I & bus.reg[(opcode >> 3) & 7].I;
+ N_FLAG = value & 0x80000000 ? true : false;
+ Z_FLAG = value ? false : true;
+}
+
+// NEG Rd, Rs
+ void thumb42_1(u32 opcode)
+{
+ int dest = opcode & 7;
+ int source = (opcode >> 3) & 7;
+ NEG_RD_RS;
+}
+
+// CMP Rd, Rs
+ void thumb42_2(u32 opcode)
+{
+ int dest = opcode & 7;
+ u32 value = bus.reg[(opcode >> 3)&7].I;
+ CMP_RD_RS;
+}
+
+// CMN Rd, Rs
+ void thumb42_3(u32 opcode)
+{
+ int dest = opcode & 7;
+ u32 value = bus.reg[(opcode >> 3)&7].I;
+ CMN_RD_RS;
+}
+
+// ORR Rd, Rs
+ void thumb43_0(u32 opcode)
+{
+ int dest = opcode & 7;
+ bus.reg[dest].I |= bus.reg[(opcode >> 3) & 7].I;
+ Z_FLAG = bus.reg[dest].I ? false : true;
+ N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false;
+}
+
+// MUL Rd, Rs
+ void thumb43_1(u32 opcode)
+{
+ clockTicks = 1;
+ int dest = opcode & 7;
+ u32 rm = bus.reg[dest].I;
+ bus.reg[dest].I = bus.reg[(opcode >> 3) & 7].I * rm;
+ if (((s32)rm) < 0)
+ rm = ~rm;
+ if ((rm & 0xFFFF0000) == 0)
+ clockTicks += 1;
+ else if ((rm & 0xFF000000) == 0)
+ clockTicks += 2;
+ else
+ clockTicks += 3;
+ bus.busPrefetchCount = (bus.busPrefetchCount<>(8-clockTicks));
+ clockTicks += codeTicksAccess(bus.armNextPC, BITS_16) + 1;
+ Z_FLAG = bus.reg[dest].I ? false : true;
+ N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false;
+}
+
+// BIC Rd, Rs
+ void thumb43_2(u32 opcode)
+{
+ int dest = opcode & 7;
+ bus.reg[dest].I &= (~bus.reg[(opcode >> 3) & 7].I);
+ Z_FLAG = bus.reg[dest].I ? false : true;
+ N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false;
+}
+
+// MVN Rd, Rs
+ void thumb43_3(u32 opcode)
+{
+ int dest = opcode & 7;
+ bus.reg[dest].I = ~bus.reg[(opcode >> 3) & 7].I;
+ Z_FLAG = bus.reg[dest].I ? false : true;
+ N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false;
+}
+
+// High-register instructions and BX //////////////////////////////////////
+
+// ADD Rd, Hs
+ void thumb44_1(u32 opcode)
+{
+ bus.reg[opcode&7].I += bus.reg[((opcode>>3)&7)+8].I;
+}
+
+// ADD Hd, Rs
+ void thumb44_2(u32 opcode)
+{
+ bus.reg[(opcode&7)+8].I += bus.reg[(opcode>>3)&7].I;
+ if((opcode&7) == 7) {
+ bus.reg[15].I &= 0xFFFFFFFE;
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 2;
+ THUMB_PREFETCH;
+ clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1
+ + codeTicksAccess(bus.armNextPC, BITS_16) + 3;
+ }
+}
+
+// ADD Hd, Hs
+ void thumb44_3(u32 opcode)
+{
+ bus.reg[(opcode&7)+8].I += bus.reg[((opcode>>3)&7)+8].I;
+ if((opcode&7) == 7) {
+ bus.reg[15].I &= 0xFFFFFFFE;
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 2;
+ THUMB_PREFETCH;
+ clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1
+ + codeTicksAccess(bus.armNextPC, BITS_16) + 3;
+ }
+}
+
+// CMP Rd, Hs
+ void thumb45_1(u32 opcode)
+{
+ int dest = opcode & 7;
+ u32 value = bus.reg[((opcode>>3)&7)+8].I;
+ CMP_RD_RS;
+}
+
+// CMP Hd, Rs
+ void thumb45_2(u32 opcode)
+{
+ int dest = (opcode & 7) + 8;
+ u32 value = bus.reg[(opcode>>3)&7].I;
+ CMP_RD_RS;
+}
+
+// CMP Hd, Hs
+ void thumb45_3(u32 opcode)
+{
+ int dest = (opcode & 7) + 8;
+ u32 value = bus.reg[((opcode>>3)&7)+8].I;
+ CMP_RD_RS;
+}
+
+// MOV Rd, Hs
+ void thumb46_1(u32 opcode)
+{
+ bus.reg[opcode&7].I = bus.reg[((opcode>>3)&7)+8].I;
+}
+
+// MOV Hd, Rs
+ void thumb46_2(u32 opcode)
+{
+ bus.reg[(opcode&7)+8].I = bus.reg[(opcode>>3)&7].I;
+ if((opcode&7) == 7) {
+ bus.reg[15].I &= 0xFFFFFFFE;
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 2;
+ THUMB_PREFETCH;
+ clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1
+ + codeTicksAccess(bus.armNextPC, BITS_16) + 3;
+ }
+}
+
+// MOV Hd, Hs
+ void thumb46_3(u32 opcode)
+{
+ bus.reg[(opcode&7)+8].I = bus.reg[((opcode>>3)&7)+8].I;
+ if((opcode&7) == 7) {
+ bus.reg[15].I &= 0xFFFFFFFE;
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 2;
+ THUMB_PREFETCH;
+ clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1
+ + codeTicksAccess(bus.armNextPC, BITS_16) + 3;
+ }
+}
+
+
+// BX Rs
+ void thumb47(u32 opcode)
+{
+ int base = (opcode >> 3) & 15;
+ bus.busPrefetchCount=0;
+ bus.reg[15].I = bus.reg[base].I;
+ if(bus.reg[base].I & 1) {
+ armState = false;
+ bus.reg[15].I &= 0xFFFFFFFE;
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 2;
+ THUMB_PREFETCH;
+ clockTicks = ((codeTicksAccessSeq16(bus.armNextPC)) << 1)
+ + codeTicksAccess(bus.armNextPC, BITS_16) + 3;
+ } else {
+ armState = true;
+ bus.reg[15].I &= 0xFFFFFFFC;
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 4;
+ ARM_PREFETCH;
+ clockTicks = ((codeTicksAccessSeq32(bus.armNextPC)) << 1)
+ + codeTicksAccess(bus.armNextPC, BITS_32) + 3;
+ }
+}
+
+// Load/store instructions ////////////////////////////////////////////////
+
+// LDR R0~R7,[PC, #Imm]
+ void thumb48(u32 opcode)
+{
+ u8 regist = (opcode >> 8) & 7;
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = (bus.reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2);
+ bus.reg[regist].I = CPUReadMemoryQuick(address);
+ bus.busPrefetchCount=0;
+ int dataticks_value = DATATICKS_ACCESS_32BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16);
+}
+
+// STR Rd, [Rs, Rn]
+ void thumb50(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I;
+ CPUWriteMemory(address, bus.reg[opcode & 7].I);
+ int dataticks_value = DATATICKS_ACCESS_32BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2;
+}
+
+// STRH Rd, [Rs, Rn]
+ void thumb52(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I;
+ CPUWriteHalfWord(address, bus.reg[opcode&7].W.W0);
+ int dataticks_value = DATATICKS_ACCESS_16BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2;
+}
+
+// STRB Rd, [Rs, Rn]
+ void thumb54(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode >>6)&7].I;
+ CPUWriteByte(address, bus.reg[opcode & 7].B.B0);
+ int dataticks_value = DATATICKS_ACCESS_16BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2;
+}
+
+// LDSB Rd, [Rs, Rn]
+ void thumb56(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I;
+ bus.reg[opcode&7].I = (s8)CPUReadByte(address);
+ int dataticks_value = DATATICKS_ACCESS_16BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16);
+}
+
+// LDR Rd, [Rs, Rn]
+ void thumb58(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I;
+ bus.reg[opcode&7].I = CPUReadMemory(address);
+ int dataticks_value = DATATICKS_ACCESS_32BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16);
+}
+
+// LDRH Rd, [Rs, Rn]
+ void thumb5A(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I;
+ bus.reg[opcode&7].I = CPUReadHalfWord(address);
+ int dataticks_value = DATATICKS_ACCESS_32BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16);
+}
+
+// LDRB Rd, [Rs, Rn]
+ void thumb5C(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I;
+ bus.reg[opcode&7].I = CPUReadByte(address);
+ int dataticks_value = DATATICKS_ACCESS_16BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16);
+}
+
+// LDSH Rd, [Rs, Rn]
+ void thumb5E(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I;
+ bus.reg[opcode&7].I = (s16)CPUReadHalfWordSigned(address);
+ int dataticks_value = DATATICKS_ACCESS_16BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16);
+}
+
+// STR Rd, [Rs, #Imm]
+ void thumb60(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2);
+ CPUWriteMemory(address, bus.reg[opcode&7].I);
+ int dataticks_value = DATATICKS_ACCESS_32BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2;
+}
+
+// LDR Rd, [Rs, #Imm]
+ void thumb68(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2);
+ bus.reg[opcode&7].I = CPUReadMemory(address);
+ int dataticks_value = DATATICKS_ACCESS_32BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16);
+}
+
+// STRB Rd, [Rs, #Imm]
+ void thumb70(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31));
+ CPUWriteByte(address, bus.reg[opcode&7].B.B0);
+ int dataticks_value = DATATICKS_ACCESS_16BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2;
+}
+
+// LDRB Rd, [Rs, #Imm]
+ void thumb78(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31));
+ bus.reg[opcode&7].I = CPUReadByte(address);
+ int dataticks_value = DATATICKS_ACCESS_16BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16);
+}
+
+// STRH Rd, [Rs, #Imm]
+ void thumb80(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1);
+ CPUWriteHalfWord(address, bus.reg[opcode&7].W.W0);
+ int dataticks_value = DATATICKS_ACCESS_16BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2;
+}
+
+// LDRH Rd, [Rs, #Imm]
+ void thumb88(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1);
+ bus.reg[opcode&7].I = CPUReadHalfWord(address);
+ int dataticks_value = DATATICKS_ACCESS_16BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16);
+}
+
+// STR R0~R7, [SP, #Imm]
+ void thumb90(u32 opcode)
+{
+ u8 regist = (opcode >> 8) & 7;
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[13].I + ((opcode&255)<<2);
+ CPUWriteMemory(address, bus.reg[regist].I);
+ int dataticks_value = DATATICKS_ACCESS_32BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2;
+}
+
+// LDR R0~R7, [SP, #Imm]
+ void thumb98(u32 opcode)
+{
+ u8 regist = (opcode >> 8) & 7;
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[13].I + ((opcode&255)<<2);
+ bus.reg[regist].I = CPUReadMemoryQuick(address);
+ int dataticks_value = DATATICKS_ACCESS_32BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16);
+}
+
+// PC/stack-related ///////////////////////////////////////////////////////
+
+// ADD R0~R7, PC, Imm
+ void thumbA0(u32 opcode)
+{
+ u8 regist = (opcode >> 8) & 7;
+ bus.reg[regist].I = (bus.reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2);
+}
+
+// ADD R0~R7, SP, Imm
+ void thumbA8(u32 opcode)
+{
+ u8 regist = (opcode >> 8) & 7;
+ bus.reg[regist].I = bus.reg[13].I + ((opcode&255)<<2);
+}
+
+// ADD SP, Imm
+ void thumbB0(u32 opcode)
+{
+ int offset = (opcode & 127) << 2;
+ if(opcode & 0x80)
+ offset = -offset;
+ bus.reg[13].I += offset;
+}
+
+// Push and pop ///////////////////////////////////////////////////////////
+
+#define PUSH_REG(val, r) \
+ if (opcode & (val)) { \
+ CPUWriteMemory(address, bus.reg[(r)].I); \
+ int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \
+ clockTicks += 1 + dataticks_value; \
+ count++; \
+ address += 4; \
+ }
+
+#define POP_REG(val, r) \
+ if (opcode & (val)) { \
+ bus.reg[(r)].I = CPUReadMemory(address); \
+int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \
+ clockTicks += 1 + dataticks_value; \
+ count++; \
+ address += 4; \
+ }
+
+// PUSH {Rlist}
+ void thumbB4(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int count = 0;
+ u32 temp = bus.reg[13].I - 4 * cpuBitsSet[opcode & 0xff];
+ u32 address = temp & 0xFFFFFFFC;
+ PUSH_REG(1, 0);
+ PUSH_REG(2, 1);
+ PUSH_REG(4, 2);
+ PUSH_REG(8, 3);
+ PUSH_REG(16, 4);
+ PUSH_REG(32, 5);
+ PUSH_REG(64, 6);
+ PUSH_REG(128, 7);
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_16);
+ bus.reg[13].I = temp;
+}
+
+// PUSH {Rlist, LR}
+ void thumbB5(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int count = 0;
+ u32 temp = bus.reg[13].I - 4 - 4 * cpuBitsSet[opcode & 0xff];
+ u32 address = temp & 0xFFFFFFFC;
+ PUSH_REG(1, 0);
+ PUSH_REG(2, 1);
+ PUSH_REG(4, 2);
+ PUSH_REG(8, 3);
+ PUSH_REG(16, 4);
+ PUSH_REG(32, 5);
+ PUSH_REG(64, 6);
+ PUSH_REG(128, 7);
+ PUSH_REG(256, 14);
+ clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_16);
+ bus.reg[13].I = temp;
+}
+
+// POP {Rlist}
+ void thumbBC(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int count = 0;
+ u32 address = bus.reg[13].I & 0xFFFFFFFC;
+ u32 temp = bus.reg[13].I + 4*cpuBitsSet[opcode & 0xFF];
+ POP_REG(1, 0);
+ POP_REG(2, 1);
+ POP_REG(4, 2);
+ POP_REG(8, 3);
+ POP_REG(16, 4);
+ POP_REG(32, 5);
+ POP_REG(64, 6);
+ POP_REG(128, 7);
+ bus.reg[13].I = temp;
+ clockTicks = 2 + codeTicksAccess(bus.armNextPC, BITS_16);
+}
+
+// POP {Rlist, PC}
+ void thumbBD(u32 opcode)
+{
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ int count = 0;
+ u32 address = bus.reg[13].I & 0xFFFFFFFC;
+ u32 temp = bus.reg[13].I + 4 + 4*cpuBitsSet[opcode & 0xFF];
+ POP_REG(1, 0);
+ POP_REG(2, 1);
+ POP_REG(4, 2);
+ POP_REG(8, 3);
+ POP_REG(16, 4);
+ POP_REG(32, 5);
+ POP_REG(64, 6);
+ POP_REG(128, 7);
+ bus.reg[15].I = (CPUReadMemory(address) & 0xFFFFFFFE);
+ int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address);
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value);
+ clockTicks += 1 + dataticks_value;
+ count++;
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 2;
+ bus.reg[13].I = temp;
+ THUMB_PREFETCH;
+ bus.busPrefetchCount = 0;
+ clockTicks += 3 + ((codeTicksAccess(bus.armNextPC, BITS_16)) << 1);
+}
+
+// Load/store multiple ////////////////////////////////////////////////////
+
+#define THUMB_STM_REG(val,r,b) \
+ if(opcode & (val)) { \
+ CPUWriteMemory(address, bus.reg[(r)].I); \
+ bus.reg[(b)].I = temp; \
+ int dataticks_val = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_val); \
+ clockTicks += 1 + dataticks_val; \
+ count++; \
+ address += 4; \
+ }
+
+#define THUMB_LDM_REG(val,r) \
+ if(opcode & (val)) { \
+ bus.reg[(r)].I = CPUReadMemory(address); \
+ int dataticks_val = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \
+ DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_val); \
+ clockTicks += 1 + dataticks_val; \
+ count++; \
+ address += 4; \
+ }
+
+// STM R0~7!, {Rlist}
+ void thumbC0(u32 opcode)
+{
+ u8 regist = (opcode >> 8) & 7;
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[regist].I & 0xFFFFFFFC;
+ u32 temp = bus.reg[regist].I + 4*cpuBitsSet[opcode & 0xff];
+ int count = 0;
+ // store
+ THUMB_STM_REG(1, 0, regist);
+ THUMB_STM_REG(2, 1, regist);
+ THUMB_STM_REG(4, 2, regist);
+ THUMB_STM_REG(8, 3, regist);
+ THUMB_STM_REG(16, 4, regist);
+ THUMB_STM_REG(32, 5, regist);
+ THUMB_STM_REG(64, 6, regist);
+ THUMB_STM_REG(128, 7, regist);
+ clockTicks = 1 + codeTicksAccess(bus.armNextPC, BITS_16);
+}
+
+// LDM R0~R7!, {Rlist}
+ void thumbC8(u32 opcode)
+{
+ u8 regist = (opcode >> 8) & 7;
+ if (bus.busPrefetchCount == 0)
+ bus.busPrefetch = bus.busPrefetchEnable;
+ u32 address = bus.reg[regist].I & 0xFFFFFFFC;
+ u32 temp = bus.reg[regist].I + 4*cpuBitsSet[opcode & 0xFF];
+ int count = 0;
+ // load
+ THUMB_LDM_REG(1, 0);
+ THUMB_LDM_REG(2, 1);
+ THUMB_LDM_REG(4, 2);
+ THUMB_LDM_REG(8, 3);
+ THUMB_LDM_REG(16, 4);
+ THUMB_LDM_REG(32, 5);
+ THUMB_LDM_REG(64, 6);
+ THUMB_LDM_REG(128, 7);
+ clockTicks = 2 + codeTicksAccess(bus.armNextPC, BITS_16);
+ if(!(opcode & (1<*thumbInsnTable[opcode>>6])(opcode);
+
+ ct = clockTicks;
+
+ if (ct < 0)
+ return 0;
+
+ /// better pipelining
+ if (ct==0)
+ clockTicks = codeTicksAccessSeq16(oldArmNextPC) + 1;
+
+ cpuTotalTicks += clockTicks;
+
+
+#ifdef USE_SWITICKS
+ } while (cpuTotalTicks < cpuNextEvent && !armState && !holdState && !SWITicks);
+#else
+} while ((cpuTotalTicks < cpuNextEvent) & ~armState & ~holdState);
+#endif
+ return 1;
+}
+
+
+/*============================================================
+ GBA GFX
+============================================================ */
+
+#ifdef TILED_RENDERING
+#ifdef _MSC_VER
+union u8h
+{
+ __pragma( pack(push, 1));
+ struct
+#ifdef LSB_FIRST
+ {
+ /* 0*/ unsigned char lo:4;
+ /* 4*/ unsigned char hi:4;
+#else
+ {
+ /* 4*/ unsigned char hi:4;
+ /* 0*/ unsigned char lo:4;
+#endif
+ }
+ __pragma(pack(pop));
+ u8 val;
+};
+#else
+union u8h
+{
+ struct
+#ifdef LSB_FIRST
+ {
+ /* 0*/ unsigned char lo:4;
+ /* 4*/ unsigned char hi:4;
+#else
+ {
+ /* 4*/ unsigned char hi:4;
+ /* 0*/ unsigned char lo:4;
+#endif
+ } __attribute__ ((packed));
+ u8 val;
+};
+#endif
+
+union TileEntry
+{
+#ifdef LSB_FIRST
+ struct
+ {
+ /* 0*/ unsigned tileNum:10;
+ /*12*/ unsigned hFlip:1;
+ /*13*/ unsigned vFlip:1;
+ /*14*/ unsigned palette:4;
+ };
+#else
+ struct
+ {
+ /*14*/ unsigned palette:4;
+ /*13*/ unsigned vFlip:1;
+ /*12*/ unsigned hFlip:1;
+ /* 0*/ unsigned tileNum:10;
+ };
+#endif
+ u16 val;
+};
+
+struct TileLine
+{
+ u32 pixels[8];
+};
+
+typedef const TileLine (*TileReader) (const u16 *, const int, const u8 *, u16 *, const u32);
+
+static inline void gfxDrawPixel(u32 *dest, const u8 color, const u16 *palette, const u32 prio)
+{
+ *dest = color ? (READ16LE(&palette[color]) | prio): 0x80000000;
+}
+
+static inline const TileLine gfxReadTile(const u16 *screenSource, const int yyy, const u8 *charBase, u16 *palette, const u32 prio)
+{
+ TileEntry tile;
+ tile.val = READ16LE(screenSource);
+
+ int tileY = yyy & 7;
+ if (tile.vFlip) tileY = 7 - tileY;
+ TileLine tileLine;
+
+ const u8 *tileBase = &charBase[tile.tileNum * 64 + tileY * 8];
+
+ if (!tile.hFlip)
+ {
+ gfxDrawPixel(&tileLine.pixels[0], tileBase[0], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[1], tileBase[1], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[2], tileBase[2], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[3], tileBase[3], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[4], tileBase[4], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[5], tileBase[5], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[6], tileBase[6], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[7], tileBase[7], palette, prio);
+ }
+ else
+ {
+ gfxDrawPixel(&tileLine.pixels[0], tileBase[7], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[1], tileBase[6], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[2], tileBase[5], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[3], tileBase[4], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[4], tileBase[3], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[5], tileBase[2], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[6], tileBase[1], palette, prio);
+ gfxDrawPixel(&tileLine.pixels[7], tileBase[0], palette, prio);
+ }
+
+ return tileLine;
+}
+
+static inline const TileLine gfxReadTilePal(const u16 *screenSource, const int yyy, const u8 *charBase, u16 *palette, const u32 prio)
+{
+ TileEntry tile;
+ tile.val = READ16LE(screenSource);
+
+ int tileY = yyy & 7;
+ if (tile.vFlip) tileY = 7 - tileY;
+ palette += tile.palette * 16;
+ TileLine tileLine;
+
+ const u8h *tileBase = (u8h*) &charBase[tile.tileNum * 32 + tileY * 4];
+
+ if (!tile.hFlip)
+ {
+ gfxDrawPixel(&tileLine.pixels[0], tileBase[0].lo, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[1], tileBase[0].hi, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[2], tileBase[1].lo, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[3], tileBase[1].hi, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[4], tileBase[2].lo, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[5], tileBase[2].hi, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[6], tileBase[3].lo, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[7], tileBase[3].hi, palette, prio);
+ }
+ else
+ {
+ gfxDrawPixel(&tileLine.pixels[0], tileBase[3].hi, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[1], tileBase[3].lo, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[2], tileBase[2].hi, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[3], tileBase[2].lo, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[4], tileBase[1].hi, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[5], tileBase[1].lo, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[6], tileBase[0].hi, palette, prio);
+ gfxDrawPixel(&tileLine.pixels[7], tileBase[0].lo, palette, prio);
+ }
+
+ return tileLine;
+}
+
+static inline void gfxDrawTile(const TileLine &tileLine, u32 *line)
+{
+ memcpy(line, tileLine.pixels, sizeof(tileLine.pixels));
+}
+
+static inline void gfxDrawTileClipped(const TileLine &tileLine, u32 *line, const int start, int w)
+{
+ memcpy(line, tileLine.pixels + start, w * sizeof(u32));
+}
+
+template
+void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs,
+ u32 *line)
+{
+ u16 *palette = (u16 *)graphics.paletteRAM;
+ u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000];
+ u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800];
+ u32 prio = ((control & 3)<<25) + 0x1000000;
+ int sizeX = 256;
+ int sizeY = 256;
+ switch ((control >> 14) & 3)
+ {
+ case 0:
+ break;
+ case 1:
+ sizeX = 512;
+ break;
+ case 2:
+ sizeY = 512;
+ break;
+ case 3:
+ sizeX = 512;
+ sizeY = 512;
+ break;
+ }
+
+ int maskX = sizeX-1;
+ int maskY = sizeY-1;
+
+ bool mosaicOn = (control & 0x40) ? true : false;
+
+ int xxx = hofs & maskX;
+ int yyy = (vofs + io_registers[REG_VCOUNT]) & maskY;
+ int mosaicX = (MOSAIC & 0x000F)+1;
+ int mosaicY = ((MOSAIC & 0x00F0)>>4)+1;
+
+ if (mosaicOn)
+ {
+ if ((io_registers[REG_VCOUNT] % mosaicY) != 0)
+ {
+ mosaicY = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY);
+ yyy = (vofs + mosaicY) & maskY;
+ }
+ }
+
+ if (yyy > 255 && sizeY > 256)
+ {
+ yyy &= 255;
+ screenBase += 0x400;
+ if (sizeX > 256)
+ screenBase += 0x400;
+ }
+
+ int yshift = ((yyy>>3)<<5);
+
+ u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift;
+ int x = 0;
+ const int firstTileX = xxx & 7;
+
+ // First tile, if clipped
+ if (firstTileX)
+ {
+ gfxDrawTileClipped(readTile(screenSource, yyy, charBase, palette, prio), &line[x], firstTileX, 8 - firstTileX);
+ screenSource++;
+ x += 8 - firstTileX;
+ xxx += 8 - firstTileX;
+
+ if (xxx == 256 && sizeX > 256)
+ {
+ screenSource = screenBase + 0x400 + yshift;
+ }
+ else if (xxx >= sizeX)
+ {
+ xxx = 0;
+ screenSource = screenBase + yshift;
+ }
+ }
+
+ // Middle tiles, full
+ while (x < 240 - firstTileX)
+ {
+ gfxDrawTile(readTile(screenSource, yyy, charBase, palette, prio), &line[x]);
+ screenSource++;
+ xxx += 8;
+ x += 8;
+
+ if (xxx == 256 && sizeX > 256)
+ {
+ screenSource = screenBase + 0x400 + yshift;
+ }
+ else if (xxx >= sizeX)
+ {
+ xxx = 0;
+ screenSource = screenBase + yshift;
+ }
+ }
+
+ // Last tile, if clipped
+ if (firstTileX)
+ {
+ gfxDrawTileClipped(readTile(screenSource, yyy, charBase, palette, prio), &line[x], 0, firstTileX);
+ }
+
+ if (mosaicOn)
+ {
+ if (mosaicX > 1)
+ {
+ int m = 1;
+ for (int i = 0; i < 239; i++)
+ {
+ line[i+1] = line[i];
+ m++;
+ if (m == mosaicX)
+ {
+ m = 1;
+ i++;
+ }
+ }
+ }
+ }
+}
+
+void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, u32 *line)
+{
+ if (control & 0x80) // 1 pal / 256 col
+ gfxDrawTextScreen(control, hofs, vofs, line);
+ else // 16 pal / 16 col
+ gfxDrawTextScreen(control, hofs, vofs, line);
+}
+#else
+inline void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs,
+ u32 *line)
+{
+ u16 *palette = (u16 *)graphics.paletteRAM;
+ u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000];
+ u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800];
+ u32 prio = ((control & 3)<<25) + 0x1000000;
+ int sizeX = 256;
+ int sizeY = 256;
+ switch((control >> 14) & 3) {
+ case 0:
+ break;
+ case 1:
+ sizeX = 512;
+ break;
+ case 2:
+ sizeY = 512;
+ break;
+ case 3:
+ sizeX = 512;
+ sizeY = 512;
+ break;
+ }
+
+ int maskX = sizeX-1;
+ int maskY = sizeY-1;
+
+ bool mosaicOn = (control & 0x40) ? true : false;
+
+ int xxx = hofs & maskX;
+ int yyy = (vofs + io_registers[REG_VCOUNT]) & maskY;
+ int mosaicX = (MOSAIC & 0x000F)+1;
+ int mosaicY = ((MOSAIC & 0x00F0)>>4)+1;
+
+ if(mosaicOn) {
+ if((io_registers[REG_VCOUNT] % mosaicY) != 0) {
+ mosaicY = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY);
+ yyy = (vofs + mosaicY) & maskY;
+ }
+ }
+
+ if(yyy > 255 && sizeY > 256) {
+ yyy &= 255;
+ screenBase += 0x400;
+ if(sizeX > 256)
+ screenBase += 0x400;
+ }
+
+ int yshift = ((yyy>>3)<<5);
+ if((control) & 0x80) {
+ u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift;
+ for(int x = 0; x < 240; x++) {
+ u16 data = READ16LE(screenSource);
+
+ int tile = data & 0x3FF;
+ int tileX = (xxx & 7);
+ int tileY = yyy & 7;
+
+ if(tileX == 7)
+ screenSource++;
+
+ if(data & 0x0400)
+ tileX = 7 - tileX;
+ if(data & 0x0800)
+ tileY = 7 - tileY;
+
+ u8 color = charBase[tile * 64 + tileY * 8 + tileX];
+
+ line[x] = color ? (READ16LE(&palette[color]) | prio): 0x80000000;
+
+ xxx++;
+ if(xxx == 256) {
+ if(sizeX > 256)
+ screenSource = screenBase + 0x400 + yshift;
+ else {
+ screenSource = screenBase + yshift;
+ xxx = 0;
+ }
+ } else if(xxx >= sizeX) {
+ xxx = 0;
+ screenSource = screenBase + yshift;
+ }
+ }
+ } else {
+ u16 *screenSource = screenBase + 0x400*(xxx>>8)+((xxx&255)>>3) +
+ yshift;
+ for(int x = 0; x < 240; x++) {
+ u16 data = READ16LE(screenSource);
+
+ int tile = data & 0x3FF;
+ int tileX = (xxx & 7);
+ int tileY = yyy & 7;
+
+ if(tileX == 7)
+ screenSource++;
+
+ if(data & 0x0400)
+ tileX = 7 - tileX;
+ if(data & 0x0800)
+ tileY = 7 - tileY;
+
+ u8 color = charBase[(tile<<5) + (tileY<<2) + (tileX>>1)];
+
+ if(tileX & 1) {
+ color = (color >> 4);
+ } else {
+ color &= 0x0F;
+ }
+
+ int pal = (data>>8) & 0xF0;
+ line[x] = color ? (READ16LE(&palette[pal + color])|prio): 0x80000000;
+
+ xxx++;
+ if(xxx == 256) {
+ if(sizeX > 256)
+ screenSource = screenBase + 0x400 + yshift;
+ else {
+ screenSource = screenBase + yshift;
+ xxx = 0;
+ }
+ } else if(xxx >= sizeX) {
+ xxx = 0;
+ screenSource = screenBase + yshift;
+ }
+ }
+ }
+ if(mosaicOn) {
+ if(mosaicX > 1) {
+ int m = 1;
+ for(int i = 0; i < 239; i++) {
+ line[i+1] = line[i];
+ m++;
+ if(m == mosaicX) {
+ m = 1;
+ i++;
+ }
+ }
+ }
+ }
+}
+#endif
+
+INLINE void gfxDrawRotScreen(u16 control, u16 x_l, u16 x_h, u16 y_l, u16 y_h,
+u16 pa, u16 pb, u16 pc, u16 pd, int& currentX, int& currentY, int changed, u32 *line)
+{
+ u16 *palette = (u16 *)graphics.paletteRAM;
+ u8 *charBase = &vram[((control >> 2) & 0x03) << 14];
+ u8 *screenBase = (u8 *)&vram[((control >> 8) & 0x1f) << 11];
+ int prio = ((control & 3) << 25) + 0x1000000;
+
+ u32 map_size = (control >> 14) & 3;
+ u32 sizeX = map_sizes_rot[map_size];
+ u32 sizeY = map_sizes_rot[map_size];
+
+ int maskX = sizeX-1;
+ int maskY = sizeY-1;
+
+ int yshift = ((control >> 14) & 3)+4;
+
+#ifdef BRANCHLESS_GBA_GFX
+ int dx = pa & 0x7FFF;
+ int dmx = pb & 0x7FFF;
+ int dy = pc & 0x7FFF;
+ int dmy = pd & 0x7FFF;
+
+ dx |= isel(-(pa & 0x8000), 0, 0xFFFF8000);
+
+ dmx |= isel(-(pb & 0x8000), 0, 0xFFFF8000);
+
+ dy |= isel(-(pc & 0x8000), 0, 0xFFFF8000);
+
+ dmy |= isel(-(pd & 0x8000), 0, 0xFFFF8000);
+#else
+ int dx = pa & 0x7FFF;
+ if(pa & 0x8000)
+ dx |= 0xFFFF8000;
+ int dmx = pb & 0x7FFF;
+ if(pb & 0x8000)
+ dmx |= 0xFFFF8000;
+ int dy = pc & 0x7FFF;
+ if(pc & 0x8000)
+ dy |= 0xFFFF8000;
+ int dmy = pd & 0x7FFF;
+ if(pd & 0x8000)
+ dmy |= 0xFFFF8000;
+#endif
+
+ if(io_registers[REG_VCOUNT] == 0)
+ changed = 3;
+
+ currentX += dmx;
+ currentY += dmy;
+
+ if(changed & 1)
+ {
+ currentX = (x_l) | ((x_h & 0x07FF)<<16);
+ if(x_h & 0x0800)
+ currentX |= 0xF8000000;
+ }
+
+ if(changed & 2)
+ {
+ currentY = (y_l) | ((y_h & 0x07FF)<<16);
+ if(y_h & 0x0800)
+ currentY |= 0xF8000000;
+ }
+
+ int realX = currentX;
+ int realY = currentY;
+
+ if(control & 0x40)
+ {
+ int mosaicY = ((MOSAIC & 0xF0)>>4) + 1;
+ int y = (io_registers[REG_VCOUNT] % mosaicY);
+ realX -= y*dmx;
+ realY -= y*dmy;
+ }
+
+ memset(line, -1, 240 * sizeof(u32));
+ if(control & 0x2000)
+ {
+ for(u32 x = 0; x < 240u; ++x)
+ {
+ int xxx = (realX >> 8) & maskX;
+ int yyy = (realY >> 8) & maskY;
+
+ int tile = screenBase[(xxx>>3) + ((yyy>>3)<> 8);
+ unsigned yyy = (realY >> 8);
+
+ if(xxx < sizeX && yyy < sizeY)
+ {
+ int tile = screenBase[(xxx>>3) + ((yyy>>3)< 1)
+ {
+ int m = 1;
+ for(u32 i = 0; i < 239u; ++i)
+ {
+ line[i+1] = line[i];
+ if(++m == mosaicX)
+ {
+ m = 1;
+ ++i;
+ }
+ }
+ }
+ }
+}
+
+INLINE void gfxDrawRotScreen16Bit( int& currentX, int& currentY, int changed)
+{
+ u16 *screenBase = (u16 *)&vram[0];
+ int prio = ((io_registers[REG_BG2CNT] & 3) << 25) + 0x1000000;
+
+ u32 sizeX = 240;
+ u32 sizeY = 160;
+
+ int startX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16);
+ if(BG2X_H & 0x0800)
+ startX |= 0xF8000000;
+ int startY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16);
+ if(BG2Y_H & 0x0800)
+ startY |= 0xF8000000;
+
+#ifdef BRANCHLESS_GBA_GFX
+ int dx = io_registers[REG_BG2PA] & 0x7FFF;
+ dx |= isel(-(io_registers[REG_BG2PA] & 0x8000), 0, 0xFFFF8000);
+
+ int dmx = io_registers[REG_BG2PB] & 0x7FFF;
+ dmx |= isel(-(io_registers[REG_BG2PB] & 0x8000), 0, 0xFFFF8000);
+
+ int dy = io_registers[REG_BG2PC] & 0x7FFF;
+ dy |= isel(-(io_registers[REG_BG2PC] & 0x8000), 0, 0xFFFF8000);
+
+ int dmy = io_registers[REG_BG2PD] & 0x7FFF;
+ dmy |= isel(-(io_registers[REG_BG2PD] & 0x8000), 0, 0xFFFF8000);
+#else
+ int dx = io_registers[REG_BG2PA] & 0x7FFF;
+ if(io_registers[REG_BG2PA] & 0x8000)
+ dx |= 0xFFFF8000;
+ int dmx = io_registers[REG_BG2PB] & 0x7FFF;
+ if(io_registers[REG_BG2PB] & 0x8000)
+ dmx |= 0xFFFF8000;
+ int dy = io_registers[REG_BG2PC] & 0x7FFF;
+ if(io_registers[REG_BG2PC] & 0x8000)
+ dy |= 0xFFFF8000;
+ int dmy = io_registers[REG_BG2PD] & 0x7FFF;
+ if(io_registers[REG_BG2PD] & 0x8000)
+ dmy |= 0xFFFF8000;
+#endif
+
+ if(io_registers[REG_VCOUNT] == 0)
+ changed = 3;
+
+ currentX += dmx;
+ currentY += dmy;
+
+ if(changed & 1)
+ {
+ currentX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16);
+ if(BG2X_H & 0x0800)
+ currentX |= 0xF8000000;
+ }
+
+ if(changed & 2)
+ {
+ currentY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16);
+ if(BG2Y_H & 0x0800)
+ currentY |= 0xF8000000;
+ }
+
+ int realX = currentX;
+ int realY = currentY;
+
+ if(io_registers[REG_BG2CNT] & 0x40) {
+ int mosaicY = ((MOSAIC & 0xF0)>>4) + 1;
+ int y = (io_registers[REG_VCOUNT] % mosaicY);
+ realX -= y*dmx;
+ realY -= y*dmy;
+ }
+
+ unsigned xxx = (realX >> 8);
+ unsigned yyy = (realY >> 8);
+
+ memset(line[2], -1, 240 * sizeof(u32));
+ for(u32 x = 0; x < 240u; ++x)
+ {
+ if(xxx < sizeX && yyy < sizeY)
+ line[2][x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio);
+
+ realX += dx;
+ realY += dy;
+
+ xxx = (realX >> 8);
+ yyy = (realY >> 8);
+ }
+
+ if(io_registers[REG_BG2CNT] & 0x40) {
+ int mosaicX = (MOSAIC & 0xF) + 1;
+ if(mosaicX > 1) {
+ int m = 1;
+ for(u32 i = 0; i < 239u; ++i)
+ {
+ line[2][i+1] = line[2][i];
+ if(++m == mosaicX)
+ {
+ m = 1;
+ ++i;
+ }
+ }
+ }
+ }
+}
+
+INLINE void gfxDrawRotScreen256(int ¤tX, int& currentY, int changed)
+{
+ u16 *palette = (u16 *)graphics.paletteRAM;
+ u8 *screenBase = (io_registers[REG_DISPCNT] & 0x0010) ? &vram[0xA000] : &vram[0x0000];
+ int prio = ((io_registers[REG_BG2CNT] & 3) << 25) + 0x1000000;
+ u32 sizeX = 240;
+ u32 sizeY = 160;
+
+ int startX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16);
+ if(BG2X_H & 0x0800)
+ startX |= 0xF8000000;
+ int startY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16);
+ if(BG2Y_H & 0x0800)
+ startY |= 0xF8000000;
+
+#ifdef BRANCHLESS_GBA_GFX
+ int dx = io_registers[REG_BG2PA] & 0x7FFF;
+ dx |= isel(-(io_registers[REG_BG2PA] & 0x8000), 0, 0xFFFF8000);
+
+ int dmx = io_registers[REG_BG2PB] & 0x7FFF;
+ dmx |= isel(-(io_registers[REG_BG2PB] & 0x8000), 0, 0xFFFF8000);
+
+ int dy = io_registers[REG_BG2PC] & 0x7FFF;
+ dy |= isel(-(io_registers[REG_BG2PC] & 0x8000), 0, 0xFFFF8000);
+
+ int dmy = io_registers[REG_BG2PD] & 0x7FFF;
+ dmy |= isel(-(io_registers[REG_BG2PD] & 0x8000), 0, 0xFFFF8000);
+#else
+ int dx = io_registers[REG_BG2PA] & 0x7FFF;
+ if(io_registers[REG_BG2PA] & 0x8000)
+ dx |= 0xFFFF8000;
+ int dmx = io_registers[REG_BG2PB] & 0x7FFF;
+ if(io_registers[REG_BG2PB] & 0x8000)
+ dmx |= 0xFFFF8000;
+ int dy = io_registers[REG_BG2PC] & 0x7FFF;
+ if(io_registers[REG_BG2PC] & 0x8000)
+ dy |= 0xFFFF8000;
+ int dmy = io_registers[REG_BG2PD] & 0x7FFF;
+ if(io_registers[REG_BG2PD] & 0x8000)
+ dmy |= 0xFFFF8000;
+#endif
+
+ if(io_registers[REG_VCOUNT] == 0)
+ changed = 3;
+
+ currentX += dmx;
+ currentY += dmy;
+
+ if(changed & 1)
+ {
+ currentX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16);
+ if(BG2X_H & 0x0800)
+ currentX |= 0xF8000000;
+ }
+
+ if(changed & 2)
+ {
+ currentY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16);
+ if(BG2Y_H & 0x0800)
+ currentY |= 0xF8000000;
+ }
+
+ int realX = currentX;
+ int realY = currentY;
+
+ if(io_registers[REG_BG2CNT] & 0x40) {
+ int mosaicY = ((MOSAIC & 0xF0)>>4) + 1;
+ int y = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY);
+ realX = startX + y*dmx;
+ realY = startY + y*dmy;
+ }
+
+ int xxx = (realX >> 8);
+ int yyy = (realY >> 8);
+
+ memset(line[2], -1, 240 * sizeof(u32));
+ for(u32 x = 0; x < 240; ++x)
+ {
+ u8 color = screenBase[yyy * 240 + xxx];
+ if(unsigned(xxx) < sizeX && unsigned(yyy) < sizeY && color)
+ line[2][x] = (READ16LE(&palette[color])|prio);
+ realX += dx;
+ realY += dy;
+
+ xxx = (realX >> 8);
+ yyy = (realY >> 8);
+ }
+
+ if(io_registers[REG_BG2CNT] & 0x40)
+ {
+ int mosaicX = (MOSAIC & 0xF) + 1;
+ if(mosaicX > 1)
+ {
+ int m = 1;
+ for(u32 i = 0; i < 239u; ++i)
+ {
+ line[2][i+1] = line[2][i];
+ if(++m == mosaicX)
+ {
+ m = 1;
+ ++i;
+ }
+ }
+ }
+ }
+}
+
+INLINE void gfxDrawRotScreen16Bit160(int& currentX, int& currentY, int changed)
+{
+ u16 *screenBase = (io_registers[REG_DISPCNT] & 0x0010) ? (u16 *)&vram[0xa000] :
+ (u16 *)&vram[0];
+ int prio = ((io_registers[REG_BG2CNT] & 3) << 25) + 0x1000000;
+ u32 sizeX = 160;
+ u32 sizeY = 128;
+
+ int startX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16);
+ if(BG2X_H & 0x0800)
+ startX |= 0xF8000000;
+ int startY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16);
+ if(BG2Y_H & 0x0800)
+ startY |= 0xF8000000;
+
+#ifdef BRANCHLESS_GBA_GFX
+ int dx = io_registers[REG_BG2PA] & 0x7FFF;
+ dx |= isel(-(io_registers[REG_BG2PA] & 0x8000), 0, 0xFFFF8000);
+
+ int dmx = io_registers[REG_BG2PB] & 0x7FFF;
+ dmx |= isel(-(io_registers[REG_BG2PB] & 0x8000), 0, 0xFFFF8000);
+
+ int dy = io_registers[REG_BG2PC] & 0x7FFF;
+ dy |= isel(-(io_registers[REG_BG2PC] & 0x8000), 0, 0xFFFF8000);
+
+ int dmy = io_registers[REG_BG2PD] & 0x7FFF;
+ dmy |= isel(-(io_registers[REG_BG2PD] & 0x8000), 0, 0xFFFF8000);
+#else
+ int dx = io_registers[REG_BG2PA] & 0x7FFF;
+ if(io_registers[REG_BG2PA] & 0x8000)
+ dx |= 0xFFFF8000;
+ int dmx = io_registers[REG_BG2PB] & 0x7FFF;
+ if(io_registers[REG_BG2PB] & 0x8000)
+ dmx |= 0xFFFF8000;
+ int dy = io_registers[REG_BG2PC] & 0x7FFF;
+ if(io_registers[REG_BG2PC] & 0x8000)
+ dy |= 0xFFFF8000;
+ int dmy = io_registers[REG_BG2PD] & 0x7FFF;
+ if(io_registers[REG_BG2PD] & 0x8000)
+ dmy |= 0xFFFF8000;
+#endif
+
+ if(io_registers[REG_VCOUNT] == 0)
+ changed = 3;
+
+ currentX += dmx;
+ currentY += dmy;
+
+ if(changed & 1)
+ {
+ currentX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16);
+ if(BG2X_H & 0x0800)
+ currentX |= 0xF8000000;
+ }
+
+ if(changed & 2)
+ {
+ currentY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16);
+ if(BG2Y_H & 0x0800)
+ currentY |= 0xF8000000;
+ }
+
+ int realX = currentX;
+ int realY = currentY;
+
+ if(io_registers[REG_BG2CNT] & 0x40) {
+ int mosaicY = ((MOSAIC & 0xF0)>>4) + 1;
+ int y = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY);
+ realX = startX + y*dmx;
+ realY = startY + y*dmy;
+ }
+
+ int xxx = (realX >> 8);
+ int yyy = (realY >> 8);
+
+ memset(line[2], -1, 240 * sizeof(u32));
+ for(u32 x = 0; x < 240u; ++x)
+ {
+ if(unsigned(xxx) < sizeX && unsigned(yyy) < sizeY)
+ line[2][x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio);
+
+ realX += dx;
+ realY += dy;
+
+ xxx = (realX >> 8);
+ yyy = (realY >> 8);
+ }
+
+
+ int mosaicX = (MOSAIC & 0xF) + 1;
+ if(io_registers[REG_BG2CNT] & 0x40 && (mosaicX > 1))
+ {
+ int m = 1;
+ for(u32 i = 0; i < 239u; ++i)
+ {
+ line[2][i+1] = line[2][i];
+ if(++m == mosaicX)
+ {
+ m = 1;
+ ++i;
+ }
+ }
+ }
+}
+
+/* lineOBJpix is used to keep track of the drawn OBJs
+ and to stop drawing them if the 'maximum number of OBJ per line'
+ has been reached. */
+
+INLINE void gfxDrawSprites (void)
+{
+ unsigned lineOBJpix, m;
+
+ lineOBJpix = (io_registers[REG_DISPCNT] & 0x20) ? 954 : 1226;
+ m = 0;
+
+ u16 *sprites = (u16 *)oam;
+ u16 *spritePalette = &((u16 *)graphics.paletteRAM)[256];
+ int mosaicY = ((MOSAIC & 0xF000)>>12) + 1;
+ int mosaicX = ((MOSAIC & 0xF00)>>8) + 1;
+ for(u32 x = 0; x < 128; x++)
+ {
+ u16 a0 = READ16LE(sprites++);
+ u16 a1 = READ16LE(sprites++);
+ u16 a2 = READ16LE(sprites++);
+ ++sprites;
+
+ lineOBJpixleft[x]=lineOBJpix;
+
+ lineOBJpix-=2;
+ if (lineOBJpix<=0)
+ return;
+
+ if ((a0 & 0x0c00) == 0x0c00)
+ a0 &=0xF3FF;
+
+ u16 a0val = a0>>14;
+
+ if (a0val == 3)
+ {
+ a0 &= 0x3FFF;
+ a1 &= 0x3FFF;
+ }
+
+ u32 sizeX = 8<<(a1>>14);
+ u32 sizeY = sizeX;
+
+
+ if (a0val & 1)
+ {
+#ifdef BRANCHLESS_GBA_GFX
+ sizeX <<= isel(-(sizeX & (~31u)), 1, 0);
+ sizeY >>= isel(-(sizeY>8), 0, 1);
+#else
+ if (sizeX<32)
+ sizeX<<=1;
+ if (sizeY>8)
+ sizeY>>=1;
+#endif
+ }
+ else if (a0val & 2)
+ {
+#ifdef BRANCHLESS_GBA_GFX
+ sizeX >>= isel(-(sizeX>8), 0, 1);
+ sizeY <<= isel(-(sizeY & (~31u)), 1, 0);
+#else
+ if (sizeX>8)
+ sizeX>>=1;
+ if (sizeY<32)
+ sizeY<<=1;
+#endif
+
+ }
+
+
+ int sy = (a0 & 255);
+ int sx = (a1 & 0x1FF);
+
+ // computes ticks used by OBJ-WIN if OBJWIN is enabled
+ if (((a0 & 0x0c00) == 0x0800) && (graphics.layerEnable & 0x8000))
+ {
+ if ((a0 & 0x0300) == 0x0300)
+ {
+ sizeX<<=1;
+ sizeY<<=1;
+ }
+
+#ifdef BRANCHLESS_GBA_GFX
+ sy -= isel(256 - sy - sizeY, 0, 256);
+ sx -= isel(512 - sx - sizeX, 0, 512);
+#else
+ if((sy+sizeY) > 256)
+ sy -= 256;
+ if ((sx+sizeX)> 512)
+ sx -= 512;
+#endif
+
+ if (sx < 0)
+ {
+ sizeX+=sx;
+ sx = 0;
+ }
+ else if ((sx+sizeX)>240)
+ sizeX=240-sx;
+
+ if ((io_registers[REG_VCOUNT]>=sy) && (io_registers[REG_VCOUNT] 256)
+ sy -= 256;
+ int t = io_registers[REG_VCOUNT] - sy;
+ if(unsigned(t) < fieldY)
+ {
+ u32 startpix = 0;
+ if ((sx+fieldX)> 512)
+ startpix=512-sx;
+
+ if (lineOBJpix && ((sx < 240) || startpix))
+ {
+ lineOBJpix-=8;
+ int rot = (((a1 >> 9) & 0x1F) << 4);
+ u16 *OAM = (u16 *)oam;
+ int dx = READ16LE(&OAM[3 + rot]);
+ if(dx & 0x8000)
+ dx |= 0xFFFF8000;
+ int dmx = READ16LE(&OAM[7 + rot]);
+ if(dmx & 0x8000)
+ dmx |= 0xFFFF8000;
+ int dy = READ16LE(&OAM[11 + rot]);
+ if(dy & 0x8000)
+ dy |= 0xFFFF8000;
+ int dmy = READ16LE(&OAM[15 + rot]);
+ if(dmy & 0x8000)
+ dmy |= 0xFFFF8000;
+
+ if(a0 & 0x1000)
+ t -= (t % mosaicY);
+
+ int realX = ((sizeX) << 7) - (fieldX >> 1)*dx + ((t - (fieldY>>1))* dmx);
+ int realY = ((sizeY) << 7) - (fieldX >> 1)*dy + ((t - (fieldY>>1))* dmy);
+
+ u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6);
+
+ int c = (a2 & 0x3FF);
+ if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512))
+ continue;
+
+ if(a0 & 0x2000)
+ {
+ int inc = 32;
+ if(io_registers[REG_DISPCNT] & 0x40)
+ inc = sizeX >> 2;
+ else
+ c &= 0x3FE;
+ for(u32 x = 0; x < fieldX; x++)
+ {
+ if (x >= startpix)
+ lineOBJpix-=2;
+ unsigned xxx = realX >> 8;
+ unsigned yyy = realY >> 8;
+ if(xxx < sizeX && yyy < sizeY && sx < 240)
+ {
+
+ u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5)
+ + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + (xxx & 7))&0x7FFF)];
+
+ if ((color==0) && (((prio >> 25)&3) < ((line[4][sx]>>25)&3)))
+ {
+ line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio;
+ if((a0 & 0x1000) && m)
+ line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio;
+ }
+ else if((color) && (prio < (line[4][sx]&0xFF000000)))
+ {
+ line[4][sx] = READ16LE(&spritePalette[color]) | prio;
+ if((a0 & 0x1000) && m)
+ line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio;
+ }
+
+ if ((a0 & 0x1000) && ((m+1) == mosaicX))
+ m = 0;
+ }
+ sx = (sx+1)&511;
+ realX += dx;
+ realY += dy;
+ }
+ }
+ else
+ {
+ int inc = 32;
+ if(io_registers[REG_DISPCNT] & 0x40)
+ inc = sizeX >> 3;
+ int palette = (a2 >> 8) & 0xF0;
+ for(u32 x = 0; x < fieldX; ++x)
+ {
+ if (x >= startpix)
+ lineOBJpix-=2;
+ unsigned xxx = realX >> 8;
+ unsigned yyy = realY >> 8;
+ if(xxx < sizeX && yyy < sizeY && sx < 240)
+ {
+
+ u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5)
+ + ((yyy & 7)<<2) + ((xxx >> 3)<<5)
+ + ((xxx & 7)>>1))&0x7FFF)];
+ if(xxx & 1)
+ color >>= 4;
+ else
+ color &= 0x0F;
+
+ if ((color==0) && (((prio >> 25)&3) <
+ ((line[4][sx]>>25)&3)))
+ {
+ line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio;
+ if((a0 & 0x1000) && m)
+ line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio;
+ }
+ else if((color) && (prio < (line[4][sx]&0xFF000000)))
+ {
+ line[4][sx] = READ16LE(&spritePalette[palette+color]) | prio;
+ if((a0 & 0x1000) && m)
+ line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio;
+ }
+ }
+ if((a0 & 0x1000) && m)
+ {
+ if (++m==mosaicX)
+ m=0;
+ }
+
+ sx = (sx+1)&511;
+ realX += dx;
+ realY += dy;
+
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if(sy+sizeY > 256)
+ sy -= 256;
+ int t = io_registers[REG_VCOUNT] - sy;
+ if(unsigned(t) < sizeY)
+ {
+ u32 startpix = 0;
+ if ((sx+sizeX)> 512)
+ startpix=512-sx;
+
+ if((sx < 240) || startpix)
+ {
+ lineOBJpix+=2;
+
+ if(a1 & 0x2000)
+ t = sizeY - t - 1;
+
+ int c = (a2 & 0x3FF);
+ if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512))
+ continue;
+
+ int inc = 32;
+ int xxx = 0;
+ if(a1 & 0x1000)
+ xxx = sizeX-1;
+
+ if(a0 & 0x1000)
+ t -= (t % mosaicY);
+
+ if(a0 & 0x2000)
+ {
+ if(io_registers[REG_DISPCNT] & 0x40)
+ inc = sizeX >> 2;
+ else
+ c &= 0x3FE;
+
+ int address = 0x10000 + ((((c+ (t>>3) * inc) << 5)
+ + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7)) & 0x7FFF);
+
+ if(a1 & 0x1000)
+ xxx = 7;
+ u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6);
+
+ for(u32 xx = 0; xx < sizeX; xx++)
+ {
+ if (xx >= startpix)
+ --lineOBJpix;
+ if(sx < 240)
+ {
+ u8 color = vram[address];
+ if ((color==0) && (((prio >> 25)&3) <
+ ((line[4][sx]>>25)&3)))
+ {
+ line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio;
+ if((a0 & 0x1000) && m)
+ line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio;
+ }
+ else if((color) && (prio < (line[4][sx]&0xFF000000)))
+ {
+ line[4][sx] = READ16LE(&spritePalette[color]) | prio;
+ if((a0 & 0x1000) && m)
+ line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio;
+ }
+
+ if ((a0 & 0x1000) && ((m+1) == mosaicX))
+ m = 0;
+ }
+
+ sx = (sx+1) & 511;
+ if(a1 & 0x1000)
+ {
+ --address;
+ if(--xxx == -1)
+ {
+ address -= 56;
+ xxx = 7;
+ }
+ if(address < 0x10000)
+ address += 0x8000;
+ }
+ else
+ {
+ ++address;
+ if(++xxx == 8)
+ {
+ address += 56;
+ xxx = 0;
+ }
+ if(address > 0x17fff)
+ address -= 0x8000;
+ }
+ }
+ }
+ else
+ {
+ if(io_registers[REG_DISPCNT] & 0x40)
+ inc = sizeX >> 3;
+
+ int address = 0x10000 + ((((c + (t>>3) * inc)<<5)
+ + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7FFF);
+
+ u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6);
+ int palette = (a2 >> 8) & 0xF0;
+ if(a1 & 0x1000)
+ {
+ xxx = 7;
+ int xx = sizeX - 1;
+ do
+ {
+ if (xx >= (int)(startpix))
+ --lineOBJpix;
+ //if (lineOBJpix<0)
+ // continue;
+ if(sx < 240)
+ {
+ u8 color = vram[address];
+ if(xx & 1)
+ color >>= 4;
+ else
+ color &= 0x0F;
+
+ if ((color==0) && (((prio >> 25)&3) <
+ ((line[4][sx]>>25)&3)))
+ {
+ line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio;
+ if((a0 & 0x1000) && m)
+ line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio;
+ }
+ else if((color) && (prio < (line[4][sx]&0xFF000000)))
+ {
+ line[4][sx] = READ16LE(&spritePalette[palette + color]) | prio;
+ if((a0 & 0x1000) && m)
+ line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio;
+ }
+ }
+
+ if ((a0 & 0x1000) && ((m+1) == mosaicX))
+ m=0;
+
+ sx = (sx+1) & 511;
+ if(!(xx & 1))
+ --address;
+ if(--xxx == -1)
+ {
+ xxx = 7;
+ address -= 28;
+ }
+ if(address < 0x10000)
+ address += 0x8000;
+ }while(--xx >= 0);
+ }
+ else
+ {
+ for(u32 xx = 0; xx < sizeX; ++xx)
+ {
+ if (xx >= startpix)
+ --lineOBJpix;
+ //if (lineOBJpix<0)
+ // continue;
+ if(sx < 240)
+ {
+ u8 color = vram[address];
+ if(xx & 1)
+ color >>= 4;
+ else
+ color &= 0x0F;
+
+ if ((color==0) && (((prio >> 25)&3) <
+ ((line[4][sx]>>25)&3)))
+ {
+ line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio;
+ if((a0 & 0x1000) && m)
+ line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio;
+ }
+ else if((color) && (prio < (line[4][sx]&0xFF000000)))
+ {
+ line[4][sx] = READ16LE(&spritePalette[palette + color]) | prio;
+ if((a0 & 0x1000) && m)
+ line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio;
+
+ }
+ }
+ if ((a0 & 0x1000) && ((m+1) == mosaicX))
+ m=0;
+
+ sx = (sx+1) & 511;
+ if(xx & 1)
+ ++address;
+ if(++xxx == 8)
+ {
+ address += 28;
+ xxx = 0;
+ }
+ if(address > 0x17fff)
+ address -= 0x8000;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+INLINE void gfxDrawOBJWin (void)
+{
+ u16 *sprites = (u16 *)oam;
+ for(int x = 0; x < 128 ; x++)
+ {
+ int lineOBJpix = lineOBJpixleft[x];
+ u16 a0 = READ16LE(sprites++);
+ u16 a1 = READ16LE(sprites++);
+ u16 a2 = READ16LE(sprites++);
+ sprites++;
+
+ if (lineOBJpix<=0)
+ return;
+
+ // ignores non OBJ-WIN and disabled OBJ-WIN
+ if(((a0 & 0x0c00) != 0x0800) || ((a0 & 0x0300) == 0x0200))
+ continue;
+
+ u16 a0val = a0>>14;
+
+ if ((a0 & 0x0c00) == 0x0c00)
+ a0 &=0xF3FF;
+
+ if (a0val == 3)
+ {
+ a0 &= 0x3FFF;
+ a1 &= 0x3FFF;
+ }
+
+ int sizeX = 8<<(a1>>14);
+ int sizeY = sizeX;
+
+ if (a0val & 1)
+ {
+#ifdef BRANCHLESS_GBA_GFX
+ sizeX <<= isel(-(sizeX & (~31u)), 1, 0);
+ sizeY >>= isel(-(sizeY>8), 0, 1);
+#else
+ if (sizeX<32)
+ sizeX<<=1;
+ if (sizeY>8)
+ sizeY>>=1;
+#endif
+ }
+ else if (a0val & 2)
+ {
+#ifdef BRANCHLESS_GBA_GFX
+ sizeX >>= isel(-(sizeX>8), 0, 1);
+ sizeY <<= isel(-(sizeY & (~31u)), 1, 0);
+#else
+ if (sizeX>8)
+ sizeX>>=1;
+ if (sizeY<32)
+ sizeY<<=1;
+#endif
+
+ }
+
+ int sy = (a0 & 255);
+
+ if(a0 & 0x0100)
+ {
+ int fieldX = sizeX;
+ int fieldY = sizeY;
+ if(a0 & 0x0200)
+ {
+ fieldX <<= 1;
+ fieldY <<= 1;
+ }
+ if((sy+fieldY) > 256)
+ sy -= 256;
+ int t = io_registers[REG_VCOUNT] - sy;
+ if((t >= 0) && (t < fieldY))
+ {
+ int sx = (a1 & 0x1FF);
+ int startpix = 0;
+ if ((sx+fieldX)> 512)
+ startpix=512-sx;
+
+ if((sx < 240) || startpix)
+ {
+ lineOBJpix-=8;
+ // int t2 = t - (fieldY >> 1);
+ int rot = (a1 >> 9) & 0x1F;
+ u16 *OAM = (u16 *)oam;
+ int dx = READ16LE(&OAM[3 + (rot << 4)]);
+ if(dx & 0x8000)
+ dx |= 0xFFFF8000;
+ int dmx = READ16LE(&OAM[7 + (rot << 4)]);
+ if(dmx & 0x8000)
+ dmx |= 0xFFFF8000;
+ int dy = READ16LE(&OAM[11 + (rot << 4)]);
+ if(dy & 0x8000)
+ dy |= 0xFFFF8000;
+ int dmy = READ16LE(&OAM[15 + (rot << 4)]);
+ if(dmy & 0x8000)
+ dmy |= 0xFFFF8000;
+
+ int realX = ((sizeX) << 7) - (fieldX >> 1)*dx - (fieldY>>1)*dmx
+ + t * dmx;
+ int realY = ((sizeY) << 7) - (fieldX >> 1)*dy - (fieldY>>1)*dmy
+ + t * dmy;
+
+ int c = (a2 & 0x3FF);
+ if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512))
+ continue;
+
+ int inc = 32;
+ bool condition1 = a0 & 0x2000;
+
+ if(io_registers[REG_DISPCNT] & 0x40)
+ inc = sizeX >> 3;
+
+ for(int x = 0; x < fieldX; x++)
+ {
+ bool cont = true;
+ if (x >= startpix)
+ lineOBJpix-=2;
+ if (lineOBJpix<0)
+ continue;
+ int xxx = realX >> 8;
+ int yyy = realY >> 8;
+
+ if(xxx < 0 || xxx >= sizeX || yyy < 0 || yyy >= sizeY || sx >= 240)
+ cont = false;
+
+ if(cont)
+ {
+ u32 color;
+ if(condition1)
+ color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5)
+ + ((yyy & 7)<<3) + ((xxx >> 3)<<6) +
+ (xxx & 7))&0x7fff)];
+ else
+ {
+ color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5)
+ + ((yyy & 7)<<2) + ((xxx >> 3)<<5) +
+ ((xxx & 7)>>1))&0x7fff)];
+ if(xxx & 1)
+ color >>= 4;
+ else
+ color &= 0x0F;
+ }
+
+ if(color)
+ line[5][sx] = 1;
+ }
+ sx = (sx+1)&511;
+ realX += dx;
+ realY += dy;
+ }
+ }
+ }
+ }
+ else
+ {
+ if((sy+sizeY) > 256)
+ sy -= 256;
+ int t = io_registers[REG_VCOUNT] - sy;
+ if((t >= 0) && (t < sizeY))
+ {
+ int sx = (a1 & 0x1FF);
+ int startpix = 0;
+ if ((sx+sizeX)> 512)
+ startpix=512-sx;
+
+ if((sx < 240) || startpix)
+ {
+ lineOBJpix+=2;
+ if(a1 & 0x2000)
+ t = sizeY - t - 1;
+ int c = (a2 & 0x3FF);
+ if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512))
+ continue;
+ if(a0 & 0x2000)
+ {
+
+ int inc = 32;
+ if(io_registers[REG_DISPCNT] & 0x40)
+ inc = sizeX >> 2;
+ else
+ c &= 0x3FE;
+
+ int xxx = 0;
+ if(a1 & 0x1000)
+ xxx = sizeX-1;
+ int address = 0x10000 + ((((c+ (t>>3) * inc) << 5)
+ + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7))&0x7fff);
+ if(a1 & 0x1000)
+ xxx = 7;
+ for(int xx = 0; xx < sizeX; xx++)
+ {
+ if (xx >= startpix)
+ lineOBJpix--;
+ if (lineOBJpix<0)
+ continue;
+ if(sx < 240)
+ {
+ u8 color = vram[address];
+ if(color)
+ line[5][sx] = 1;
+ }
+
+ sx = (sx+1) & 511;
+ if(a1 & 0x1000) {
+ xxx--;
+ address--;
+ if(xxx == -1) {
+ address -= 56;
+ xxx = 7;
+ }
+ if(address < 0x10000)
+ address += 0x8000;
+ } else {
+ xxx++;
+ address++;
+ if(xxx == 8) {
+ address += 56;
+ xxx = 0;
+ }
+ if(address > 0x17fff)
+ address -= 0x8000;
+ }
+ }
+ }
+ else
+ {
+ int inc = 32;
+ if(io_registers[REG_DISPCNT] & 0x40)
+ inc = sizeX >> 3;
+ int xxx = 0;
+ if(a1 & 0x1000)
+ xxx = sizeX - 1;
+ int address = 0x10000 + ((((c + (t>>3) * inc)<<5)
+ + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7fff);
+ // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6);
+ // int palette = (a2 >> 8) & 0xF0;
+ if(a1 & 0x1000)
+ {
+ xxx = 7;
+ for(int xx = sizeX - 1; xx >= 0; xx--)
+ {
+ if (xx >= startpix)
+ lineOBJpix--;
+ if (lineOBJpix<0)
+ continue;
+ if(sx < 240)
+ {
+ u8 color = vram[address];
+ if(xx & 1)
+ color = (color >> 4);
+ else
+ color &= 0x0F;
+
+ if(color)
+ line[5][sx] = 1;
+ }
+ sx = (sx+1) & 511;
+ xxx--;
+ if(!(xx & 1))
+ address--;
+ if(xxx == -1) {
+ xxx = 7;
+ address -= 28;
+ }
+ if(address < 0x10000)
+ address += 0x8000;
+ }
+ }
+ else
+ {
+ for(int xx = 0; xx < sizeX; xx++)
+ {
+ if (xx >= startpix)
+ lineOBJpix--;
+ if (lineOBJpix<0)
+ continue;
+ if(sx < 240)
+ {
+ u8 color = vram[address];
+ if(xx & 1)
+ color = (color >> 4);
+ else
+ color &= 0x0F;
+
+ if(color)
+ line[5][sx] = 1;
+ }
+ sx = (sx+1) & 511;
+ xxx++;
+ if(xx & 1)
+ address++;
+ if(xxx == 8) {
+ address += 28;
+ xxx = 0;
+ }
+ if(address > 0x17fff)
+ address -= 0x8000;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+INLINE u32 gfxIncreaseBrightness(u32 color, int coeff)
+{
+ color = (((color & 0xffff) << 16) | (color & 0xffff)) & 0x3E07C1F;
+
+ color += ((((0x3E07C1F - color) * coeff) >> 4) & 0x3E07C1F);
+
+ return (color >> 16) | color;
+}
+
+INLINE u32 gfxDecreaseBrightness(u32 color, int coeff)
+{
+ color = (((color & 0xffff) << 16) | (color & 0xffff)) & 0x3E07C1F;
+
+ color -= (((color * coeff) >> 4) & 0x3E07C1F);
+
+ return (color >> 16) | color;
+}
+
+#define GFX_ALPHA_BLEND(color, color2, ca, cb) \
+ int r = AlphaClampLUT[(((color & 0x1F) * ca) >> 4) + (((color2 & 0x1F) * cb) >> 4)]; \
+ int g = AlphaClampLUT[((((color >> 5) & 0x1F) * ca) >> 4) + ((((color2 >> 5) & 0x1F) * cb) >> 4)]; \
+ int b = AlphaClampLUT[((((color >> 10) & 0x1F) * ca) >> 4) + ((((color2 >> 10) & 0x1F) * cb) >> 4)]; \
+ color = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r;
+
+/*============================================================
+ GBA.CPP
+============================================================ */
+int saveType;
+static const bool useBios = true;
+bool skipBios;
+bool cpuIsMultiBoot;
+int cpuSaveType;
+bool enableRtc;
+bool mirroringEnable;
+bool skipSaveGameBattery;
+
+int cpuDmaCount;
+
+uint8_t *bios;
+uint8_t *rom;
+uint8_t *internalRAM;
+uint8_t *workRAM;
+uint8_t *vram;
+u16 *pix;
+uint8_t *oam;
+uint8_t *ioMem;
+
+#ifdef USE_SWITICKS
+int SWITicks = 0;
+#endif
+
+bool cpuSramEnabled; // = true;
+bool cpuFlashEnabled; // = true;
+bool cpuEEPROMEnabled; // = true;
+bool cpuEEPROMSensorEnabled; // = false;
+
+#ifndef LSB_FIRST
+bool cpuBiosSwapped = false;
+#endif
+
+INLINE int CPUUpdateTicks (void)
+{
+ int cpuLoopTicks = graphics.lcdTicks;
+
+ if(soundTicks < cpuLoopTicks)
+ cpuLoopTicks = soundTicks;
+
+ if(timer0On && (timer0Ticks < cpuLoopTicks))
+ cpuLoopTicks = timer0Ticks;
+
+ if(timer1On && !(io_registers[REG_TM1CNT] & 4) && (timer1Ticks < cpuLoopTicks))
+ cpuLoopTicks = timer1Ticks;
+
+ if(timer2On && !(io_registers[REG_TM2CNT] & 4) && (timer2Ticks < cpuLoopTicks))
+ cpuLoopTicks = timer2Ticks;
+
+ if(timer3On && !(io_registers[REG_TM3CNT] & 4) && (timer3Ticks < cpuLoopTicks))
+ cpuLoopTicks = timer3Ticks;
+
+#ifdef USE_SWITICKS
+ if (SWITicks)
+ {
+ if (SWITicks < cpuLoopTicks)
+ cpuLoopTicks = SWITicks;
+ }
+#endif
+
+ if (IRQTicks)
+ {
+ if (IRQTicks < cpuLoopTicks)
+ cpuLoopTicks = IRQTicks;
+ }
+
+ return cpuLoopTicks;
+}
+
+#define CPUUpdateWindow0() \
+{ \
+ int x00_window0 = io_registers[REG_WIN0H] >>8; \
+ int x01_window0 = io_registers[REG_WIN0H] & 255; \
+ int x00_lte_x01 = x00_window0 <= x01_window0; \
+ for(int i = 0; i < 240; i++) \
+ gfxInWin[0][i] = ((i >= x00_window0 && i < x01_window0) & x00_lte_x01) | ((i >= x00_window0 || i < x01_window0) & ~x00_lte_x01); \
+}
+
+#define CPUUpdateWindow1() \
+{ \
+ int x00_window1 = io_registers[REG_WIN1H]>>8; \
+ int x01_window1 = io_registers[REG_WIN1H] & 255; \
+ int x00_lte_x01 = x00_window1 <= x01_window1; \
+ for(int i = 0; i < 240; i++) \
+ gfxInWin[1][i] = ((i >= x00_window1 && i < x01_window1) & x00_lte_x01) | ((i >= x00_window1 || i < x01_window1) & ~x00_lte_x01); \
+}
+
+#define CPUCompareVCOUNT() \
+ if(io_registers[REG_VCOUNT] == (io_registers[REG_DISPSTAT] >> 8)) \
+ { \
+ io_registers[REG_DISPSTAT] |= 4; \
+ UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); \
+ if(io_registers[REG_DISPSTAT] & 0x20) \
+ { \
+ io_registers[REG_IF] |= 4; \
+ UPDATE_REG(0x202, io_registers[REG_IF]); \
+ } \
+ } \
+ else \
+ { \
+ io_registers[REG_DISPSTAT] &= 0xFFFB; \
+ UPDATE_REG(0x4, io_registers[REG_DISPSTAT]); \
+ } \
+ if (graphics.layerEnableDelay > 0) \
+ { \
+ graphics.layerEnableDelay--; \
+ if (graphics.layerEnableDelay == 1) \
+ graphics.layerEnable = io_registers[REG_DISPCNT]; \
+ }
+
+// TODO: Batteryram stuffs
+/*
+bool CPUWriteBatteryFile(const char *fileName)
+{
+ if(gbaSaveType == 0)
+ {
+ if(eepromInUse)
+ gbaSaveType = 3;
+ else
+ switch(saveType)
+ {
+ case 1:
+ gbaSaveType = 1;
+ break;
+ case 2:
+ gbaSaveType = 2;
+ break;
+ }
+ }
+
+ if((gbaSaveType) && (gbaSaveType!=5))
+ {
+ FILE *file = fopen(fileName, "wb");
+
+ if(!file) {
+ systemMessage("Error creating file %s", fileName);
+ return false;
+ }
+
+ // only save if Flash/Sram in use or EEprom in use
+ if(gbaSaveType != 3) {
+ if(gbaSaveType == 2) {
+ if(fwrite(flashSaveMemory, 1, flashSize, file) != (size_t)flashSize) {
+ fclose(file);
+ return false;
+ }
+ } else {
+ if(fwrite(flashSaveMemory, 1, 0x10000, file) != 0x10000) {
+ fclose(file);
+ return false;
+ }
+ }
+ } else {
+ if(fwrite(eepromData, 1, eepromSize, file) != (size_t)eepromSize) {
+ fclose(file);
+ return false;
+ }
+ }
+ fclose(file);
+ }
+ return true;
+}
+
+bool CPUReadBatteryFile(const char *fileName)
+{
+ FILE *file = fopen(fileName, "rb");
+
+ if(!file)
+ return false;
+
+ // check file size to know what we should read
+ fseek(file, 0, SEEK_END);
+
+ long size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ if(size == 512 || size == 0x2000) {
+ if(fread(eepromData, 1, size, file) != (size_t)size) {
+ fclose(file);
+ return false;
+ }
+ } else {
+ if(size == 0x20000) {
+ if(fread(flashSaveMemory, 1, 0x20000, file) != 0x20000) {
+ fclose(file);
+ return false;
+ }
+ flashSetSize(0x20000);
+ } else {
+ if(fread(flashSaveMemory, 1, 0x10000, file) != 0x10000) {
+ fclose(file);
+ return false;
+ }
+ flashSetSize(0x10000);
+ }
+ }
+ fclose(file);
+ return true;
+}
+*/
+void CPUCleanUp (void)
+{
+ if(rom != NULL) {
+ free(rom);
+ rom = NULL;
+ }
+
+ if(vram != NULL) {
+ free(vram);
+ vram = NULL;
+ }
+
+ if(graphics.paletteRAM != NULL) {
+ free(graphics.paletteRAM);
+ graphics.paletteRAM = NULL;
+ }
+
+ if(internalRAM != NULL) {
+ free(internalRAM);
+ internalRAM = NULL;
+ }
+
+ if(workRAM != NULL) {
+ free(workRAM);
+ workRAM = NULL;
+ }
+
+ if(bios != NULL) {
+ free(bios);
+ bios = NULL;
+ }
+
+ if(pix != NULL) {
+ free(pix);
+ pix = NULL;
+ }
+
+ if(oam != NULL) {
+ free(oam);
+ oam = NULL;
+ }
+
+ if(ioMem != NULL) {
+ free(ioMem);
+ ioMem = NULL;
+ }
+}
+
+int CPULoadRom(const u8 *romfile, const u32 romfilelen)
+{
+ if (cpuIsMultiBoot)
+ {
+ if (romfilelen > 0x40000)
+ return 0;
+ }
+ else
+ {
+ if (romfilelen > 0x2000000)
+ return 0;
+ }
+
+ romSize = 0x2000000;
+ if(rom != NULL)
+ CPUCleanUp();
+
+ rom = (uint8_t *)malloc(0x2000000);
+
+ if(rom == NULL)
+ return 0;
+
+ workRAM = (uint8_t *)calloc(1, 0x40000);
+
+ if(workRAM == NULL)
+ return 0;
+
+ uint8_t *whereToLoad = cpuIsMultiBoot ? workRAM : rom;
+
+ memcpy(whereToLoad, romfile, romfilelen);
+ romSize = romfilelen;
+
+ uint16_t *temp = (uint16_t *)(rom+((romSize+1)&~1));
+ int i;
+ for(i = (romSize+1)&~1; i < 0x2000000; i+=2) {
+ WRITE16LE(temp, (i >> 1) & 0xFFFF);
+ temp++;
+ }
+
+ bios = (uint8_t *)calloc(1,0x4000);
+ if(bios == NULL) {
+ CPUCleanUp();
+ return 0;
+ }
+ internalRAM = (uint8_t *)calloc(1,0x8000);
+ if(internalRAM == NULL) {
+ CPUCleanUp();
+ return 0;
+ }
+ graphics.paletteRAM = (uint8_t *)calloc(1,0x400);
+ if(graphics.paletteRAM == NULL) {
+ CPUCleanUp();
+ return 0;
+ }
+ vram = (uint8_t *)calloc(1, 0x20000);
+ if(vram == NULL) {
+ CPUCleanUp();
+ return 0;
+ }
+ oam = (uint8_t *)calloc(1, 0x400);
+ if(oam == NULL) {
+ CPUCleanUp();
+ return 0;
+ }
+ pix = (u16 *)calloc(1, 4 * PIX_BUFFER_SCREEN_WIDTH * 160);
+ if(pix == NULL) {
+ CPUCleanUp();
+ return 0;
+ }
+ ioMem = (uint8_t *)calloc(1, 0x400);
+ if(ioMem == NULL) {
+ CPUCleanUp();
+ return 0;
+ }
+
+ flashInit();
+ eepromInit();
+
+ memset(line[0], -1, 240 * sizeof(u32));
+ memset(line[1], -1, 240 * sizeof(u32));
+ memset(line[2], -1, 240 * sizeof(u32));
+ memset(line[3], -1, 240 * sizeof(u32));
+
+ return romSize;
+}
+
+void doMirroring (bool b)
+{
+ uint32_t mirroredRomSize = (((romSize)>>20) & 0x3F)<<20;
+ uint32_t mirroredRomAddress = romSize;
+ if ((mirroredRomSize <=0x800000) && (b))
+ {
+ mirroredRomAddress = mirroredRomSize;
+ if (mirroredRomSize==0)
+ mirroredRomSize=0x100000;
+ while (mirroredRomAddress<0x01000000)
+ {
+ memcpy((uint16_t *)(rom+mirroredRomAddress), (uint16_t *)(rom), mirroredRomSize);
+ mirroredRomAddress+=mirroredRomSize;
+ }
+ }
+}
+
+#define brightness_switch() \
+ switch((BLDMOD >> 6) & 3) \
+ { \
+ case 2: \
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); \
+ break; \
+ case 3: \
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); \
+ break; \
+ }
+
+#define alpha_blend_brightness_switch() \
+ if(top2 & (BLDMOD>>8)) \
+ if(color < 0x80000000) \
+ { \
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); \
+ } \
+ else if(BLDMOD & top) \
+ { \
+ brightness_switch(); \
+ }
+
+/* we only use 16bit color depth */
+#define INIT_COLOR_DEPTH_LINE_MIX() uint16_t * lineMix = (pix + PIX_BUFFER_SCREEN_WIDTH * io_registers[REG_VCOUNT])
+
+void mode0RenderLine (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 0: Render Line\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ if(graphics.layerEnable & 0x0100) {
+ gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]);
+ }
+
+ if(graphics.layerEnable & 0x0200) {
+ gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]);
+ }
+
+ if(graphics.layerEnable & 0x0400) {
+ gfxDrawTextScreen(io_registers[REG_BG2CNT], io_registers[REG_BG2HOFS], io_registers[REG_BG2VOFS], line[2]);
+ }
+
+ if(graphics.layerEnable & 0x0800) {
+ gfxDrawTextScreen(io_registers[REG_BG3CNT], io_registers[REG_BG3HOFS], io_registers[REG_BG3VOFS], line[3]);
+ }
+
+
+ uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(int x = 0; x < 240; x++)
+ {
+ uint32_t color = backdrop;
+ uint8_t top = 0x20;
+
+ if(line[0][x] < color) {
+ color = line[0][x];
+ top = 0x01;
+ }
+
+ if((uint8_t)(line[1][x]>>24) < (uint8_t)(color >> 24)) {
+ color = line[1][x];
+ top = 0x02;
+ }
+
+ if((uint8_t)(line[2][x]>>24) < (uint8_t)(color >> 24)) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((uint8_t)(line[3][x]>>24) < (uint8_t)(color >> 24)) {
+ color = line[3][x];
+ top = 0x08;
+ }
+
+ if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) {
+ color = line[4][x];
+ top = 0x10;
+
+ if(color & 0x00010000) {
+ // semi-transparent OBJ
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if((uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[0][x];
+ top2 = 0x01;
+ }
+
+ if((uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[1][x];
+ top2 = 0x02;
+ }
+
+ if((uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[3][x];
+ top2 = 0x08;
+ }
+
+ alpha_blend_brightness_switch();
+ }
+ }
+
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+}
+
+void mode0RenderLineNoWindow (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 0: Render Line No Window\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+ uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000);
+
+ if(graphics.layerEnable & 0x0100) {
+ gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]);
+ }
+
+ if(graphics.layerEnable & 0x0200) {
+ gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]);
+ }
+
+ if(graphics.layerEnable & 0x0400) {
+ gfxDrawTextScreen(io_registers[REG_BG2CNT], io_registers[REG_BG2HOFS], io_registers[REG_BG2VOFS], line[2]);
+ }
+
+ if(graphics.layerEnable & 0x0800) {
+ gfxDrawTextScreen(io_registers[REG_BG3CNT], io_registers[REG_BG3HOFS], io_registers[REG_BG3VOFS], line[3]);
+ }
+
+ int effect = (BLDMOD >> 6) & 3;
+
+ for(int x = 0; x < 240; x++) {
+ uint32_t color = backdrop;
+ uint8_t top = 0x20;
+
+ if(line[0][x] < color) {
+ color = line[0][x];
+ top = 0x01;
+ }
+
+ if(line[1][x] < (color & 0xFF000000)) {
+ color = line[1][x];
+ top = 0x02;
+ }
+
+ if(line[2][x] < (color & 0xFF000000)) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if(line[3][x] < (color & 0xFF000000)) {
+ color = line[3][x];
+ top = 0x08;
+ }
+
+ if(line[4][x] < (color & 0xFF000000)) {
+ color = line[4][x];
+ top = 0x10;
+ }
+
+ if(!(color & 0x00010000)) {
+ switch(effect) {
+ case 0:
+ break;
+ case 1:
+ if(top & BLDMOD)
+ {
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+ if((line[0][x] < back) && (top != 0x01))
+ {
+ back = line[0][x];
+ top2 = 0x01;
+ }
+
+ if((line[1][x] < (back & 0xFF000000)) && (top != 0x02))
+ {
+ back = line[1][x];
+ top2 = 0x02;
+ }
+
+ if((line[2][x] < (back & 0xFF000000)) && (top != 0x04))
+ {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((line[3][x] < (back & 0xFF000000)) && (top != 0x08))
+ {
+ back = line[3][x];
+ top2 = 0x08;
+ }
+
+ if((line[4][x] < (back & 0xFF000000)) && (top != 0x10))
+ {
+ back = line[4][x];
+ top2 = 0x10;
+ }
+
+ if(top2 & (BLDMOD>>8) && color < 0x80000000)
+ {
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]);
+ }
+
+ }
+ break;
+ case 2:
+ if(BLDMOD & top)
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ case 3:
+ if(BLDMOD & top)
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ }
+ } else {
+ // semi-transparent OBJ
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if(line[0][x] < back) {
+ back = line[0][x];
+ top2 = 0x01;
+ }
+
+ if(line[1][x] < (back & 0xFF000000)) {
+ back = line[1][x];
+ top2 = 0x02;
+ }
+
+ if(line[2][x] < (back & 0xFF000000)) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if(line[3][x] < (back & 0xFF000000)) {
+ back = line[3][x];
+ top2 = 0x08;
+ }
+
+ alpha_blend_brightness_switch();
+ }
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+}
+
+void mode0RenderLineAll (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 0: Render Line All\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ bool inWindow0 = false;
+ bool inWindow1 = false;
+
+ if(graphics.layerEnable & 0x2000) {
+ uint8_t v0 = io_registers[REG_WIN0V] >> 8;
+ uint8_t v1 = io_registers[REG_WIN0V] & 255;
+ inWindow0 = ((v0 == v1) && (v0 >= 0xe8));
+ if(v1 >= v0)
+ inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1);
+ else
+ inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1);
+ }
+ if(graphics.layerEnable & 0x4000) {
+ uint8_t v0 = io_registers[REG_WIN1V] >> 8;
+ uint8_t v1 = io_registers[REG_WIN1V] & 255;
+ inWindow1 = ((v0 == v1) && (v0 >= 0xe8));
+ if(v1 >= v0)
+ inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1);
+ else
+ inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1);
+ }
+
+ if((graphics.layerEnable & 0x0100)) {
+ gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]);
+ }
+
+ if((graphics.layerEnable & 0x0200)) {
+ gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]);
+ }
+
+ if((graphics.layerEnable & 0x0400)) {
+ gfxDrawTextScreen(io_registers[REG_BG2CNT], io_registers[REG_BG2HOFS], io_registers[REG_BG2VOFS], line[2]);
+ }
+
+ if((graphics.layerEnable & 0x0800)) {
+ gfxDrawTextScreen(io_registers[REG_BG3CNT], io_registers[REG_BG3HOFS], io_registers[REG_BG3VOFS], line[3]);
+ }
+
+ uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000);
+
+ uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF;
+ uint8_t inWin1Mask = io_registers[REG_WININ] >> 8;
+ uint8_t outMask = io_registers[REG_WINOUT] & 0xFF;
+
+ for(int x = 0; x < 240; x++) {
+ uint32_t color = backdrop;
+ uint8_t top = 0x20;
+ uint8_t mask = outMask;
+
+ if(!(line[5][x] & 0x80000000)) {
+ mask = io_registers[REG_WINOUT] >> 8;
+ }
+
+ int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31;
+ int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31;
+ mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask);
+ mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask);
+
+ if((mask & 1) && (line[0][x] < color)) {
+ color = line[0][x];
+ top = 0x01;
+ }
+
+ if((mask & 2) && ((uint8_t)(line[1][x]>>24) < (uint8_t)(color >> 24))) {
+ color = line[1][x];
+ top = 0x02;
+ }
+
+ if((mask & 4) && ((uint8_t)(line[2][x]>>24) < (uint8_t)(color >> 24))) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((mask & 8) && ((uint8_t)(line[3][x]>>24) < (uint8_t)(color >> 24))) {
+ color = line[3][x];
+ top = 0x08;
+ }
+
+ if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24))) {
+ color = line[4][x];
+ top = 0x10;
+ }
+
+ if(color & 0x00010000)
+ {
+ // semi-transparent OBJ
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if((mask & 1) && ((uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24))) {
+ back = line[0][x];
+ top2 = 0x01;
+ }
+
+ if((mask & 2) && ((uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24))) {
+ back = line[1][x];
+ top2 = 0x02;
+ }
+
+ if((mask & 4) && ((uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24))) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((mask & 8) && ((uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24))) {
+ back = line[3][x];
+ top2 = 0x08;
+ }
+
+ alpha_blend_brightness_switch();
+ }
+ else if((mask & 32) && (top & BLDMOD))
+ {
+ // special FX on in the window
+ switch((BLDMOD >> 6) & 3)
+ {
+ case 0:
+ break;
+ case 1:
+ {
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+ if(((mask & 1) && (uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24)) && top != 0x01)
+ {
+ back = line[0][x];
+ top2 = 0x01;
+ }
+
+ if(((mask & 2) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) && top != 0x02)
+ {
+ back = line[1][x];
+ top2 = 0x02;
+ }
+
+ if(((mask & 4) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) && top != 0x04)
+ {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if(((mask & 8) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) && top != 0x08)
+ {
+ back = line[3][x];
+ top2 = 0x08;
+ }
+
+ if(((mask & 16) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) && top != 0x10) {
+ back = line[4][x];
+ top2 = 0x10;
+ }
+
+ if(top2 & (BLDMOD>>8) && color < 0x80000000)
+ {
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]);
+ }
+ }
+ break;
+ case 2:
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ case 3:
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ }
+ }
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+}
+
+/*
+Mode 1 is a tiled graphics mode, but with background layer 2 supporting scaling and rotation.
+There is no layer 3 in this mode.
+Layers 0 and 1 can be either 16 colours (with 16 different palettes) or 256 colours.
+There are 1024 tiles available.
+Layer 2 is 256 colours and allows only 256 tiles.
+
+These routines only render a single line at a time, because of the way the GBA does events.
+*/
+
+void mode1RenderLine (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 1: Render Line\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ if(graphics.layerEnable & 0x0100) {
+ gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]);
+ }
+
+ if(graphics.layerEnable & 0x0200) {
+ gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]);
+ }
+
+ if(graphics.layerEnable & 0x0400) {
+ int changed = gfxBG2Changed;
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+ gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H,
+ io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD],
+ gfxBG2X, gfxBG2Y, changed, line[2]);
+ }
+
+ uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(uint32_t x = 0; x < 240u; ++x) {
+ uint32_t color = backdrop;
+ uint8_t top = 0x20;
+
+ uint8_t li1 = (uint8_t)(line[1][x]>>24);
+ uint8_t li2 = (uint8_t)(line[2][x]>>24);
+ uint8_t li4 = (uint8_t)(line[4][x]>>24);
+
+ uint8_t r = (li2 < li1) ? (li2) : (li1);
+
+ if(li4 < r){
+ r = (li4);
+ }
+
+ if(line[0][x] < backdrop) {
+ color = line[0][x];
+ top = 0x01;
+ }
+
+ if(r < (uint8_t)(color >> 24)) {
+ if(r == li1){
+ color = line[1][x];
+ top = 0x02;
+ }else if(r == li2){
+ color = line[2][x];
+ top = 0x04;
+ }else if(r == li4){
+ color = line[4][x];
+ top = 0x10;
+ if((color & 0x00010000))
+ {
+ // semi-transparent OBJ
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ uint8_t li0 = (uint8_t)(line[0][x]>>24);
+ uint8_t li1 = (uint8_t)(line[1][x]>>24);
+ uint8_t li2 = (uint8_t)(line[2][x]>>24);
+ uint8_t r = (li1 < li0) ? (li1) : (li0);
+
+ if(li2 < r) {
+ r = (li2);
+ }
+
+ if(r < (uint8_t)(back >> 24)) {
+ if(r == li0){
+ back = line[0][x];
+ top2 = 0x01;
+ }else if(r == li1){
+ back = line[1][x];
+ top2 = 0x02;
+ }else if(r == li2){
+ back = line[2][x];
+ top2 = 0x04;
+ }
+ }
+
+ alpha_blend_brightness_switch();
+ }
+ }
+ }
+
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+void mode1RenderLineNoWindow (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 1: Render Line No Window\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ if(graphics.layerEnable & 0x0100) {
+ gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]);
+ }
+
+
+ if(graphics.layerEnable & 0x0200) {
+ gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]);
+ }
+
+ if(graphics.layerEnable & 0x0400) {
+ int changed = gfxBG2Changed;
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+ gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H,
+ io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD],
+ gfxBG2X, gfxBG2Y, changed, line[2]);
+ }
+
+ uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(int x = 0; x < 240; ++x) {
+ uint32_t color = backdrop;
+ uint8_t top = 0x20;
+
+ uint8_t li1 = (uint8_t)(line[1][x]>>24);
+ uint8_t li2 = (uint8_t)(line[2][x]>>24);
+ uint8_t li4 = (uint8_t)(line[4][x]>>24);
+
+ uint8_t r = (li2 < li1) ? (li2) : (li1);
+
+ if(li4 < r){
+ r = (li4);
+ }
+
+ if(line[0][x] < backdrop) {
+ color = line[0][x];
+ top = 0x01;
+ }
+
+ if(r < (uint8_t)(color >> 24)) {
+ if(r == li1){
+ color = line[1][x];
+ top = 0x02;
+ }else if(r == li2){
+ color = line[2][x];
+ top = 0x04;
+ }else if(r == li4){
+ color = line[4][x];
+ top = 0x10;
+ }
+ }
+
+ if(!(color & 0x00010000)) {
+ switch((BLDMOD >> 6) & 3) {
+ case 0:
+ break;
+ case 1:
+ if(top & BLDMOD)
+ {
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if((top != 0x01) && (uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[0][x];
+ top2 = 0x01;
+ }
+
+ if((top != 0x02) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[1][x];
+ top2 = 0x02;
+ }
+
+ if((top != 0x04) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[4][x];
+ top2 = 0x10;
+ }
+
+ if(top2 & (BLDMOD>>8) && color < 0x80000000)
+ {
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]);
+ }
+ }
+ break;
+ case 2:
+ if(BLDMOD & top)
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ case 3:
+ if(BLDMOD & top)
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ }
+ } else {
+ // semi-transparent OBJ
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ uint8_t li0 = (uint8_t)(line[0][x]>>24);
+ uint8_t li1 = (uint8_t)(line[1][x]>>24);
+ uint8_t li2 = (uint8_t)(line[2][x]>>24);
+
+ uint8_t r = (li1 < li0) ? (li1) : (li0);
+
+ if(li2 < r) {
+ r = (li2);
+ }
+
+ if(r < (uint8_t)(back >> 24))
+ {
+ if(r == li0)
+ {
+ back = line[0][x];
+ top2 = 0x01;
+ }
+ else if(r == li1)
+ {
+ back = line[1][x];
+ top2 = 0x02;
+ }
+ else if(r == li2)
+ {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+ }
+
+ alpha_blend_brightness_switch();
+ }
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+void mode1RenderLineAll (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 1: Render Line All\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ bool inWindow0 = false;
+ bool inWindow1 = false;
+
+ if(graphics.layerEnable & 0x2000)
+ {
+ uint8_t v0 = io_registers[REG_WIN0V] >> 8;
+ uint8_t v1 = io_registers[REG_WIN0V] & 255;
+ inWindow0 = ((v0 == v1) && (v0 >= 0xe8));
+#ifndef ORIGINAL_BRANCHES
+ uint32_t condition = v1 >= v0;
+ int32_t condition_mask = ((condition) | -(condition)) >> 31;
+ inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask))));
+#else
+ if(v1 >= v0)
+ inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1);
+ else
+ inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1);
+#endif
+ }
+ if(graphics.layerEnable & 0x4000)
+ {
+ uint8_t v0 = io_registers[REG_WIN1V] >> 8;
+ uint8_t v1 = io_registers[REG_WIN1V] & 255;
+ inWindow1 = ((v0 == v1) && (v0 >= 0xe8));
+#ifndef ORIGINAL_BRANCHES
+ uint32_t condition = v1 >= v0;
+ int32_t condition_mask = ((condition) | -(condition)) >> 31;
+ inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask))));
+#else
+ if(v1 >= v0)
+ inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1);
+ else
+ inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1);
+#endif
+ }
+
+ if(graphics.layerEnable & 0x0100) {
+ gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]);
+ }
+
+ if(graphics.layerEnable & 0x0200) {
+ gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]);
+ }
+
+ if(graphics.layerEnable & 0x0400) {
+ int changed = gfxBG2Changed;
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+ gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H,
+ io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD],
+ gfxBG2X, gfxBG2Y, changed, line[2]);
+ }
+
+ uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000);
+
+ uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF;
+ uint8_t inWin1Mask = io_registers[REG_WININ] >> 8;
+ uint8_t outMask = io_registers[REG_WINOUT] & 0xFF;
+
+ for(int x = 0; x < 240; ++x) {
+ uint32_t color = backdrop;
+ uint8_t top = 0x20;
+ uint8_t mask = outMask;
+
+ if(!(line[5][x] & 0x80000000)) {
+ mask = io_registers[REG_WINOUT] >> 8;
+ }
+
+ int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31;
+ int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31;
+ mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask);
+ mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask);
+
+ // At the very least, move the inexpensive 'mask' operation up front
+ if((mask & 1) && line[0][x] < backdrop) {
+ color = line[0][x];
+ top = 0x01;
+ }
+
+ if((mask & 2) && (uint8_t)(line[1][x]>>24) < (uint8_t)(color >> 24)) {
+ color = line[1][x];
+ top = 0x02;
+ }
+
+ if((mask & 4) && (uint8_t)(line[2][x]>>24) < (uint8_t)(color >> 24)) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((mask & 16) && (uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) {
+ color = line[4][x];
+ top = 0x10;
+ }
+
+ if(color & 0x00010000) {
+ // semi-transparent OBJ
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if((mask & 1) && (uint8_t)(line[0][x]>>24) < (uint8_t)(backdrop >> 24)) {
+ back = line[0][x];
+ top2 = 0x01;
+ }
+
+ if((mask & 2) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[1][x];
+ top2 = 0x02;
+ }
+
+ if((mask & 4) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ alpha_blend_brightness_switch();
+ } else if(mask & 32) {
+ // special FX on the window
+ switch((BLDMOD >> 6) & 3) {
+ case 0:
+ break;
+ case 1:
+ if(top & BLDMOD)
+ {
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if((mask & 1) && (top != 0x01) && (uint8_t)(line[0][x]>>24) < (uint8_t)(backdrop >> 24)) {
+ back = line[0][x];
+ top2 = 0x01;
+ }
+
+ if((mask & 2) && (top != 0x02) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[1][x];
+ top2 = 0x02;
+ }
+
+ if((mask & 4) && (top != 0x04) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[4][x];
+ top2 = 0x10;
+ }
+
+ if(top2 & (BLDMOD>>8) && color < 0x80000000)
+ {
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]);
+ }
+ }
+ break;
+ case 2:
+ if(BLDMOD & top)
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ case 3:
+ if(BLDMOD & top)
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ }
+ }
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+/*
+Mode 2 is a 256 colour tiled graphics mode which supports scaling and rotation.
+There is no background layer 0 or 1 in this mode. Only background layers 2 and 3.
+There are 256 tiles available.
+It does not support flipping.
+
+These routines only render a single line at a time, because of the way the GBA does events.
+*/
+
+void mode2RenderLine (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 2: Render Line\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ if(graphics.layerEnable & 0x0400) {
+ int changed = gfxBG2Changed;
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H,
+ io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], gfxBG2X, gfxBG2Y,
+ changed, line[2]);
+ }
+
+ if(graphics.layerEnable & 0x0800) {
+ int changed = gfxBG3Changed;
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen(io_registers[REG_BG3CNT], BG3X_L, BG3X_H, BG3Y_L, BG3Y_H,
+ io_registers[REG_BG3PA], io_registers[REG_BG3PB], io_registers[REG_BG3PC], io_registers[REG_BG3PD], gfxBG3X, gfxBG3Y,
+ changed, line[3]);
+ }
+
+ uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(int x = 0; x < 240; ++x) {
+ uint32_t color = backdrop;
+ uint8_t top = 0x20;
+
+ uint8_t li2 = (uint8_t)(line[2][x]>>24);
+ uint8_t li3 = (uint8_t)(line[3][x]>>24);
+ uint8_t li4 = (uint8_t)(line[4][x]>>24);
+
+ uint8_t r = (li3 < li2) ? (li3) : (li2);
+
+ if(li4 < r){
+ r = (li4);
+ }
+
+ if(r < (uint8_t)(color >> 24)) {
+ if(r == li2){
+ color = line[2][x];
+ top = 0x04;
+ }else if(r == li3){
+ color = line[3][x];
+ top = 0x08;
+ }else if(r == li4){
+ color = line[4][x];
+ top = 0x10;
+
+ if(color & 0x00010000) {
+ // semi-transparent OBJ
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ uint8_t li2 = (uint8_t)(line[2][x]>>24);
+ uint8_t li3 = (uint8_t)(line[3][x]>>24);
+ uint8_t r = (li3 < li2) ? (li3) : (li2);
+
+ if(r < (uint8_t)(back >> 24)) {
+ if(r == li2){
+ back = line[2][x];
+ top2 = 0x04;
+ }else if(r == li3){
+ back = line[3][x];
+ top2 = 0x08;
+ }
+ }
+
+ alpha_blend_brightness_switch();
+ }
+ }
+ }
+
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ gfxBG3Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+void mode2RenderLineNoWindow (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 2: Render Line No Window\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ if(graphics.layerEnable & 0x0400) {
+ int changed = gfxBG2Changed;
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H,
+ io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], gfxBG2X, gfxBG2Y,
+ changed, line[2]);
+ }
+
+ if(graphics.layerEnable & 0x0800) {
+ int changed = gfxBG3Changed;
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen(io_registers[REG_BG3CNT], BG3X_L, BG3X_H, BG3Y_L, BG3Y_H,
+ io_registers[REG_BG3PA], io_registers[REG_BG3PB], io_registers[REG_BG3PC], io_registers[REG_BG3PD], gfxBG3X, gfxBG3Y,
+ changed, line[3]);
+ }
+
+ uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(int x = 0; x < 240; ++x) {
+ uint32_t color = backdrop;
+ uint8_t top = 0x20;
+
+ uint8_t li2 = (uint8_t)(line[2][x]>>24);
+ uint8_t li3 = (uint8_t)(line[3][x]>>24);
+ uint8_t li4 = (uint8_t)(line[4][x]>>24);
+
+ uint8_t r = (li3 < li2) ? (li3) : (li2);
+
+ if(li4 < r){
+ r = (li4);
+ }
+
+ if(r < (uint8_t)(color >> 24)) {
+ if(r == li2){
+ color = line[2][x];
+ top = 0x04;
+ }else if(r == li3){
+ color = line[3][x];
+ top = 0x08;
+ }else if(r == li4){
+ color = line[4][x];
+ top = 0x10;
+ }
+ }
+
+ if(!(color & 0x00010000)) {
+ switch((BLDMOD >> 6) & 3) {
+ case 0:
+ break;
+ case 1:
+ if(top & BLDMOD)
+ {
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if((top != 0x04) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((top != 0x08) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[3][x];
+ top2 = 0x08;
+ }
+
+ if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[4][x];
+ top2 = 0x10;
+ }
+
+ if(top2 & (BLDMOD>>8) && color < 0x80000000)
+ {
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]);
+ }
+ }
+ break;
+ case 2:
+ if(BLDMOD & top)
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ case 3:
+ if(BLDMOD & top)
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ }
+ } else {
+ // semi-transparent OBJ
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ uint8_t li2 = (uint8_t)(line[2][x]>>24);
+ uint8_t li3 = (uint8_t)(line[3][x]>>24);
+ uint8_t r = (li3 < li2) ? (li3) : (li2);
+
+ if(r < (uint8_t)(back >> 24)) {
+ if(r == li2){
+ back = line[2][x];
+ top2 = 0x04;
+ }else if(r == li3){
+ back = line[3][x];
+ top2 = 0x08;
+ }
+ }
+
+ alpha_blend_brightness_switch();
+ }
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ gfxBG3Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+void mode2RenderLineAll (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 2: Render Line All\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ bool inWindow0 = false;
+ bool inWindow1 = false;
+
+ if(graphics.layerEnable & 0x2000)
+ {
+ uint8_t v0 = io_registers[REG_WIN0V] >> 8;
+ uint8_t v1 = io_registers[REG_WIN0V] & 255;
+ inWindow0 = ((v0 == v1) && (v0 >= 0xe8));
+#ifndef ORIGINAL_BRANCHES
+ uint32_t condition = v1 >= v0;
+ int32_t condition_mask = ((condition) | -(condition)) >> 31;
+ inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask))));
+#else
+ if(v1 >= v0)
+ inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1);
+ else
+ inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1);
+#endif
+ }
+ if(graphics.layerEnable & 0x4000)
+ {
+ uint8_t v0 = io_registers[REG_WIN1V] >> 8;
+ uint8_t v1 = io_registers[REG_WIN1V] & 255;
+ inWindow1 = ((v0 == v1) && (v0 >= 0xe8));
+#ifndef ORIGINAL_BRANCHES
+ uint32_t condition = v1 >= v0;
+ int32_t condition_mask = ((condition) | -(condition)) >> 31;
+ inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask))));
+#else
+ if(v1 >= v0)
+ inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1);
+ else
+ inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1);
+#endif
+ }
+
+ if(graphics.layerEnable & 0x0400) {
+ int changed = gfxBG2Changed;
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H,
+ io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], gfxBG2X, gfxBG2Y,
+ changed, line[2]);
+ }
+
+ if(graphics.layerEnable & 0x0800) {
+ int changed = gfxBG3Changed;
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen(io_registers[REG_BG3CNT], BG3X_L, BG3X_H, BG3Y_L, BG3Y_H,
+ io_registers[REG_BG3PA], io_registers[REG_BG3PB], io_registers[REG_BG3PC], io_registers[REG_BG3PD], gfxBG3X, gfxBG3Y,
+ changed, line[3]);
+ }
+
+ uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000);
+
+ uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF;
+ uint8_t inWin1Mask = io_registers[REG_WININ] >> 8;
+ uint8_t outMask = io_registers[REG_WINOUT] & 0xFF;
+
+ for(int x = 0; x < 240; x++) {
+ uint32_t color = backdrop;
+ uint8_t top = 0x20;
+ uint8_t mask = outMask;
+
+ if(!(line[5][x] & 0x80000000)) {
+ mask = io_registers[REG_WINOUT] >> 8;
+ }
+
+ int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31;
+ int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31;
+ mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask);
+ mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask);
+
+ if((mask & 4) && line[2][x] < color) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((mask & 8) && (uint8_t)(line[3][x]>>24) < (uint8_t)(color >> 24)) {
+ color = line[3][x];
+ top = 0x08;
+ }
+
+ if((mask & 16) && (uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) {
+ color = line[4][x];
+ top = 0x10;
+ }
+
+ if(color & 0x00010000) {
+ // semi-transparent OBJ
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if((mask & 4) && line[2][x] < back) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((mask & 8) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[3][x];
+ top2 = 0x08;
+ }
+
+ alpha_blend_brightness_switch();
+ } else if(mask & 32) {
+ // special FX on the window
+ switch((BLDMOD >> 6) & 3) {
+ case 0:
+ break;
+ case 1:
+ if(top & BLDMOD)
+ {
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if((mask & 4) && (top != 0x04) && line[2][x] < back) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((mask & 8) && (top != 0x08) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[3][x];
+ top2 = 0x08;
+ }
+
+ if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[4][x];
+ top2 = 0x10;
+ }
+
+ if(top2 & (BLDMOD>>8) && color < 0x80000000)
+ {
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]);
+ }
+ }
+ break;
+ case 2:
+ if(BLDMOD & top)
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ case 3:
+ if(BLDMOD & top)
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ }
+ }
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ gfxBG3Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+/*
+Mode 3 is a 15-bit (32768) colour bitmap graphics mode.
+It has a single layer, background layer 2, the same size as the screen.
+It doesn't support paging, scrolling, flipping, rotation or tiles.
+
+These routines only render a single line at a time, because of the way the GBA does events.
+*/
+
+void mode3RenderLine (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 3: Render Line\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ if(graphics.layerEnable & 0x0400) {
+ int changed = gfxBG2Changed;
+
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen16Bit(gfxBG2X, gfxBG2Y, changed);
+ }
+
+ uint32_t background = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(int x = 0; x < 240; ++x) {
+ uint32_t color = background;
+ uint8_t top = 0x20;
+
+ if(line[2][x] < color) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) {
+ color = line[4][x];
+ top = 0x10;
+
+ if(color & 0x00010000) {
+ // semi-transparent OBJ
+ uint32_t back = background;
+ uint8_t top2 = 0x20;
+
+ if(line[2][x] < background) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ alpha_blend_brightness_switch();
+ }
+ }
+
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+void mode3RenderLineNoWindow (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 3: Render Line No Window\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ if(graphics.layerEnable & 0x0400) {
+ int changed = gfxBG2Changed;
+
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen16Bit(gfxBG2X, gfxBG2Y, changed);
+ }
+
+ uint32_t background = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(int x = 0; x < 240; ++x) {
+ uint32_t color = background;
+ uint8_t top = 0x20;
+
+ if(line[2][x] < background) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) {
+ color = line[4][x];
+ top = 0x10;
+ }
+
+ if(!(color & 0x00010000)) {
+ switch((BLDMOD >> 6) & 3) {
+ case 0:
+ break;
+ case 1:
+ if(top & BLDMOD)
+ {
+ uint32_t back = background;
+ uint8_t top2 = 0x20;
+
+ if(top != 0x04 && (line[2][x] < background) ) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if(top != 0x10 && ((uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24))) {
+ back = line[4][x];
+ top2 = 0x10;
+ }
+
+ if(top2 & (BLDMOD>>8) && color < 0x80000000)
+ {
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]);
+ }
+
+ }
+ break;
+ case 2:
+ if(BLDMOD & top)
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ case 3:
+ if(BLDMOD & top)
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ }
+ } else {
+ // semi-transparent OBJ
+ uint32_t back = background;
+ uint8_t top2 = 0x20;
+
+ if(line[2][x] < background) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ alpha_blend_brightness_switch();
+ }
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+void mode3RenderLineAll (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 3: Render Line All\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ bool inWindow0 = false;
+ bool inWindow1 = false;
+
+ if(graphics.layerEnable & 0x2000)
+ {
+ uint8_t v0 = io_registers[REG_WIN0V] >> 8;
+ uint8_t v1 = io_registers[REG_WIN0V] & 255;
+ inWindow0 = ((v0 == v1) && (v0 >= 0xe8));
+#ifndef ORIGINAL_BRANCHES
+ uint32_t condition = v1 >= v0;
+ int32_t condition_mask = ((condition) | -(condition)) >> 31;
+ inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask))));
+#else
+ if(v1 >= v0)
+ inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1);
+ else
+ inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1);
+#endif
+ }
+
+ if(graphics.layerEnable & 0x4000)
+ {
+ uint8_t v0 = io_registers[REG_WIN1V] >> 8;
+ uint8_t v1 = io_registers[REG_WIN1V] & 255;
+ inWindow1 = ((v0 == v1) && (v0 >= 0xe8));
+#ifndef ORIGINAL_BRANCHES
+ uint32_t condition = v1 >= v0;
+ int32_t condition_mask = ((condition) | -(condition)) >> 31;
+ inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask))));
+#else
+ if(v1 >= v0)
+ inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1);
+ else
+ inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1);
+#endif
+ }
+
+ if(graphics.layerEnable & 0x0400) {
+ int changed = gfxBG2Changed;
+
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen16Bit(gfxBG2X, gfxBG2Y, changed);
+ }
+
+ uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF;
+ uint8_t inWin1Mask = io_registers[REG_WININ] >> 8;
+ uint8_t outMask = io_registers[REG_WINOUT] & 0xFF;
+
+ uint32_t background = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(int x = 0; x < 240; ++x) {
+ uint32_t color = background;
+ uint8_t top = 0x20;
+ uint8_t mask = outMask;
+
+ if(!(line[5][x] & 0x80000000)) {
+ mask = io_registers[REG_WINOUT] >> 8;
+ }
+
+ int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31;
+ int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31;
+ mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask);
+ mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask);
+
+ if((mask & 4) && line[2][x] < background) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24))) {
+ color = line[4][x];
+ top = 0x10;
+ }
+
+ if(color & 0x00010000) {
+ // semi-transparent OBJ
+ uint32_t back = background;
+ uint8_t top2 = 0x20;
+
+ if((mask & 4) && line[2][x] < background) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ alpha_blend_brightness_switch();
+ } else if(mask & 32) {
+ switch((BLDMOD >> 6) & 3) {
+ case 0:
+ break;
+ case 1:
+ if(top & BLDMOD)
+ {
+ uint32_t back = background;
+ uint8_t top2 = 0x20;
+
+ if((mask & 4) && (top != 0x04) && line[2][x] < back) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[4][x];
+ top2 = 0x10;
+ }
+
+ if(top2 & (BLDMOD>>8) && color < 0x80000000)
+ {
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]);
+ }
+ }
+ break;
+ case 2:
+ if(BLDMOD & top)
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ case 3:
+ if(BLDMOD & top)
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ }
+ }
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+/*
+Mode 4 is a 256 colour bitmap graphics mode with 2 swappable pages.
+It has a single layer, background layer 2, the same size as the screen.
+It doesn't support scrolling, flipping, rotation or tiles.
+
+These routines only render a single line at a time, because of the way the GBA does events.
+*/
+
+void mode4RenderLine (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 4: Render Line\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ if(graphics.layerEnable & 0x400)
+ {
+ int changed = gfxBG2Changed;
+
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen256(gfxBG2X, gfxBG2Y, changed);
+ }
+
+ uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(int x = 0; x < 240; ++x)
+ {
+ uint32_t color = backdrop;
+ uint8_t top = 0x20;
+
+ if(line[2][x] < backdrop) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) {
+ color = line[4][x];
+ top = 0x10;
+
+ if(color & 0x00010000) {
+ // semi-transparent OBJ
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if(line[2][x] < backdrop) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ alpha_blend_brightness_switch();
+ }
+ }
+
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+void mode4RenderLineNoWindow (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 4: Render Line No Window\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ if(graphics.layerEnable & 0x400)
+ {
+ int changed = gfxBG2Changed;
+
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen256(gfxBG2X, gfxBG2Y, changed);
+ }
+
+ uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(int x = 0; x < 240; ++x)
+ {
+ uint32_t color = backdrop;
+ uint8_t top = 0x20;
+
+ if(line[2][x] < backdrop) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) {
+ color = line[4][x];
+ top = 0x10;
+ }
+
+ if(!(color & 0x00010000)) {
+ switch((BLDMOD >> 6) & 3) {
+ case 0:
+ break;
+ case 1:
+ if(top & BLDMOD)
+ {
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if((top != 0x04) && line[2][x] < backdrop) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[4][x];
+ top2 = 0x10;
+ }
+
+ if(top2 & (BLDMOD>>8) && color < 0x80000000)
+ {
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]);
+ }
+ }
+ break;
+ case 2:
+ if(BLDMOD & top)
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ case 3:
+ if(BLDMOD & top)
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ }
+ } else {
+ // semi-transparent OBJ
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if(line[2][x] < back) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ alpha_blend_brightness_switch();
+ }
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+void mode4RenderLineAll (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 4: Render Line All\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ bool inWindow0 = false;
+ bool inWindow1 = false;
+
+ if(graphics.layerEnable & 0x2000)
+ {
+ uint8_t v0 = io_registers[REG_WIN0V] >> 8;
+ uint8_t v1 = io_registers[REG_WIN0V] & 255;
+ inWindow0 = ((v0 == v1) && (v0 >= 0xe8));
+#ifndef ORIGINAL_BRANCHES
+ uint32_t condition = v1 >= v0;
+ int32_t condition_mask = ((condition) | -(condition)) >> 31;
+ inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask))));
+#else
+ if(v1 >= v0)
+ inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1);
+ else
+ inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1);
+#endif
+ }
+
+ if(graphics.layerEnable & 0x4000)
+ {
+ uint8_t v0 = io_registers[REG_WIN1V] >> 8;
+ uint8_t v1 = io_registers[REG_WIN1V] & 255;
+ inWindow1 = ((v0 == v1) && (v0 >= 0xe8));
+#ifndef ORIGINAL_BRANCHES
+ uint32_t condition = v1 >= v0;
+ int32_t condition_mask = ((condition) | -(condition)) >> 31;
+ inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask))));
+#else
+ if(v1 >= v0)
+ inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1);
+ else
+ inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1);
+#endif
+ }
+
+ if(graphics.layerEnable & 0x400)
+ {
+ int changed = gfxBG2Changed;
+
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen256(gfxBG2X, gfxBG2Y, changed);
+ }
+
+ uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000);
+
+ uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF;
+ uint8_t inWin1Mask = io_registers[REG_WININ] >> 8;
+ uint8_t outMask = io_registers[REG_WINOUT] & 0xFF;
+
+ for(int x = 0; x < 240; ++x) {
+ uint32_t color = backdrop;
+ uint8_t top = 0x20;
+ uint8_t mask = outMask;
+
+ if(!(line[5][x] & 0x80000000))
+ mask = io_registers[REG_WINOUT] >> 8;
+
+ int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31;
+ int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31;
+ mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask);
+ mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask);
+
+ if((mask & 4) && (line[2][x] < backdrop))
+ {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)))
+ {
+ color = line[4][x];
+ top = 0x10;
+ }
+
+ if(color & 0x00010000) {
+ // semi-transparent OBJ
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if((mask & 4) && line[2][x] < back) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ alpha_blend_brightness_switch();
+ } else if(mask & 32) {
+ switch((BLDMOD >> 6) & 3) {
+ case 0:
+ break;
+ case 1:
+ if(top & BLDMOD)
+ {
+ uint32_t back = backdrop;
+ uint8_t top2 = 0x20;
+
+ if((mask & 4) && (top != 0x04) && (line[2][x] < backdrop)) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[4][x];
+ top2 = 0x10;
+ }
+
+ if(top2 & (BLDMOD>>8) && color < 0x80000000)
+ {
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]);
+ }
+ }
+ break;
+ case 2:
+ if(BLDMOD & top)
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ case 3:
+ if(BLDMOD & top)
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ }
+ }
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+/*
+Mode 5 is a low resolution (160x128) 15-bit colour bitmap graphics mode
+with 2 swappable pages!
+It has a single layer, background layer 2, lower resolution than the screen.
+It doesn't support scrolling, flipping, rotation or tiles.
+
+These routines only render a single line at a time, because of the way the GBA does events.
+*/
+
+void mode5RenderLine (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 5: Render Line\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ if(graphics.layerEnable & 0x0400) {
+ int changed = gfxBG2Changed;
+
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen16Bit160(gfxBG2X, gfxBG2Y, changed);
+ }
+
+ uint32_t background;
+ background = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(int x = 0; x < 240; ++x) {
+ uint32_t color = background;
+ uint8_t top = 0x20;
+
+ if(line[2][x] < background) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) {
+ color = line[4][x];
+ top = 0x10;
+
+ if(color & 0x00010000) {
+ // semi-transparent OBJ
+ uint32_t back = background;
+ uint8_t top2 = 0x20;
+
+ if(line[2][x] < back) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ alpha_blend_brightness_switch();
+ }
+ }
+
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+void mode5RenderLineNoWindow (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 5: Render Line No Window\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ if(graphics.layerEnable & 0x0400) {
+ int changed = gfxBG2Changed;
+
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen16Bit160(gfxBG2X, gfxBG2Y, changed);
+ }
+
+ uint32_t background;
+ background = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(int x = 0; x < 240; ++x) {
+ uint32_t color = background;
+ uint8_t top = 0x20;
+
+ if(line[2][x] < background) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) {
+ color = line[4][x];
+ top = 0x10;
+ }
+
+ if(!(color & 0x00010000)) {
+ switch((BLDMOD >> 6) & 3) {
+ case 0:
+ break;
+ case 1:
+ if(top & BLDMOD)
+ {
+ uint32_t back = background;
+ uint8_t top2 = 0x20;
+
+ if((top != 0x04) && line[2][x] < background) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[4][x];
+ top2 = 0x10;
+ }
+
+ if(top2 & (BLDMOD>>8) && color < 0x80000000)
+ {
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]);
+ }
+
+ }
+ break;
+ case 2:
+ if(BLDMOD & top)
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ case 3:
+ if(BLDMOD & top)
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ }
+ } else {
+ // semi-transparent OBJ
+ uint32_t back = background;
+ uint8_t top2 = 0x20;
+
+ if(line[2][x] < back) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ alpha_blend_brightness_switch();
+ }
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+void mode5RenderLineAll (void)
+{
+#ifdef REPORT_VIDEO_MODES
+ fprintf(stderr, "MODE 5: Render Line All\n");
+#endif
+ INIT_COLOR_DEPTH_LINE_MIX();
+
+ uint16_t *palette = (uint16_t *)graphics.paletteRAM;
+
+ if(graphics.layerEnable & 0x0400)
+ {
+ int changed = gfxBG2Changed;
+
+#if 0
+ if(gfxLastVCOUNT > io_registers[REG_VCOUNT])
+ changed = 3;
+#endif
+
+ gfxDrawRotScreen16Bit160(gfxBG2X, gfxBG2Y, changed);
+ }
+
+
+
+ bool inWindow0 = false;
+ bool inWindow1 = false;
+
+ if(graphics.layerEnable & 0x2000)
+ {
+ uint8_t v0 = io_registers[REG_WIN0V] >> 8;
+ uint8_t v1 = io_registers[REG_WIN0V] & 255;
+ inWindow0 = ((v0 == v1) && (v0 >= 0xe8));
+#ifndef ORIGINAL_BRANCHES
+ uint32_t condition = v1 >= v0;
+ int32_t condition_mask = ((condition) | -(condition)) >> 31;
+ inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask))));
+#else
+ if(v1 >= v0)
+ inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1);
+ else
+ inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1);
+#endif
+ }
+
+ if(graphics.layerEnable & 0x4000)
+ {
+ uint8_t v0 = io_registers[REG_WIN1V] >> 8;
+ uint8_t v1 = io_registers[REG_WIN1V] & 255;
+ inWindow1 = ((v0 == v1) && (v0 >= 0xe8));
+#ifndef ORIGINAL_BRANCHES
+ uint32_t condition = v1 >= v0;
+ int32_t condition_mask = ((condition) | -(condition)) >> 31;
+ inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask))));
+#else
+ if(v1 >= v0)
+ inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1);
+ else
+ inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1);
+#endif
+ }
+
+ uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF;
+ uint8_t inWin1Mask = io_registers[REG_WININ] >> 8;
+ uint8_t outMask = io_registers[REG_WINOUT] & 0xFF;
+
+ uint32_t background;
+ background = (READ16LE(&palette[0]) | 0x30000000);
+
+ for(int x = 0; x < 240; ++x) {
+ uint32_t color = background;
+ uint8_t top = 0x20;
+ uint8_t mask = outMask;
+
+ if(!(line[5][x] & 0x80000000)) {
+ mask = io_registers[REG_WINOUT] >> 8;
+ }
+
+ int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31;
+ int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31;
+ mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask);
+ mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask);
+
+ if((mask & 4) && (line[2][x] < background)) {
+ color = line[2][x];
+ top = 0x04;
+ }
+
+ if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24))) {
+ color = line[4][x];
+ top = 0x10;
+ }
+
+ if(color & 0x00010000) {
+ // semi-transparent OBJ
+ uint32_t back = background;
+ uint8_t top2 = 0x20;
+
+ if((mask & 4) && line[2][x] < back) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ alpha_blend_brightness_switch();
+ } else if(mask & 32) {
+ switch((BLDMOD >> 6) & 3) {
+ case 0:
+ break;
+ case 1:
+ if(top & BLDMOD)
+ {
+ uint32_t back = background;
+ uint8_t top2 = 0x20;
+
+ if((mask & 4) && (top != 0x04) && (line[2][x] < background)) {
+ back = line[2][x];
+ top2 = 0x04;
+ }
+
+ if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) {
+ back = line[4][x];
+ top2 = 0x10;
+ }
+
+ if(top2 & (BLDMOD>>8) && color < 0x80000000)
+ {
+ GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]);
+ }
+ }
+ break;
+ case 2:
+ if(BLDMOD & top)
+ color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ case 3:
+ if(BLDMOD & top)
+ color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]);
+ break;
+ }
+ }
+
+ lineMix[x] = CONVERT_COLOR(color);
+ }
+ gfxBG2Changed = 0;
+ //gfxLastVCOUNT = io_registers[REG_VCOUNT];
+}
+
+void (Gigazoid::*renderLine)(void);
+bool render_line_all_enabled;
+
+#define CPUUpdateRender() \
+ render_line_all_enabled = false; \
+ switch(io_registers[REG_DISPCNT] & 7) { \
+ case 0: \
+ if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \
+ renderLine = &Gigazoid::mode0RenderLine; \
+ else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \
+ renderLine = &Gigazoid::mode0RenderLineNoWindow; \
+ else { \
+ renderLine = &Gigazoid::mode0RenderLineAll; \
+ render_line_all_enabled = true; \
+ } \
+ break; \
+ case 1: \
+ if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \
+ renderLine = &Gigazoid::mode1RenderLine; \
+ else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \
+ renderLine = &Gigazoid::mode1RenderLineNoWindow; \
+ else { \
+ renderLine = &Gigazoid::mode1RenderLineAll; \
+ render_line_all_enabled = true; \
+ } \
+ break; \
+ case 2: \
+ if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \
+ renderLine = &Gigazoid::mode2RenderLine; \
+ else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \
+ renderLine = &Gigazoid::mode2RenderLineNoWindow; \
+ else { \
+ renderLine = &Gigazoid::mode2RenderLineAll; \
+ render_line_all_enabled = true; \
+ } \
+ break; \
+ case 3: \
+ if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \
+ renderLine = &Gigazoid::mode3RenderLine; \
+ else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \
+ renderLine = &Gigazoid::mode3RenderLineNoWindow; \
+ else { \
+ renderLine = &Gigazoid::mode3RenderLineAll; \
+ render_line_all_enabled = true; \
+ } \
+ break; \
+ case 4: \
+ if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \
+ renderLine = &Gigazoid::mode4RenderLine; \
+ else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \
+ renderLine = &Gigazoid::mode4RenderLineNoWindow; \
+ else { \
+ renderLine = &Gigazoid::mode4RenderLineAll; \
+ render_line_all_enabled = true; \
+ } \
+ break; \
+ case 5: \
+ if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \
+ renderLine = &Gigazoid::mode5RenderLine; \
+ else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \
+ renderLine = &Gigazoid::mode5RenderLineNoWindow; \
+ else { \
+ renderLine = &Gigazoid::mode5RenderLineAll; \
+ render_line_all_enabled = true; \
+ } \
+ }
+
+#define CPUSwap(a, b) \
+a ^= b; \
+b ^= a; \
+a ^= b;
+
+void CPUSwitchMode(int mode, bool saveState, bool breakLoop)
+{
+ CPU_UPDATE_CPSR();
+
+ switch(armMode) {
+ case 0x10:
+ case 0x1F:
+ bus.reg[R13_USR].I = bus.reg[13].I;
+ bus.reg[R14_USR].I = bus.reg[14].I;
+ bus.reg[17].I = bus.reg[16].I;
+ break;
+ case 0x11:
+ CPUSwap(bus.reg[R8_FIQ].I, bus.reg[8].I);
+ CPUSwap(bus.reg[R9_FIQ].I, bus.reg[9].I);
+ CPUSwap(bus.reg[R10_FIQ].I, bus.reg[10].I);
+ CPUSwap(bus.reg[R11_FIQ].I, bus.reg[11].I);
+ CPUSwap(bus.reg[R12_FIQ].I, bus.reg[12].I);
+ bus.reg[R13_FIQ].I = bus.reg[13].I;
+ bus.reg[R14_FIQ].I = bus.reg[14].I;
+ bus.reg[SPSR_FIQ].I = bus.reg[17].I;
+ break;
+ case 0x12:
+ bus.reg[R13_IRQ].I = bus.reg[13].I;
+ bus.reg[R14_IRQ].I = bus.reg[14].I;
+ bus.reg[SPSR_IRQ].I = bus.reg[17].I;
+ break;
+ case 0x13:
+ bus.reg[R13_SVC].I = bus.reg[13].I;
+ bus.reg[R14_SVC].I = bus.reg[14].I;
+ bus.reg[SPSR_SVC].I = bus.reg[17].I;
+ break;
+ case 0x17:
+ bus.reg[R13_ABT].I = bus.reg[13].I;
+ bus.reg[R14_ABT].I = bus.reg[14].I;
+ bus.reg[SPSR_ABT].I = bus.reg[17].I;
+ break;
+ case 0x1b:
+ bus.reg[R13_UND].I = bus.reg[13].I;
+ bus.reg[R14_UND].I = bus.reg[14].I;
+ bus.reg[SPSR_UND].I = bus.reg[17].I;
+ break;
+ }
+
+ uint32_t CPSR = bus.reg[16].I;
+ uint32_t SPSR = bus.reg[17].I;
+
+ switch(mode) {
+ case 0x10:
+ case 0x1F:
+ bus.reg[13].I = bus.reg[R13_USR].I;
+ bus.reg[14].I = bus.reg[R14_USR].I;
+ bus.reg[16].I = SPSR;
+ break;
+ case 0x11:
+ CPUSwap(bus.reg[8].I, bus.reg[R8_FIQ].I);
+ CPUSwap(bus.reg[9].I, bus.reg[R9_FIQ].I);
+ CPUSwap(bus.reg[10].I, bus.reg[R10_FIQ].I);
+ CPUSwap(bus.reg[11].I, bus.reg[R11_FIQ].I);
+ CPUSwap(bus.reg[12].I, bus.reg[R12_FIQ].I);
+ bus.reg[13].I = bus.reg[R13_FIQ].I;
+ bus.reg[14].I = bus.reg[R14_FIQ].I;
+ if(saveState)
+ bus.reg[17].I = CPSR; else
+ bus.reg[17].I = bus.reg[SPSR_FIQ].I;
+ break;
+ case 0x12:
+ bus.reg[13].I = bus.reg[R13_IRQ].I;
+ bus.reg[14].I = bus.reg[R14_IRQ].I;
+ bus.reg[16].I = SPSR;
+ if(saveState)
+ bus.reg[17].I = CPSR;
+ else
+ bus.reg[17].I = bus.reg[SPSR_IRQ].I;
+ break;
+ case 0x13:
+ bus.reg[13].I = bus.reg[R13_SVC].I;
+ bus.reg[14].I = bus.reg[R14_SVC].I;
+ bus.reg[16].I = SPSR;
+ if(saveState)
+ bus.reg[17].I = CPSR;
+ else
+ bus.reg[17].I = bus.reg[SPSR_SVC].I;
+ break;
+ case 0x17:
+ bus.reg[13].I = bus.reg[R13_ABT].I;
+ bus.reg[14].I = bus.reg[R14_ABT].I;
+ bus.reg[16].I = SPSR;
+ if(saveState)
+ bus.reg[17].I = CPSR;
+ else
+ bus.reg[17].I = bus.reg[SPSR_ABT].I;
+ break;
+ case 0x1b:
+ bus.reg[13].I = bus.reg[R13_UND].I;
+ bus.reg[14].I = bus.reg[R14_UND].I;
+ bus.reg[16].I = SPSR;
+ if(saveState)
+ bus.reg[17].I = CPSR;
+ else
+ bus.reg[17].I = bus.reg[SPSR_UND].I;
+ break;
+ default:
+ break;
+ }
+ armMode = mode;
+ CPUUpdateFlags(breakLoop);
+ CPU_UPDATE_CPSR();
+}
+
+
+
+void doDMA(uint32_t &s, uint32_t &d, uint32_t si, uint32_t di, uint32_t c, int transfer32)
+{
+ int sm = s >> 24;
+ int dm = d >> 24;
+ int sw = 0;
+ int dw = 0;
+ int sc = c;
+
+ cpuDmaCount = c;
+ // This is done to get the correct waitstates.
+ int32_t sm_gt_15_mask = ((sm>15) | -(sm>15)) >> 31;
+ int32_t dm_gt_15_mask = ((dm>15) | -(dm>15)) >> 31;
+ sm = ((((15) & sm_gt_15_mask) | ((((sm) & ~(sm_gt_15_mask))))));
+ dm = ((((15) & dm_gt_15_mask) | ((((dm) & ~(dm_gt_15_mask))))));
+
+ //if ((sm>=0x05) && (sm<=0x07) || (dm>=0x05) && (dm <=0x07))
+ // blank = (((io_registers[REG_DISPSTAT] | ((io_registers[REG_DISPSTAT] >> 1)&1))==1) ? true : false);
+
+ if(transfer32)
+ {
+ s &= 0xFFFFFFFC;
+ if(s < 0x02000000 && (bus.reg[15].I >> 24))
+ {
+ do
+ {
+ CPUWriteMemory(d, 0);
+ d += di;
+ c--;
+ }while(c != 0);
+ }
+ else
+ {
+ do {
+ CPUWriteMemory(d, CPUReadMemory(s));
+ d += di;
+ s += si;
+ c--;
+ }while(c != 0);
+ }
+ }
+ else
+ {
+ s &= 0xFFFFFFFE;
+ si = (int)si >> 1;
+ di = (int)di >> 1;
+ if(s < 0x02000000 && (bus.reg[15].I >> 24))
+ {
+ do {
+ CPUWriteHalfWord(d, 0);
+ d += di;
+ c--;
+ }while(c != 0);
+ }
+ else
+ {
+ do{
+ CPUWriteHalfWord(d, CPUReadHalfWord(s));
+ d += di;
+ s += si;
+ c--;
+ }while(c != 0);
+ }
+ }
+
+ cpuDmaCount = 0;
+
+ if(transfer32)
+ {
+ sw = 1+memoryWaitSeq32[sm & 15];
+ dw = 1+memoryWaitSeq32[dm & 15];
+ cpuDmaTicksToUpdate += (sw+dw)*(sc-1) + 6 + memoryWait32[sm & 15] + memoryWaitSeq32[dm & 15];
+ }
+ else
+ {
+ sw = 1+memoryWaitSeq[sm & 15];
+ dw = 1+memoryWaitSeq[dm & 15];
+ cpuDmaTicksToUpdate += (sw+dw)*(sc-1) + 6 + memoryWait[sm & 15] + memoryWaitSeq[dm & 15];
+ }
+}
+
+
+void CPUCheckDMA(int reason, int dmamask)
+{
+ uint32_t arrayval[] = {4, (uint32_t)-4, 0, 4};
+ // DMA 0
+ if((DM0CNT_H & 0x8000) && (dmamask & 1))
+ {
+ if(((DM0CNT_H >> 12) & 3) == reason)
+ {
+ uint32_t sourceIncrement, destIncrement;
+ uint32_t condition1 = ((DM0CNT_H >> 7) & 3);
+ uint32_t condition2 = ((DM0CNT_H >> 5) & 3);
+ sourceIncrement = arrayval[condition1];
+ destIncrement = arrayval[condition2];
+ doDMA(dma0Source, dma0Dest, sourceIncrement, destIncrement,
+ DM0CNT_L ? DM0CNT_L : 0x4000,
+ DM0CNT_H & 0x0400);
+
+ if(DM0CNT_H & 0x4000)
+ {
+ io_registers[REG_IF] |= 0x0100;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ cpuNextEvent = cpuTotalTicks;
+ }
+
+ if(((DM0CNT_H >> 5) & 3) == 3) {
+ dma0Dest = DM0DAD_L | (DM0DAD_H << 16);
+ }
+
+ if(!(DM0CNT_H & 0x0200) || (reason == 0)) {
+ DM0CNT_H &= 0x7FFF;
+ UPDATE_REG(0xBA, DM0CNT_H);
+ }
+ }
+ }
+
+ // DMA 1
+ if((DM1CNT_H & 0x8000) && (dmamask & 2)) {
+ if(((DM1CNT_H >> 12) & 3) == reason) {
+ uint32_t sourceIncrement, destIncrement;
+ uint32_t condition1 = ((DM1CNT_H >> 7) & 3);
+ uint32_t condition2 = ((DM1CNT_H >> 5) & 3);
+ sourceIncrement = arrayval[condition1];
+ destIncrement = arrayval[condition2];
+ uint32_t di_value, c_value, transfer_value;
+ if(reason == 3)
+ {
+ di_value = 0;
+ c_value = 4;
+ transfer_value = 0x0400;
+ }
+ else
+ {
+ di_value = destIncrement;
+ c_value = DM1CNT_L ? DM1CNT_L : 0x4000;
+ transfer_value = DM1CNT_H & 0x0400;
+ }
+ doDMA(dma1Source, dma1Dest, sourceIncrement, di_value, c_value, transfer_value);
+
+ if(DM1CNT_H & 0x4000) {
+ io_registers[REG_IF] |= 0x0200;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ cpuNextEvent = cpuTotalTicks;
+ }
+
+ if(((DM1CNT_H >> 5) & 3) == 3) {
+ dma1Dest = DM1DAD_L | (DM1DAD_H << 16);
+ }
+
+ if(!(DM1CNT_H & 0x0200) || (reason == 0)) {
+ DM1CNT_H &= 0x7FFF;
+ UPDATE_REG(0xC6, DM1CNT_H);
+ }
+ }
+ }
+
+ // DMA 2
+ if((DM2CNT_H & 0x8000) && (dmamask & 4)) {
+ if(((DM2CNT_H >> 12) & 3) == reason) {
+ uint32_t sourceIncrement, destIncrement;
+ uint32_t condition1 = ((DM2CNT_H >> 7) & 3);
+ uint32_t condition2 = ((DM2CNT_H >> 5) & 3);
+ sourceIncrement = arrayval[condition1];
+ destIncrement = arrayval[condition2];
+ uint32_t di_value, c_value, transfer_value;
+ if(reason == 3)
+ {
+ di_value = 0;
+ c_value = 4;
+ transfer_value = 0x0400;
+ }
+ else
+ {
+ di_value = destIncrement;
+ c_value = DM2CNT_L ? DM2CNT_L : 0x4000;
+ transfer_value = DM2CNT_H & 0x0400;
+ }
+ doDMA(dma2Source, dma2Dest, sourceIncrement, di_value, c_value, transfer_value);
+
+ if(DM2CNT_H & 0x4000) {
+ io_registers[REG_IF] |= 0x0400;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ cpuNextEvent = cpuTotalTicks;
+ }
+
+ if(((DM2CNT_H >> 5) & 3) == 3) {
+ dma2Dest = DM2DAD_L | (DM2DAD_H << 16);
+ }
+
+ if(!(DM2CNT_H & 0x0200) || (reason == 0)) {
+ DM2CNT_H &= 0x7FFF;
+ UPDATE_REG(0xD2, DM2CNT_H);
+ }
+ }
+ }
+
+ // DMA 3
+ if((DM3CNT_H & 0x8000) && (dmamask & 8))
+ {
+ if(((DM3CNT_H >> 12) & 3) == reason)
+ {
+ uint32_t sourceIncrement, destIncrement;
+ uint32_t condition1 = ((DM3CNT_H >> 7) & 3);
+ uint32_t condition2 = ((DM3CNT_H >> 5) & 3);
+ sourceIncrement = arrayval[condition1];
+ destIncrement = arrayval[condition2];
+ doDMA(dma3Source, dma3Dest, sourceIncrement, destIncrement,
+ DM3CNT_L ? DM3CNT_L : 0x10000,
+ DM3CNT_H & 0x0400);
+ if(DM3CNT_H & 0x4000) {
+ io_registers[REG_IF] |= 0x0800;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ cpuNextEvent = cpuTotalTicks;
+ }
+
+ if(((DM3CNT_H >> 5) & 3) == 3) {
+ dma3Dest = DM3DAD_L | (DM3DAD_H << 16);
+ }
+
+ if(!(DM3CNT_H & 0x0200) || (reason == 0)) {
+ DM3CNT_H &= 0x7FFF;
+ UPDATE_REG(0xDE, DM3CNT_H);
+ }
+ }
+ }
+}
+
+uint16_t *address_lut[0x300];
+
+void CPUUpdateRegister(uint32_t address, uint16_t value)
+{
+ switch(address)
+ {
+ case 0x00:
+ {
+ if((value & 7) > 5) // display modes above 0-5 are prohibited
+ io_registers[REG_DISPCNT] = (value & 7);
+
+ bool change = (0 != ((io_registers[REG_DISPCNT] ^ value) & 0x80));
+ bool changeBG = (0 != ((io_registers[REG_DISPCNT] ^ value) & 0x0F00));
+ uint16_t changeBGon = ((~io_registers[REG_DISPCNT]) & value) & 0x0F00; // these layers are being activated
+
+ io_registers[REG_DISPCNT] = (value & 0xFFF7); // bit 3 can only be accessed by the BIOS to enable GBC mode
+ UPDATE_REG(0x00, io_registers[REG_DISPCNT]);
+
+ graphics.layerEnable = value;
+
+ if(changeBGon)
+ {
+ graphics.layerEnableDelay = 4;
+ graphics.layerEnable &= ~changeBGon;
+ }
+
+ windowOn = (graphics.layerEnable & 0x6000) ? true : false;
+ if(change && !((value & 0x80)))
+ {
+ if(!(io_registers[REG_DISPSTAT] & 1))
+ {
+ graphics.lcdTicks = 1008;
+ io_registers[REG_DISPSTAT] &= 0xFFFC;
+ UPDATE_REG(0x04, io_registers[REG_DISPSTAT]);
+ CPUCompareVCOUNT();
+ }
+ }
+ CPUUpdateRender();
+ // we only care about changes in BG0-BG3
+ if(changeBG)
+ {
+ if(!(graphics.layerEnable & 0x0100))
+ memset(line[0], -1, 240 * sizeof(u32));
+ if(!(graphics.layerEnable & 0x0200))
+ memset(line[1], -1, 240 * sizeof(u32));
+ if(!(graphics.layerEnable & 0x0400))
+ memset(line[2], -1, 240 * sizeof(u32));
+ if(!(graphics.layerEnable & 0x0800))
+ memset(line[3], -1, 240 * sizeof(u32));
+ }
+ break;
+ }
+ case 0x04:
+ io_registers[REG_DISPSTAT] = (value & 0xFF38) | (io_registers[REG_DISPSTAT] & 7);
+ UPDATE_REG(0x04, io_registers[REG_DISPSTAT]);
+ break;
+ case 0x06:
+ // not writable
+ break;
+ case 0x08: /* BG0CNT */
+ case 0x0A: /* BG1CNT */
+ *address_lut[address] = (value & 0xDFCF);
+ UPDATE_REG(address, *address_lut[address]);
+ break;
+ case 0x0C: /* BG2CNT */
+ case 0x0E: /* BG3CNT */
+ *address_lut[address] = (value & 0xFFCF);
+ UPDATE_REG(address, *address_lut[address]);
+ break;
+ case 0x10: /* BG0HOFS */
+ case 0x12: /* BG0VOFS */
+ case 0x14: /* BG1HOFS */
+ case 0x16: /* BG1VOFS */
+ case 0x18: /* BG2HOFS */
+ case 0x1A: /* BG2VOFS */
+ case 0x1C: /* BG3HOFS */
+ case 0x1E: /* BG3VOFS */
+ *address_lut[address] = value & 511;
+ UPDATE_REG(address, *address_lut[address]);
+ break;
+ case 0x20: /* BG2PA */
+ case 0x22: /* BG2PB */
+ case 0x24: /* BG2PC */
+ case 0x26: /* BG2PD */
+ *address_lut[address] = value;
+ UPDATE_REG(address, *address_lut[address]);
+ break;
+ case 0x28:
+ BG2X_L = value;
+ UPDATE_REG(0x28, BG2X_L);
+ gfxBG2Changed |= 1;
+ break;
+ case 0x2A:
+ BG2X_H = (value & 0xFFF);
+ UPDATE_REG(0x2A, BG2X_H);
+ gfxBG2Changed |= 1;
+ break;
+ case 0x2C:
+ BG2Y_L = value;
+ UPDATE_REG(0x2C, BG2Y_L);
+ gfxBG2Changed |= 2;
+ break;
+ case 0x2E:
+ BG2Y_H = value & 0xFFF;
+ UPDATE_REG(0x2E, BG2Y_H);
+ gfxBG2Changed |= 2;
+ break;
+ case 0x30: /* BG3PA */
+ case 0x32: /* BG3PB */
+ case 0x34: /* BG3PC */
+ case 0x36: /* BG3PD */
+ *address_lut[address] = value;
+ UPDATE_REG(address, *address_lut[address]);
+ break;
+ case 0x38:
+ BG3X_L = value;
+ UPDATE_REG(0x38, BG3X_L);
+ gfxBG3Changed |= 1;
+ break;
+ case 0x3A:
+ BG3X_H = value & 0xFFF;
+ UPDATE_REG(0x3A, BG3X_H);
+ gfxBG3Changed |= 1;
+ break;
+ case 0x3C:
+ BG3Y_L = value;
+ UPDATE_REG(0x3C, BG3Y_L);
+ gfxBG3Changed |= 2;
+ break;
+ case 0x3E:
+ BG3Y_H = value & 0xFFF;
+ UPDATE_REG(0x3E, BG3Y_H);
+ gfxBG3Changed |= 2;
+ break;
+ case 0x40:
+ io_registers[REG_WIN0H] = value;
+ UPDATE_REG(0x40, io_registers[REG_WIN0H]);
+ CPUUpdateWindow0();
+ break;
+ case 0x42:
+ io_registers[REG_WIN1H] = value;
+ UPDATE_REG(0x42, io_registers[REG_WIN1H]);
+ CPUUpdateWindow1();
+ break;
+ case 0x44:
+ case 0x46:
+ *address_lut[address] = value;
+ UPDATE_REG(address, *address_lut[address]);
+ break;
+ case 0x48: /* WININ */
+ case 0x4A: /* WINOUT */
+ *address_lut[address] = value & 0x3F3F;
+ UPDATE_REG(address, *address_lut[address]);
+ break;
+ case 0x4C:
+ MOSAIC = value;
+ UPDATE_REG(0x4C, MOSAIC);
+ break;
+ case 0x50:
+ BLDMOD = value & 0x3FFF;
+ UPDATE_REG(0x50, BLDMOD);
+ fxOn = ((BLDMOD>>6)&3) != 0;
+ CPUUpdateRender();
+ break;
+ case 0x52:
+ COLEV = value & 0x1F1F;
+ UPDATE_REG(0x52, COLEV);
+ break;
+ case 0x54:
+ COLY = value & 0x1F;
+ UPDATE_REG(0x54, COLY);
+ break;
+ case 0x60:
+ case 0x62:
+ case 0x64:
+ case 0x68:
+ case 0x6c:
+ case 0x70:
+ case 0x72:
+ case 0x74:
+ case 0x78:
+ case 0x7c:
+ case 0x80:
+ case 0x84:
+ {
+ int gb_addr[2] = {address & 0xFF, (address & 0xFF) + 1};
+ uint32_t address_array[2] = {address & 0xFF, (address&0xFF)+1};
+ uint8_t data_array[2] = {(uint8_t)(value & 0xFF), (uint8_t)(value>>8)};
+ gb_addr[0] = table[gb_addr[0] - 0x60];
+ gb_addr[1] = table[gb_addr[1] - 0x60];
+ soundEvent_u8_parallel(gb_addr, address_array, data_array);
+ break;
+ }
+ case 0x82:
+ case 0x88:
+ case 0xa0:
+ case 0xa2:
+ case 0xa4:
+ case 0xa6:
+ case 0x90:
+ case 0x92:
+ case 0x94:
+ case 0x96:
+ case 0x98:
+ case 0x9a:
+ case 0x9c:
+ case 0x9e:
+ soundEvent_u16(address&0xFF, value);
+ break;
+ case 0xB0:
+ DM0SAD_L = value;
+ UPDATE_REG(0xB0, DM0SAD_L);
+ break;
+ case 0xB2:
+ DM0SAD_H = value & 0x07FF;
+ UPDATE_REG(0xB2, DM0SAD_H);
+ break;
+ case 0xB4:
+ DM0DAD_L = value;
+ UPDATE_REG(0xB4, DM0DAD_L);
+ break;
+ case 0xB6:
+ DM0DAD_H = value & 0x07FF;
+ UPDATE_REG(0xB6, DM0DAD_H);
+ break;
+ case 0xB8:
+ DM0CNT_L = value & 0x3FFF;
+ UPDATE_REG(0xB8, 0);
+ break;
+ case 0xBA:
+ {
+ bool start = ((DM0CNT_H ^ value) & 0x8000) ? true : false;
+ value &= 0xF7E0;
+
+ DM0CNT_H = value;
+ UPDATE_REG(0xBA, DM0CNT_H);
+
+ if(start && (value & 0x8000))
+ {
+ dma0Source = DM0SAD_L | (DM0SAD_H << 16);
+ dma0Dest = DM0DAD_L | (DM0DAD_H << 16);
+ CPUCheckDMA(0, 1);
+ }
+ }
+ break;
+ case 0xBC:
+ DM1SAD_L = value;
+ UPDATE_REG(0xBC, DM1SAD_L);
+ break;
+ case 0xBE:
+ DM1SAD_H = value & 0x0FFF;
+ UPDATE_REG(0xBE, DM1SAD_H);
+ break;
+ case 0xC0:
+ DM1DAD_L = value;
+ UPDATE_REG(0xC0, DM1DAD_L);
+ break;
+ case 0xC2:
+ DM1DAD_H = value & 0x07FF;
+ UPDATE_REG(0xC2, DM1DAD_H);
+ break;
+ case 0xC4:
+ DM1CNT_L = value & 0x3FFF;
+ UPDATE_REG(0xC4, 0);
+ break;
+ case 0xC6:
+ {
+ bool start = ((DM1CNT_H ^ value) & 0x8000) ? true : false;
+ value &= 0xF7E0;
+
+ DM1CNT_H = value;
+ UPDATE_REG(0xC6, DM1CNT_H);
+
+ if(start && (value & 0x8000))
+ {
+ dma1Source = DM1SAD_L | (DM1SAD_H << 16);
+ dma1Dest = DM1DAD_L | (DM1DAD_H << 16);
+ CPUCheckDMA(0, 2);
+ }
+ }
+ break;
+ case 0xC8:
+ DM2SAD_L = value;
+ UPDATE_REG(0xC8, DM2SAD_L);
+ break;
+ case 0xCA:
+ DM2SAD_H = value & 0x0FFF;
+ UPDATE_REG(0xCA, DM2SAD_H);
+ break;
+ case 0xCC:
+ DM2DAD_L = value;
+ UPDATE_REG(0xCC, DM2DAD_L);
+ break;
+ case 0xCE:
+ DM2DAD_H = value & 0x07FF;
+ UPDATE_REG(0xCE, DM2DAD_H);
+ break;
+ case 0xD0:
+ DM2CNT_L = value & 0x3FFF;
+ UPDATE_REG(0xD0, 0);
+ break;
+ case 0xD2:
+ {
+ bool start = ((DM2CNT_H ^ value) & 0x8000) ? true : false;
+
+ value &= 0xF7E0;
+
+ DM2CNT_H = value;
+ UPDATE_REG(0xD2, DM2CNT_H);
+
+ if(start && (value & 0x8000)) {
+ dma2Source = DM2SAD_L | (DM2SAD_H << 16);
+ dma2Dest = DM2DAD_L | (DM2DAD_H << 16);
+
+ CPUCheckDMA(0, 4);
+ }
+ }
+ break;
+ case 0xD4:
+ DM3SAD_L = value;
+ UPDATE_REG(0xD4, DM3SAD_L);
+ break;
+ case 0xD6:
+ DM3SAD_H = value & 0x0FFF;
+ UPDATE_REG(0xD6, DM3SAD_H);
+ break;
+ case 0xD8:
+ DM3DAD_L = value;
+ UPDATE_REG(0xD8, DM3DAD_L);
+ break;
+ case 0xDA:
+ DM3DAD_H = value & 0x0FFF;
+ UPDATE_REG(0xDA, DM3DAD_H);
+ break;
+ case 0xDC:
+ DM3CNT_L = value;
+ UPDATE_REG(0xDC, 0);
+ break;
+ case 0xDE:
+ {
+ bool start = ((DM3CNT_H ^ value) & 0x8000) ? true : false;
+
+ value &= 0xFFE0;
+
+ DM3CNT_H = value;
+ UPDATE_REG(0xDE, DM3CNT_H);
+
+ if(start && (value & 0x8000)) {
+ dma3Source = DM3SAD_L | (DM3SAD_H << 16);
+ dma3Dest = DM3DAD_L | (DM3DAD_H << 16);
+ CPUCheckDMA(0,8);
+ }
+ }
+ break;
+ case 0x100:
+ timer0Reload = value;
+ break;
+ case 0x102:
+ timer0Value = value;
+ timerOnOffDelay|=1;
+ cpuNextEvent = cpuTotalTicks;
+ break;
+ case 0x104:
+ timer1Reload = value;
+ break;
+ case 0x106:
+ timer1Value = value;
+ timerOnOffDelay|=2;
+ cpuNextEvent = cpuTotalTicks;
+ break;
+ case 0x108:
+ timer2Reload = value;
+ break;
+ case 0x10A:
+ timer2Value = value;
+ timerOnOffDelay|=4;
+ cpuNextEvent = cpuTotalTicks;
+ break;
+ case 0x10C:
+ timer3Reload = value;
+ break;
+ case 0x10E:
+ timer3Value = value;
+ timerOnOffDelay|=8;
+ cpuNextEvent = cpuTotalTicks;
+ break;
+ case 0x130:
+ io_registers[REG_P1] |= (value & 0x3FF);
+ UPDATE_REG(0x130, io_registers[REG_P1]);
+ break;
+ case 0x132:
+ UPDATE_REG(0x132, value & 0xC3FF);
+ break;
+
+
+ case 0x200:
+ io_registers[REG_IE] = value & 0x3FFF;
+ UPDATE_REG(0x200, io_registers[REG_IE]);
+ if ((io_registers[REG_IME] & 1) && (io_registers[REG_IF] & io_registers[REG_IE]) && armIrqEnable)
+ cpuNextEvent = cpuTotalTicks;
+ break;
+ case 0x202:
+ io_registers[REG_IF] ^= (value & io_registers[REG_IF]);
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ break;
+ case 0x204:
+ {
+ memoryWait[0x0e] = memoryWaitSeq[0x0e] = gamepakRamWaitState[value & 3];
+
+ memoryWait[0x08] = memoryWait[0x09] = 3;
+ memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = 1;
+
+ memoryWait[0x0a] = memoryWait[0x0b] = 3;
+ memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = 1;
+
+ memoryWait[0x0c] = memoryWait[0x0d] = 3;
+ memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = 1;
+
+ memoryWait32[8] = memoryWait[8] + memoryWaitSeq[8] + 1;
+ memoryWaitSeq32[8] = memoryWaitSeq[8]*2 + 1;
+
+ memoryWait32[9] = memoryWait[9] + memoryWaitSeq[9] + 1;
+ memoryWaitSeq32[9] = memoryWaitSeq[9]*2 + 1;
+
+ memoryWait32[10] = memoryWait[10] + memoryWaitSeq[10] + 1;
+ memoryWaitSeq32[10] = memoryWaitSeq[10]*2 + 1;
+
+ memoryWait32[11] = memoryWait[11] + memoryWaitSeq[11] + 1;
+ memoryWaitSeq32[11] = memoryWaitSeq[11]*2 + 1;
+
+ memoryWait32[12] = memoryWait[12] + memoryWaitSeq[12] + 1;
+ memoryWaitSeq32[12] = memoryWaitSeq[12]*2 + 1;
+
+ memoryWait32[13] = memoryWait[13] + memoryWaitSeq[13] + 1;
+ memoryWaitSeq32[13] = memoryWaitSeq[13]*2 + 1;
+
+ memoryWait32[14] = memoryWait[14] + memoryWaitSeq[14] + 1;
+ memoryWaitSeq32[14] = memoryWaitSeq[14]*2 + 1;
+
+ if((value & 0x4000) == 0x4000)
+ bus.busPrefetchEnable = true;
+ else
+ bus.busPrefetchEnable = false;
+
+ bus.busPrefetch = false;
+ bus.busPrefetchCount = 0;
+
+ UPDATE_REG(0x204, value & 0x7FFF);
+
+ }
+ break;
+ case 0x208:
+ io_registers[REG_IME] = value & 1;
+ UPDATE_REG(0x208, io_registers[REG_IME]);
+ if ((io_registers[REG_IME] & 1) && (io_registers[REG_IF] & io_registers[REG_IE]) && armIrqEnable)
+ cpuNextEvent = cpuTotalTicks;
+ break;
+ case 0x300:
+ if(value != 0)
+ value &= 0xFFFE;
+ UPDATE_REG(0x300, value);
+ break;
+ default:
+ UPDATE_REG(address&0x3FE, value);
+ break;
+ }
+}
+
+
+void CPUInit(const u8 *biosfile, const u32 biosfilelen)
+{
+#ifndef LSB_FIRST
+ if(!cpuBiosSwapped) {
+ for(unsigned int i = 0; i < sizeof(myROM)/4; i++) {
+ WRITE32LE(&myROM[i], myROM[i]);
+ }
+ cpuBiosSwapped = true;
+ }
+#endif
+ gbaSaveType = 0;
+ eepromInUse = 0;
+ saveType = 0;
+
+ memcpy(bios, biosfile, 16384);
+
+ int i = 0;
+
+ biosProtected[0] = 0x00;
+ biosProtected[1] = 0xf0;
+ biosProtected[2] = 0x29;
+ biosProtected[3] = 0xe1;
+
+ for(i = 0; i < 256; i++)
+ {
+ int count = 0;
+ int j;
+ for(j = 0; j < 8; j++)
+ if(i & (1 << j))
+ count++;
+ cpuBitsSet[i] = count;
+
+ for(j = 0; j < 8; j++)
+ if(i & (1 << j))
+ break;
+ }
+
+ for(i = 0; i < 0x400; i++)
+ ioReadable[i] = true;
+ for(i = 0x10; i < 0x48; i++)
+ ioReadable[i] = false;
+ for(i = 0x4c; i < 0x50; i++)
+ ioReadable[i] = false;
+ for(i = 0x54; i < 0x60; i++)
+ ioReadable[i] = false;
+ for(i = 0x8c; i < 0x90; i++)
+ ioReadable[i] = false;
+ for(i = 0xa0; i < 0xb8; i++)
+ ioReadable[i] = false;
+ for(i = 0xbc; i < 0xc4; i++)
+ ioReadable[i] = false;
+ for(i = 0xc8; i < 0xd0; i++)
+ ioReadable[i] = false;
+ for(i = 0xd4; i < 0xdc; i++)
+ ioReadable[i] = false;
+ for(i = 0xe0; i < 0x100; i++)
+ ioReadable[i] = false;
+ for(i = 0x110; i < 0x120; i++)
+ ioReadable[i] = false;
+ for(i = 0x12c; i < 0x130; i++)
+ ioReadable[i] = false;
+ for(i = 0x138; i < 0x140; i++)
+ ioReadable[i] = false;
+ for(i = 0x144; i < 0x150; i++)
+ ioReadable[i] = false;
+ for(i = 0x15c; i < 0x200; i++)
+ ioReadable[i] = false;
+ for(i = 0x20c; i < 0x300; i++)
+ ioReadable[i] = false;
+ for(i = 0x304; i < 0x400; i++)
+ ioReadable[i] = false;
+
+ if(romSize < 0x1fe2000) {
+ *((uint16_t *)&rom[0x1fe209c]) = 0xdffa; // SWI 0xFA
+ *((uint16_t *)&rom[0x1fe209e]) = 0x4770; // BX LR
+ }
+
+ graphics.layerEnable = 0xff00;
+ graphics.layerEnableDelay = 1;
+ io_registers[REG_DISPCNT] = 0x0080;
+ io_registers[REG_DISPSTAT] = 0;
+ graphics.lcdTicks = (useBios && !skipBios) ? 1008 : 208;
+
+ /* address lut for use in CPUUpdateRegister */
+ address_lut[0x08] = &io_registers[REG_BG0CNT];
+ address_lut[0x0A] = &io_registers[REG_BG1CNT];
+ address_lut[0x0C] = &io_registers[REG_BG2CNT];
+ address_lut[0x0E] = &io_registers[REG_BG3CNT];
+ address_lut[0x10] = &io_registers[REG_BG0HOFS];
+ address_lut[0x12] = &io_registers[REG_BG0VOFS];
+ address_lut[0x14] = &io_registers[REG_BG1HOFS];
+ address_lut[0x16] = &io_registers[REG_BG1VOFS];
+ address_lut[0x18] = &io_registers[REG_BG2HOFS];
+ address_lut[0x1A] = &io_registers[REG_BG2VOFS];
+ address_lut[0x1C] = &io_registers[REG_BG3HOFS];
+ address_lut[0x1E] = &io_registers[REG_BG3VOFS];
+ address_lut[0x20] = &io_registers[REG_BG2PA];
+ address_lut[0x22] = &io_registers[REG_BG2PB];
+ address_lut[0x24] = &io_registers[REG_BG2PC];
+ address_lut[0x26] = &io_registers[REG_BG2PD];
+ address_lut[0x48] = &io_registers[REG_WININ];
+ address_lut[0x4A] = &io_registers[REG_WINOUT];
+ address_lut[0x30] = &io_registers[REG_BG3PA];
+ address_lut[0x32] = &io_registers[REG_BG3PB];
+ address_lut[0x34] = &io_registers[REG_BG3PC];
+ address_lut[0x36] = &io_registers[REG_BG3PD];
+ address_lut[0x40] = &io_registers[REG_WIN0H];
+ address_lut[0x42] = &io_registers[REG_WIN1H];
+ address_lut[0x44] = &io_registers[REG_WIN0V];
+ address_lut[0x46] = &io_registers[REG_WIN1V];
+}
+
+void CPUReset (void)
+{
+ if(gbaSaveType == 0)
+ {
+ if(eepromInUse)
+ gbaSaveType = 3;
+ else
+ switch(saveType)
+ {
+ case 1:
+ gbaSaveType = 1;
+ break;
+ case 2:
+ gbaSaveType = 2;
+ break;
+ }
+ }
+ rtcReset();
+ memset(&bus.reg[0], 0, sizeof(bus.reg)); // clean registers
+ memset(oam, 0, 0x400); // clean OAM
+ memset(graphics.paletteRAM, 0, 0x400); // clean palette
+ memset(pix, 0, 4 * 160 * 240); // clean picture
+ memset(vram, 0, 0x20000); // clean vram
+ memset(ioMem, 0, 0x400); // clean io memory
+
+ io_registers[REG_DISPCNT] = 0x0080;
+ io_registers[REG_DISPSTAT] = 0x0000;
+ io_registers[REG_VCOUNT] = (useBios && !skipBios) ? 0 :0x007E;
+ io_registers[REG_BG0CNT] = 0x0000;
+ io_registers[REG_BG1CNT] = 0x0000;
+ io_registers[REG_BG2CNT] = 0x0000;
+ io_registers[REG_BG3CNT] = 0x0000;
+ io_registers[REG_BG0HOFS] = 0x0000;
+ io_registers[REG_BG0VOFS] = 0x0000;
+ io_registers[REG_BG1HOFS] = 0x0000;
+ io_registers[REG_BG1VOFS] = 0x0000;
+ io_registers[REG_BG2HOFS] = 0x0000;
+ io_registers[REG_BG2VOFS] = 0x0000;
+ io_registers[REG_BG3HOFS] = 0x0000;
+ io_registers[REG_BG3VOFS] = 0x0000;
+ io_registers[REG_BG2PA] = 0x0100;
+ io_registers[REG_BG2PB] = 0x0000;
+ io_registers[REG_BG2PC] = 0x0000;
+ io_registers[REG_BG2PD] = 0x0100;
+ BG2X_L = 0x0000;
+ BG2X_H = 0x0000;
+ BG2Y_L = 0x0000;
+ BG2Y_H = 0x0000;
+ io_registers[REG_BG3PA] = 0x0100;
+ io_registers[REG_BG3PB] = 0x0000;
+ io_registers[REG_BG3PC] = 0x0000;
+ io_registers[REG_BG3PD] = 0x0100;
+ BG3X_L = 0x0000;
+ BG3X_H = 0x0000;
+ BG3Y_L = 0x0000;
+ BG3Y_H = 0x0000;
+ io_registers[REG_WIN0H] = 0x0000;
+ io_registers[REG_WIN1H] = 0x0000;
+ io_registers[REG_WIN0V] = 0x0000;
+ io_registers[REG_WIN1V] = 0x0000;
+ io_registers[REG_WININ] = 0x0000;
+ io_registers[REG_WINOUT] = 0x0000;
+ MOSAIC = 0x0000;
+ BLDMOD = 0x0000;
+ COLEV = 0x0000;
+ COLY = 0x0000;
+ DM0SAD_L = 0x0000;
+ DM0SAD_H = 0x0000;
+ DM0DAD_L = 0x0000;
+ DM0DAD_H = 0x0000;
+ DM0CNT_L = 0x0000;
+ DM0CNT_H = 0x0000;
+ DM1SAD_L = 0x0000;
+ DM1SAD_H = 0x0000;
+ DM1DAD_L = 0x0000;
+ DM1DAD_H = 0x0000;
+ DM1CNT_L = 0x0000;
+ DM1CNT_H = 0x0000;
+ DM2SAD_L = 0x0000;
+ DM2SAD_H = 0x0000;
+ DM2DAD_L = 0x0000;
+ DM2DAD_H = 0x0000;
+ DM2CNT_L = 0x0000;
+ DM2CNT_H = 0x0000;
+ DM3SAD_L = 0x0000;
+ DM3SAD_H = 0x0000;
+ DM3DAD_L = 0x0000;
+ DM3DAD_H = 0x0000;
+ DM3CNT_L = 0x0000;
+ DM3CNT_H = 0x0000;
+ io_registers[REG_TM0D] = 0x0000;
+ io_registers[REG_TM0CNT] = 0x0000;
+ io_registers[REG_TM1D] = 0x0000;
+ io_registers[REG_TM1CNT] = 0x0000;
+ io_registers[REG_TM2D] = 0x0000;
+ io_registers[REG_TM2CNT] = 0x0000;
+ io_registers[REG_TM3D] = 0x0000;
+ io_registers[REG_TM3CNT] = 0x0000;
+ io_registers[REG_P1] = 0x03FF;
+ io_registers[REG_IE] = 0x0000;
+ io_registers[REG_IF] = 0x0000;
+ io_registers[REG_IME] = 0x0000;
+
+ armMode = 0x1F;
+
+ if(cpuIsMultiBoot) {
+ bus.reg[13].I = 0x03007F00;
+ bus.reg[15].I = 0x02000000;
+ bus.reg[16].I = 0x00000000;
+ bus.reg[R13_IRQ].I = 0x03007FA0;
+ bus.reg[R13_SVC].I = 0x03007FE0;
+ armIrqEnable = true;
+ } else {
+#ifdef HAVE_HLE_BIOS
+ if(useBios && !skipBios)
+ {
+ bus.reg[15].I = 0x00000000;
+ armMode = 0x13;
+ armIrqEnable = false;
+ }
+ else
+ {
+#endif
+ bus.reg[13].I = 0x03007F00;
+ bus.reg[15].I = 0x08000000;
+ bus.reg[16].I = 0x00000000;
+ bus.reg[R13_IRQ].I = 0x03007FA0;
+ bus.reg[R13_SVC].I = 0x03007FE0;
+ armIrqEnable = true;
+#ifdef HAVE_HLE_BIOS
+ }
+#endif
+ }
+ armState = true;
+ C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false;
+ UPDATE_REG(0x00, io_registers[REG_DISPCNT]);
+ UPDATE_REG(0x06, io_registers[REG_VCOUNT]);
+ UPDATE_REG(0x20, io_registers[REG_BG2PA]);
+ UPDATE_REG(0x26, io_registers[REG_BG2PD]);
+ UPDATE_REG(0x30, io_registers[REG_BG3PA]);
+ UPDATE_REG(0x36, io_registers[REG_BG3PD]);
+ UPDATE_REG(0x130, io_registers[REG_P1]);
+ UPDATE_REG(0x88, 0x200);
+
+ // disable FIQ
+ bus.reg[16].I |= 0x40;
+
+ CPU_UPDATE_CPSR();
+
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 4;
+
+ // reset internal state
+ holdState = false;
+
+ biosProtected[0] = 0x00;
+ biosProtected[1] = 0xf0;
+ biosProtected[2] = 0x29;
+ biosProtected[3] = 0xe1;
+
+ graphics.lcdTicks = (useBios && !skipBios) ? 1008 : 208;
+ timer0On = false;
+ timer0Ticks = 0;
+ timer0Reload = 0;
+ timer0ClockReload = 0;
+ timer1On = false;
+ timer1Ticks = 0;
+ timer1Reload = 0;
+ timer1ClockReload = 0;
+ timer2On = false;
+ timer2Ticks = 0;
+ timer2Reload = 0;
+ timer2ClockReload = 0;
+ timer3On = false;
+ timer3Ticks = 0;
+ timer3Reload = 0;
+ timer3ClockReload = 0;
+ dma0Source = 0;
+ dma0Dest = 0;
+ dma1Source = 0;
+ dma1Dest = 0;
+ dma2Source = 0;
+ dma2Dest = 0;
+ dma3Source = 0;
+ dma3Dest = 0;
+ cpuSaveGameFunc = &Gigazoid::flashSaveDecide;
+ renderLine = &Gigazoid::mode0RenderLine;
+ fxOn = false;
+ windowOn = false;
+ saveType = 0;
+ graphics.layerEnable = io_registers[REG_DISPCNT];
+
+ memset(line[0], -1, 240 * sizeof(u32));
+ memset(line[1], -1, 240 * sizeof(u32));
+ memset(line[2], -1, 240 * sizeof(u32));
+ memset(line[3], -1, 240 * sizeof(u32));
+
+ for(int i = 0; i < 256; i++) {
+ map[i].address = 0;
+ map[i].mask = 0;
+ }
+
+ map[0].address = bios;
+ map[0].mask = 0x3FFF;
+ map[2].address = workRAM;
+ map[2].mask = 0x3FFFF;
+ map[3].address = internalRAM;
+ map[3].mask = 0x7FFF;
+ map[4].address = ioMem;
+ map[4].mask = 0x3FF;
+ map[5].address = graphics.paletteRAM;
+ map[5].mask = 0x3FF;
+ map[6].address = vram;
+ map[6].mask = 0x1FFFF;
+ map[7].address = oam;
+ map[7].mask = 0x3FF;
+ map[8].address = rom;
+ map[8].mask = 0x1FFFFFF;
+ map[9].address = rom;
+ map[9].mask = 0x1FFFFFF;
+ map[10].address = rom;
+ map[10].mask = 0x1FFFFFF;
+ map[12].address = rom;
+ map[12].mask = 0x1FFFFFF;
+ map[14].address = flashSaveMemory;
+ map[14].mask = 0xFFFF;
+
+ eepromReset();
+ flashReset();
+
+ soundReset();
+
+ CPUUpdateWindow0();
+ CPUUpdateWindow1();
+
+ // make sure registers are correctly initialized if not using BIOS
+ if(cpuIsMultiBoot)
+ BIOS_RegisterRamReset(0xfe);
+ else if(!useBios && !cpuIsMultiBoot)
+ BIOS_RegisterRamReset(0xff);
+
+ switch(cpuSaveType) {
+ case 0: // automatic
+ cpuSramEnabled = true;
+ cpuFlashEnabled = true;
+ cpuEEPROMEnabled = true;
+ cpuEEPROMSensorEnabled = false;
+ saveType = gbaSaveType = 0;
+ break;
+ case 1: // EEPROM
+ cpuSramEnabled = false;
+ cpuFlashEnabled = false;
+ cpuEEPROMEnabled = true;
+ cpuEEPROMSensorEnabled = false;
+ saveType = gbaSaveType = 3;
+ // EEPROM usage is automatically detected
+ break;
+ case 2: // SRAM
+ cpuSramEnabled = true;
+ cpuFlashEnabled = false;
+ cpuEEPROMEnabled = false;
+ cpuEEPROMSensorEnabled = false;
+ cpuSaveGameFunc = &Gigazoid::sramDelayedWrite; // to insure we detect the write
+ saveType = gbaSaveType = 1;
+ break;
+ case 3: // FLASH
+ cpuSramEnabled = false;
+ cpuFlashEnabled = true;
+ cpuEEPROMEnabled = false;
+ cpuEEPROMSensorEnabled = false;
+ cpuSaveGameFunc = &Gigazoid::flashDelayedWrite; // to insure we detect the write
+ saveType = gbaSaveType = 2;
+ break;
+ case 4: // EEPROM+Sensor
+ cpuSramEnabled = false;
+ cpuFlashEnabled = false;
+ cpuEEPROMEnabled = true;
+ cpuEEPROMSensorEnabled = true;
+ // EEPROM usage is automatically detected
+ saveType = gbaSaveType = 3;
+ break;
+ case 5: // NONE
+ cpuSramEnabled = false;
+ cpuFlashEnabled = false;
+ cpuEEPROMEnabled = false;
+ cpuEEPROMSensorEnabled = false;
+ // no save at all
+ saveType = gbaSaveType = 5;
+ break;
+ }
+
+ ARM_PREFETCH;
+
+#ifdef USE_SWITICKS
+ SWITicks = 0;
+#endif
+}
+
+void CPUInterrupt(void)
+{
+ uint32_t PC = bus.reg[15].I;
+ bool savedState = armState;
+
+ if(armMode != 0x12 )
+ CPUSwitchMode(0x12, true, false);
+
+ bus.reg[14].I = PC;
+ if(!savedState)
+ bus.reg[14].I += 2;
+ bus.reg[15].I = 0x18;
+ armState = true;
+ armIrqEnable = false;
+
+ bus.armNextPC = bus.reg[15].I;
+ bus.reg[15].I += 4;
+ ARM_PREFETCH;
+
+ // if(!holdState)
+ biosProtected[0] = 0x02;
+ biosProtected[1] = 0xc0;
+ biosProtected[2] = 0x5e;
+ biosProtected[3] = 0xe5;
+}
+
+void CPULoop (void)
+{
+ bus.busPrefetchCount = 0;
+ int ticks = 250000;
+ int timerOverflow = 0;
+ // variable used by the CPU core
+ cpuTotalTicks = 0;
+
+ cpuNextEvent = CPUUpdateTicks();
+ if(cpuNextEvent > ticks)
+ cpuNextEvent = ticks;
+
+
+ do
+ {
+ if(!holdState)
+ {
+ if(armState)
+ {
+ if (!armExecute())
+ return;
+ }
+ else
+ {
+ if (!thumbExecute())
+ return;
+ }
+ clockTicks = 0;
+ }
+ else
+ clockTicks = CPUUpdateTicks();
+
+ cpuTotalTicks += clockTicks;
+
+
+ if(cpuTotalTicks >= cpuNextEvent) {
+ int remainingTicks = cpuTotalTicks - cpuNextEvent;
+
+#ifdef USE_SWITICKS
+ if (SWITicks)
+ {
+ SWITicks-=clockTicks;
+ if (SWITicks<0)
+ SWITicks = 0;
+ }
+#endif
+
+ clockTicks = cpuNextEvent;
+ cpuTotalTicks = 0;
+
+updateLoop:
+
+ if (IRQTicks)
+ {
+ IRQTicks -= clockTicks;
+ if (IRQTicks<0)
+ IRQTicks = 0;
+ }
+
+ graphics.lcdTicks -= clockTicks;
+
+ if(graphics.lcdTicks <= 0)
+ {
+ if(io_registers[REG_DISPSTAT] & 1)
+ { // V-BLANK
+ // if in V-Blank mode, keep computing...
+ if(io_registers[REG_DISPSTAT] & 2)
+ {
+ graphics.lcdTicks += 1008;
+ io_registers[REG_VCOUNT] += 1;
+ UPDATE_REG(0x06, io_registers[REG_VCOUNT]);
+ io_registers[REG_DISPSTAT] &= 0xFFFD;
+ UPDATE_REG(0x04, io_registers[REG_DISPSTAT]);
+ CPUCompareVCOUNT();
+ }
+ else
+ {
+ graphics.lcdTicks += 224;
+ io_registers[REG_DISPSTAT] |= 2;
+ UPDATE_REG(0x04, io_registers[REG_DISPSTAT]);
+ if(io_registers[REG_DISPSTAT] & 16)
+ {
+ io_registers[REG_IF] |= 2;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ }
+ }
+
+ if(io_registers[REG_VCOUNT] >= 228)
+ {
+ //Reaching last line
+ io_registers[REG_DISPSTAT] &= 0xFFFC;
+ UPDATE_REG(0x04, io_registers[REG_DISPSTAT]);
+ io_registers[REG_VCOUNT] = 0;
+ UPDATE_REG(0x06, io_registers[REG_VCOUNT]);
+ CPUCompareVCOUNT();
+ }
+ }
+ else if(io_registers[REG_DISPSTAT] & 2)
+ {
+ // if in H-Blank, leave it and move to drawing mode
+ io_registers[REG_VCOUNT] += 1;
+ UPDATE_REG(0x06, io_registers[REG_VCOUNT]);
+
+ graphics.lcdTicks += 1008;
+ io_registers[REG_DISPSTAT] &= 0xFFFD;
+ if(io_registers[REG_VCOUNT] == 160)
+ {
+ /* update joystick information */
+ io_registers[REG_P1] = 0x03FF ^ (joy & 0x3FF);
+#if 0
+ if(cpuEEPROMSensorEnabled)
+ systemUpdateMotionSensor();
+#endif
+ UPDATE_REG(0x130, io_registers[REG_P1]);
+ io_registers[REG_P1CNT] = READ16LE(((uint16_t *)&ioMem[0x132]));
+
+ // this seems wrong, but there are cases where the game
+ // can enter the stop state without requesting an IRQ from
+ // the joypad.
+ if((io_registers[REG_P1CNT] & 0x4000) || stopState) {
+ uint16_t p1 = (0x3FF ^ io_registers[REG_P1CNT]) & 0x3FF;
+ if(io_registers[REG_P1CNT] & 0x8000) {
+ if(p1 == (io_registers[REG_P1CNT] & 0x3FF)) {
+ io_registers[REG_IF] |= 0x1000;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ }
+ } else {
+ if(p1 & io_registers[REG_P1CNT]) {
+ io_registers[REG_IF] |= 0x1000;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ }
+ }
+ }
+
+ io_registers[REG_DISPSTAT] |= 1;
+ io_registers[REG_DISPSTAT] &= 0xFFFD;
+ UPDATE_REG(0x04, io_registers[REG_DISPSTAT]);
+ if(io_registers[REG_DISPSTAT] & 0x0008)
+ {
+ io_registers[REG_IF] |= 1;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ }
+ CPUCheckDMA(1, 0x0f);
+ systemDrawScreen();
+ }
+
+ UPDATE_REG(0x04, io_registers[REG_DISPSTAT]);
+ CPUCompareVCOUNT();
+ }
+ else
+ {
+ bool draw_objwin = (graphics.layerEnable & 0x9000) == 0x9000;
+ bool draw_sprites = graphics.layerEnable & 0x1000;
+ memset(line[4], -1, 240 * sizeof(u32)); // erase all sprites
+
+ if(draw_sprites)
+ gfxDrawSprites();
+
+ if(render_line_all_enabled)
+ {
+ memset(line[5], -1, 240 * sizeof(u32)); // erase all OBJ Win
+ if(draw_objwin)
+ gfxDrawOBJWin();
+ }
+
+ (this->*renderLine)();
+
+ // entering H-Blank
+ io_registers[REG_DISPSTAT] |= 2;
+ UPDATE_REG(0x04, io_registers[REG_DISPSTAT]);
+ graphics.lcdTicks += 224;
+ CPUCheckDMA(2, 0x0f);
+ if(io_registers[REG_DISPSTAT] & 16)
+ {
+ io_registers[REG_IF] |= 2;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ }
+ }
+ }
+
+ // we shouldn't be doing sound in stop state, but we lose synchronization
+ // if sound is disabled, so in stop state, soundTick will just produce
+ // mute sound
+ soundTicks -= clockTicks;
+ if(!soundTicks)
+ {
+ process_sound_tick_fn();
+ soundTicks += SOUND_CLOCK_TICKS;
+ }
+
+ if(!stopState) {
+ if(timer0On) {
+ timer0Ticks -= clockTicks;
+ if(timer0Ticks <= 0) {
+ timer0Ticks += (0x10000 - timer0Reload) << timer0ClockReload;
+ timerOverflow |= 1;
+ soundTimerOverflow(0);
+ if(io_registers[REG_TM0CNT] & 0x40) {
+ io_registers[REG_IF] |= 0x08;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ }
+ }
+ io_registers[REG_TM0D] = 0xFFFF - (timer0Ticks >> timer0ClockReload);
+ UPDATE_REG(0x100, io_registers[REG_TM0D]);
+ }
+
+ if(timer1On) {
+ if(io_registers[REG_TM1CNT] & 4) {
+ if(timerOverflow & 1) {
+ io_registers[REG_TM1D]++;
+ if(io_registers[REG_TM1D] == 0) {
+ io_registers[REG_TM1D] += timer1Reload;
+ timerOverflow |= 2;
+ soundTimerOverflow(1);
+ if(io_registers[REG_TM1CNT] & 0x40) {
+ io_registers[REG_IF] |= 0x10;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ }
+ }
+ UPDATE_REG(0x104, io_registers[REG_TM1D]);
+ }
+ } else {
+ timer1Ticks -= clockTicks;
+ if(timer1Ticks <= 0) {
+ timer1Ticks += (0x10000 - timer1Reload) << timer1ClockReload;
+ timerOverflow |= 2;
+ soundTimerOverflow(1);
+ if(io_registers[REG_TM1CNT] & 0x40) {
+ io_registers[REG_IF] |= 0x10;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ }
+ }
+ io_registers[REG_TM1D] = 0xFFFF - (timer1Ticks >> timer1ClockReload);
+ UPDATE_REG(0x104, io_registers[REG_TM1D]);
+ }
+ }
+
+ if(timer2On) {
+ if(io_registers[REG_TM2CNT] & 4) {
+ if(timerOverflow & 2) {
+ io_registers[REG_TM2D]++;
+ if(io_registers[REG_TM2D] == 0) {
+ io_registers[REG_TM2D] += timer2Reload;
+ timerOverflow |= 4;
+ if(io_registers[REG_TM2CNT] & 0x40) {
+ io_registers[REG_IF] |= 0x20;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ }
+ }
+ UPDATE_REG(0x108, io_registers[REG_TM2D]);
+ }
+ } else {
+ timer2Ticks -= clockTicks;
+ if(timer2Ticks <= 0) {
+ timer2Ticks += (0x10000 - timer2Reload) << timer2ClockReload;
+ timerOverflow |= 4;
+ if(io_registers[REG_TM2CNT] & 0x40) {
+ io_registers[REG_IF] |= 0x20;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ }
+ }
+ io_registers[REG_TM2D] = 0xFFFF - (timer2Ticks >> timer2ClockReload);
+ UPDATE_REG(0x108, io_registers[REG_TM2D]);
+ }
+ }
+
+ if(timer3On) {
+ if(io_registers[REG_TM3CNT] & 4) {
+ if(timerOverflow & 4) {
+ io_registers[REG_TM3D]++;
+ if(io_registers[REG_TM3D] == 0) {
+ io_registers[REG_TM3D] += timer3Reload;
+ if(io_registers[REG_TM3CNT] & 0x40) {
+ io_registers[REG_IF] |= 0x40;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ }
+ }
+ UPDATE_REG(0x10C, io_registers[REG_TM3D]);
+ }
+ } else {
+ timer3Ticks -= clockTicks;
+ if(timer3Ticks <= 0) {
+ timer3Ticks += (0x10000 - timer3Reload) << timer3ClockReload;
+ if(io_registers[REG_TM3CNT] & 0x40) {
+ io_registers[REG_IF] |= 0x40;
+ UPDATE_REG(0x202, io_registers[REG_IF]);
+ }
+ }
+ io_registers[REG_TM3D] = 0xFFFF - (timer3Ticks >> timer3ClockReload);
+ UPDATE_REG(0x10C, io_registers[REG_TM3D]);
+ }
+ }
+ }
+
+ timerOverflow = 0;
+ ticks -= clockTicks;
+ cpuNextEvent = CPUUpdateTicks();
+
+ if(cpuDmaTicksToUpdate > 0)
+ {
+ if(cpuDmaTicksToUpdate > cpuNextEvent)
+ clockTicks = cpuNextEvent;
+ else
+ clockTicks = cpuDmaTicksToUpdate;
+ cpuDmaTicksToUpdate -= clockTicks;
+ if(cpuDmaTicksToUpdate < 0)
+ cpuDmaTicksToUpdate = 0;
+ goto updateLoop;
+ }
+
+ if(io_registers[REG_IF] && (io_registers[REG_IME] & 1) && armIrqEnable)
+ {
+ int res = io_registers[REG_IF] & io_registers[REG_IE];
+ if(stopState)
+ res &= 0x3080;
+ if(res)
+ {
+ if (intState)
+ {
+ if (!IRQTicks)
+ {
+ CPUInterrupt();
+ intState = false;
+ holdState = false;
+ stopState = false;
+ }
+ }
+ else
+ {
+ if (!holdState)
+ {
+ intState = true;
+ IRQTicks=7;
+ if (cpuNextEvent> IRQTicks)
+ cpuNextEvent = IRQTicks;
+ }
+ else
+ {
+ CPUInterrupt();
+ holdState = false;
+ stopState = false;
+ }
+ }
+
+#ifdef USE_SWITICKS
+ // Stops the SWI Ticks emulation if an IRQ is executed
+ //(to avoid problems with nested IRQ/SWI)
+ if (SWITicks)
+ SWITicks = 0;
+#endif
+ }
+ }
+
+ if(remainingTicks > 0) {
+ if(remainingTicks > cpuNextEvent)
+ clockTicks = cpuNextEvent;
+ else
+ clockTicks = remainingTicks;
+ remainingTicks -= clockTicks;
+ if(remainingTicks < 0)
+ remainingTicks = 0;
+ goto updateLoop;
+ }
+
+ if (timerOnOffDelay)
+ {
+ // Apply Timer
+ if (timerOnOffDelay & 1)
+ {
+ timer0ClockReload = TIMER_TICKS[timer0Value & 3];
+ if(!timer0On && (timer0Value & 0x80)) {
+ // reload the counter
+ io_registers[REG_TM0D] = timer0Reload;
+ timer0Ticks = (0x10000 - io_registers[REG_TM0D]) << timer0ClockReload;
+ UPDATE_REG(0x100, io_registers[REG_TM0D]);
+ }
+ timer0On = timer0Value & 0x80 ? true : false;
+ io_registers[REG_TM0CNT] = timer0Value & 0xC7;
+ UPDATE_REG(0x102, io_registers[REG_TM0CNT]);
+ }
+ if (timerOnOffDelay & 2)
+ {
+ timer1ClockReload = TIMER_TICKS[timer1Value & 3];
+ if(!timer1On && (timer1Value & 0x80)) {
+ // reload the counter
+ io_registers[REG_TM1D] = timer1Reload;
+ timer1Ticks = (0x10000 - io_registers[REG_TM1D]) << timer1ClockReload;
+ UPDATE_REG(0x104, io_registers[REG_TM1D]);
+ }
+ timer1On = timer1Value & 0x80 ? true : false;
+ io_registers[REG_TM1CNT] = timer1Value & 0xC7;
+ UPDATE_REG(0x106, io_registers[REG_TM1CNT]);
+ }
+ if (timerOnOffDelay & 4)
+ {
+ timer2ClockReload = TIMER_TICKS[timer2Value & 3];
+ if(!timer2On && (timer2Value & 0x80)) {
+ // reload the counter
+ io_registers[REG_TM2D] = timer2Reload;
+ timer2Ticks = (0x10000 - io_registers[REG_TM2D]) << timer2ClockReload;
+ UPDATE_REG(0x108, io_registers[REG_TM2D]);
+ }
+ timer2On = timer2Value & 0x80 ? true : false;
+ io_registers[REG_TM2CNT] = timer2Value & 0xC7;
+ UPDATE_REG(0x10A, io_registers[REG_TM2CNT]);
+ }
+ if (timerOnOffDelay & 8)
+ {
+ timer3ClockReload = TIMER_TICKS[timer3Value & 3];
+ if(!timer3On && (timer3Value & 0x80)) {
+ // reload the counter
+ io_registers[REG_TM3D] = timer3Reload;
+ timer3Ticks = (0x10000 - io_registers[REG_TM3D]) << timer3ClockReload;
+ UPDATE_REG(0x10C, io_registers[REG_TM3D]);
+ }
+ timer3On = timer3Value & 0x80 ? true : false;
+ io_registers[REG_TM3CNT] = timer3Value & 0xC7;
+ UPDATE_REG(0x10E, io_registers[REG_TM3CNT]);
+ }
+ cpuNextEvent = CPUUpdateTicks();
+ timerOnOffDelay = 0;
+ // End of Apply Timer
+ }
+
+ if(cpuNextEvent > ticks)
+ cpuNextEvent = ticks;
+
+ if(ticks <= 0)
+ break;
+
+ }
+ }while(1);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// END GBA.CPP
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void Gigazoid_Init()
+{
+ // one time constructor stuff
+
+ flashState = FLASH_READ_ARRAY;
+ flashReadState = FLASH_READ_ARRAY;
+ flashSize = 0x10000;
+ flashDeviceID = 0x1b;
+ flashManufacturerID = 0x32;
+ flashBank = 0;
+
+ eepromMode = EEPROM_IDLE;
+
+ eepromInUse = false;
+ eepromSize = 512;
+
+ rtcEnabled = false;
+
+ // this is fixed now
+ // soundSampleRate = 22050;
+
+ SOUND_CLOCK_TICKS = SOUND_CLOCK_TICKS_;
+ soundTicks = SOUND_CLOCK_TICKS_;
+
+ soundEnableFlag = 0x3ff; /* emulator channels enabled*/
+
+ armState = true;
+ armIrqEnable = true;
+ armMode = 0x1f;
+
+ romSize = 0x2000000;
+
+ cpuSramEnabled = true;
+ cpuFlashEnabled = true;
+ cpuEEPROMEnabled = true;
+ cpuEEPROMSensorEnabled = false;
+
+ cpuSaveGameFunc = &Gigazoid::flashSaveDecide;
+ renderLine = &Gigazoid::mode0RenderLine;
+
+ #define ARRAYINIT(n) memcpy((n), (n##_init), sizeof(n))
+ ARRAYINIT(memoryWait);
+ ARRAYINIT(memoryWaitSeq);
+ ARRAYINIT(memoryWait32);
+ ARRAYINIT(memoryWaitSeq32);
+ #undef ARRAYINIT
+}
+
+bool systemVideoFrameDone;
+u32 *systemVideoFrameDest;
+
+void systemDrawScreen (void)
+{
+ systemVideoFrameDone = true;
+ // upconvert 555->888 (TODO: BETTER)
+ for (int i = 0; i < 240 * 160; i++)
+ {
+ u32 input = pix[i];
+ u32 output = 0xff000000 |
+ input << 9 & 0xf80000 |
+ input << 6 & 0xf800 |
+ input << 3 & 0xf8;
+ systemVideoFrameDest[i] = output;
+ }
+}
+
+// TODO: fix up RTC so this is used
+//uint32_t systemGetClock (void) { return 0; }
+
+// TODO joypad: set "joy" lower 0x3ff
+
+void systemMessage(const char *, ...)
+{
+}
+
+// called at regular intervals on sound clock
+void systemOnWriteDataToSoundBuffer(int16_t * finalWave, int length) { }
+
+public:
+ Gigazoid()
+ {
+ Gigazoid_Init();
+ }
+
+ ~Gigazoid()
+ {
+ CPUCleanUp(); // todo: check me!
+ }
+
+ bool LoadRom(const u8 *romfile, const u32 romfilelen, const u8 *biosfile, const u32 biosfilelen)
+ {
+ if (biosfilelen != 16384)
+ return false;
+
+ // todo: set cpuismultiboot
+ if (!CPULoadRom(romfile, romfilelen))
+ return false;
+
+ // todo: populate these 4 variables from a syncsetting or a gamedb (vba_over.ini)
+ cpuSaveType = 0;
+ flashSize = 0x10000;
+ enableRtc = false;
+ mirroringEnable = false;
+
+ if(flashSize == 0x10000 || flashSize == 0x20000)
+ flashSetSize(flashSize);
+ if(enableRtc)
+ rtcEnable(enableRtc);
+ doMirroring(mirroringEnable);
+
+ CPUInit(biosfile, biosfilelen);
+ CPUReset();
+ soundReset();
+
+ return true;
+ }
+
+ void Reset()
+ {
+ CPUReset();
+ }
+
+ void FrameAdvance(int input, u32 *videobuffer)
+ {
+ joy = input;
+ systemVideoFrameDone = false;
+ systemVideoFrameDest = videobuffer;
+ do
+ {
+ CPULoop();
+ } while (!systemVideoFrameDone);
+ systemVideoFrameDest = nullptr;
+ }
+
+}; // class Gigazoid
+
+// zeroing mem operators: these are very important
+void *operator new(std::size_t n)
+{
+ void *p = std::malloc(n);
+ std::memset(p, 0, n);
+ return p;
+}
+void operator delete(void *p)
+{
+ std::free(p);
+}
+
+#define EXPORT extern "C" __declspec(dllexport)
+
+// public interface follows
+EXPORT Gigazoid *Create()
+{
+ return new Gigazoid();
+}
+
+EXPORT void Destroy(Gigazoid *g)
+{
+ delete g;
+}
+
+EXPORT int LoadRom(Gigazoid *g, const u8 *romfile, const u32 romfilelen, const u8 *biosfile, const u32 biosfilelen)
+{
+ return g->LoadRom(romfile, romfilelen, biosfile, biosfilelen);
+}
+
+EXPORT void Reset(Gigazoid *g)
+{
+ g->Reset();
+}
+
+EXPORT void FrameAdvance(Gigazoid *g, int input, u32 *videobuffer)
+{
+ g->FrameAdvance(input, videobuffer);
+}
+
+#include "optable.inc"
diff --git a/vbanext/instance.h b/vbanext/instance.h
new file mode 100644
index 0000000000..0e7a5499cc
--- /dev/null
+++ b/vbanext/instance.h
@@ -0,0 +1,194 @@
+#ifndef INSTANCE_H
+#define INSTANCE_H
+
+#define FLASH_128K_SZ 0x20000
+
+#define EEPROM_IDLE 0
+#define EEPROM_READADDRESS 1
+#define EEPROM_READDATA 2
+#define EEPROM_READDATA2 3
+#define EEPROM_WRITEDATA 4
+
+enum {
+ IMAGE_UNKNOWN,
+ IMAGE_GBA
+};
+
+#define SGCNT0_H 0x82
+#define FIFOA_L 0xa0
+#define FIFOA_H 0xa2
+#define FIFOB_L 0xa4
+#define FIFOB_H 0xa6
+
+#define BLIP_BUFFER_ACCURACY 16
+#define BLIP_PHASE_BITS 8
+#define BLIP_WIDEST_IMPULSE_ 16
+#define BLIP_BUFFER_EXTRA_ 18
+#define BLIP_RES 256
+#define BLIP_RES_MIN_ONE 255
+#define BLIP_SAMPLE_BITS 30
+#define BLIP_READER_DEFAULT_BASS 9
+#define BLIP_DEFAULT_LENGTH 250 /* 1/4th of a second */
+
+#define BUFS_SIZE 3
+#define STEREO 2
+
+#define CLK_MUL GB_APU_OVERCLOCK
+#define CLK_MUL_MUL_2 8
+#define CLK_MUL_MUL_4 16
+#define CLK_MUL_MUL_6 24
+#define CLK_MUL_MUL_8 32
+#define CLK_MUL_MUL_15 60
+#define CLK_MUL_MUL_32 128
+#define DAC_BIAS 7
+
+#define PERIOD_MASK 0x70
+#define SHIFT_MASK 0x07
+
+#define PERIOD2_MASK 0x1FFFF
+
+#define BANK40_MASK 0x40
+#define BANK_SIZE 32
+#define BANK_SIZE_MIN_ONE 31
+#define BANK_SIZE_DIV_TWO 16
+
+/* 11-bit frequency in NRx3 and NRx4*/
+#define GB_OSC_FREQUENCY() (((regs[4] & 7) << 8) + regs[3])
+
+#define WAVE_TYPE 0x100
+#define NOISE_TYPE 0x200
+#define MIXED_TYPE WAVE_TYPE | NOISE_TYPE
+#define TYPE_INDEX_MASK 0xFF
+
+#define BITS_16 0
+#define BITS_32 1
+
+#define R13_IRQ 18
+#define R14_IRQ 19
+#define SPSR_IRQ 20
+#define R13_USR 26
+#define R14_USR 27
+#define R13_SVC 28
+#define R14_SVC 29
+#define SPSR_SVC 30
+#define R13_ABT 31
+#define R14_ABT 32
+#define SPSR_ABT 33
+#define R13_UND 34
+#define R14_UND 35
+#define SPSR_UND 36
+#define R8_FIQ 37
+#define R9_FIQ 38
+#define R10_FIQ 39
+#define R11_FIQ 40
+#define R12_FIQ 41
+#define R13_FIQ 42
+#define R14_FIQ 43
+#define SPSR_FIQ 44
+
+typedef struct {
+ uint8_t *address;
+ uint32_t mask;
+} memoryMap;
+
+typedef union {
+ struct {
+#ifdef LSB_FIRST
+ uint8_t B0;
+ uint8_t B1;
+ uint8_t B2;
+ uint8_t B3;
+#else
+ uint8_t B3;
+ uint8_t B2;
+ uint8_t B1;
+ uint8_t B0;
+#endif
+ } B;
+ struct {
+#ifdef LSB_FIRST
+ uint16_t W0;
+ uint16_t W1;
+#else
+ uint16_t W1;
+ uint16_t W0;
+#endif
+ } W;
+#ifdef LSB_FIRST
+ uint32_t I;
+#else
+ volatile uint32_t I;
+#endif
+} reg_pair;
+
+typedef struct
+{
+ reg_pair reg[45];
+ bool busPrefetch;
+ bool busPrefetchEnable;
+ uint32_t busPrefetchCount;
+ uint32_t armNextPC;
+} bus_t;
+
+typedef struct
+{
+ uint8_t * paletteRAM;
+ int layerEnable;
+ int layerEnableDelay;
+ int lcdTicks;
+} graphics_t;
+
+/* Begins reading from buffer. Name should be unique to the current block.*/
+#define BLIP_READER_BEGIN( name, blip_buffer ) \
+ const int32_t * name##_reader_buf = (blip_buffer).buffer_;\
+ int32_t name##_reader_accum = (blip_buffer).reader_accum_
+
+/* Advances to next sample*/
+#define BLIP_READER_NEXT( name, bass ) \
+ (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass)))
+
+/* Ends reading samples from buffer. The number of samples read must now be removed
+ using Blip_Buffer::remove_samples(). */
+#define BLIP_READER_END( name, blip_buffer ) \
+ (void) ((blip_buffer).reader_accum_ = name##_reader_accum)
+
+#define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset)
+
+#define BLIP_READER_NEXT_IDX_( name, idx ) {\
+ name##_reader_accum -= name##_reader_accum >> BLIP_READER_DEFAULT_BASS;\
+ name##_reader_accum += name##_reader_buf [(idx)];\
+}
+
+#define BLIP_READER_NEXT_RAW_IDX_( name, idx ) {\
+ name##_reader_accum -= name##_reader_accum >> BLIP_READER_DEFAULT_BASS; \
+ name##_reader_accum += *(int32_t const*) ((char const*) name##_reader_buf + (idx)); \
+}
+
+#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
+ defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
+ #define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in
+#else
+ #define BLIP_CLAMP_( in ) (int16_t) in != in
+#endif
+
+/* Clamp sample to int16_t range */
+#define BLIP_CLAMP( sample, out ) { if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 24) ^ 0x7FFF; }
+#define GB_ENV_DAC_ENABLED() (regs[2] & 0xF8) /* Non-zero if DAC is enabled*/
+#define GB_NOISE_PERIOD2_INDEX() (regs[3] >> 4)
+#define GB_NOISE_PERIOD2(base) (base << GB_NOISE_PERIOD2_INDEX())
+#define GB_NOISE_LFSR_MASK() ((regs[3] & 0x08) ? ~0x4040 : ~0x4000)
+#define GB_WAVE_DAC_ENABLED() (regs[0] & 0x80) /* Non-zero if DAC is enabled*/
+
+#define reload_sweep_timer() \
+ sweep_delay = (regs [0] & PERIOD_MASK) >> 4; \
+ if ( !sweep_delay ) \
+ sweep_delay = 8;
+
+
+#ifdef __LIBRETRO__
+#define PIX_BUFFER_SCREEN_WIDTH 256
+#else
+#define PIX_BUFFER_SCREEN_WIDTH 240
+#endif
+
+#endif
diff --git a/vbanext/msvs/libvbanext/libvbanext.sln b/vbanext/msvs/libvbanext/libvbanext.sln
new file mode 100644
index 0000000000..3b47db7b92
--- /dev/null
+++ b/vbanext/msvs/libvbanext/libvbanext.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libvbanext", "libvbanext.vcxproj", "{DE666222-17A4-4B1E-8A5A-0A69838AE23F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DE666222-17A4-4B1E-8A5A-0A69838AE23F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DE666222-17A4-4B1E-8A5A-0A69838AE23F}.Debug|Win32.Build.0 = Debug|Win32
+ {DE666222-17A4-4B1E-8A5A-0A69838AE23F}.Release|Win32.ActiveCfg = Release|Win32
+ {DE666222-17A4-4B1E-8A5A-0A69838AE23F}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/vbanext/msvs/libvbanext/libvbanext.vcxproj b/vbanext/msvs/libvbanext/libvbanext.vcxproj
new file mode 100644
index 0000000000..56cc5d8cb6
--- /dev/null
+++ b/vbanext/msvs/libvbanext/libvbanext.vcxproj
@@ -0,0 +1,88 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+ {DE666222-17A4-4B1E-8A5A-0A69838AE23F}
+ libvbanext
+
+
+
+ DynamicLibrary
+ true
+ MultiByte
+
+
+ DynamicLibrary
+ false
+ true
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ Disabled
+ 4146;4800
+
+
+ true
+
+
+ copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\..\..\output\dll\$(TargetFileName)
+
+
+
+
+ Level3
+ Full
+ true
+ true
+ 4146;4800
+ AnySuitable
+ true
+
+
+ true
+ true
+ true
+
+
+ copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\..\..\output\dll\$(TargetFileName)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vbanext/msvs/libvbanext/libvbanext.vcxproj.filters b/vbanext/msvs/libvbanext/libvbanext.vcxproj.filters
new file mode 100644
index 0000000000..3deff3727c
--- /dev/null
+++ b/vbanext/msvs/libvbanext/libvbanext.vcxproj.filters
@@ -0,0 +1,47 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+ {25bb448a-4359-4425-bc82-a36c2dcddcb8}
+
+
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/vbanext/optable.inc b/vbanext/optable.inc
new file mode 100644
index 0000000000..d0cbc9eb00
--- /dev/null
+++ b/vbanext/optable.inc
@@ -0,0 +1,327 @@
+#define F(fcn) &Gigazoid::fcn
+
+#define REP16(insn) \
+ F(insn),F(insn),F(insn),F(insn),F(insn),F(insn),F(insn),F(insn),\
+ F(insn),F(insn),F(insn),F(insn),F(insn),F(insn),F(insn),F(insn)
+#define REP256(insn) \
+ REP16(insn),REP16(insn),REP16(insn),REP16(insn),\
+ REP16(insn),REP16(insn),REP16(insn),REP16(insn),\
+ REP16(insn),REP16(insn),REP16(insn),REP16(insn),\
+ REP16(insn),REP16(insn),REP16(insn),REP16(insn)
+
+#define arm_UI armUnknownInsn
+#define arm_BP armUnknownInsn
+
+#define thumbUI thumbUnknownInsn
+#define thumbBP thumbUnknownInsn
+
+void (Gigazoid::*const Gigazoid::armInsnTable[4096])(u32 opcode) = {
+ F(arm000),F(arm001),F(arm002),F(arm003),F(arm004),F(arm005),F(arm006),F(arm007), // F(000)
+ F(arm000),F(arm009),F(arm002),F(arm00B),F(arm004),F(arm_UI),F(arm006),F(arm_UI), // F(008)
+ F(arm010),F(arm011),F(arm012),F(arm013),F(arm014),F(arm015),F(arm016),F(arm017), // F(010)
+ F(arm010),F(arm019),F(arm012),F(arm01B),F(arm014),F(arm01D),F(arm016),F(arm01F), // F(018)
+ F(arm020),F(arm021),F(arm022),F(arm023),F(arm024),F(arm025),F(arm026),F(arm027), // F(020)
+ F(arm020),F(arm029),F(arm022),F(arm_UI),F(arm024),F(arm_UI),F(arm026),F(arm_UI), // F(028)
+ F(arm030),F(arm031),F(arm032),F(arm033),F(arm034),F(arm035),F(arm036),F(arm037), // F(030)
+ F(arm030),F(arm039),F(arm032),F(arm_UI),F(arm034),F(arm01D),F(arm036),F(arm01F), // F(038)
+ F(arm040),F(arm041),F(arm042),F(arm043),F(arm044),F(arm045),F(arm046),F(arm047), // F(040)
+ F(arm040),F(arm_UI),F(arm042),F(arm04B),F(arm044),F(arm_UI),F(arm046),F(arm_UI), // F(048)
+ F(arm050),F(arm051),F(arm052),F(arm053),F(arm054),F(arm055),F(arm056),F(arm057), // F(050)
+ F(arm050),F(arm_UI),F(arm052),F(arm05B),F(arm054),F(arm05D),F(arm056),F(arm05F), // F(058)
+ F(arm060),F(arm061),F(arm062),F(arm063),F(arm064),F(arm065),F(arm066),F(arm067), // F(060)
+ F(arm060),F(arm_UI),F(arm062),F(arm_UI),F(arm064),F(arm_UI),F(arm066),F(arm_UI), // F(068)
+ F(arm070),F(arm071),F(arm072),F(arm073),F(arm074),F(arm075),F(arm076),F(arm077), // F(070)
+ F(arm070),F(arm_UI),F(arm072),F(arm_UI),F(arm074),F(arm05D),F(arm076),F(arm05F), // F(078)
+ F(arm080),F(arm081),F(arm082),F(arm083),F(arm084),F(arm085),F(arm086),F(arm087), // F(080)
+ F(arm080),F(arm089),F(arm082),F(arm08B),F(arm084),F(arm_UI),F(arm086),F(arm_UI), // F(088)
+ F(arm090),F(arm091),F(arm092),F(arm093),F(arm094),F(arm095),F(arm096),F(arm097), // F(090)
+ F(arm090),F(arm099),F(arm092),F(arm09B),F(arm094),F(arm09D),F(arm096),F(arm09F), // F(098)
+ F(arm0A0),F(arm0A1),F(arm0A2),F(arm0A3),F(arm0A4),F(arm0A5),F(arm0A6),F(arm0A7), // F(0A0)
+ F(arm0A0),F(arm0A9),F(arm0A2),F(arm_UI),F(arm0A4),F(arm_UI),F(arm0A6),F(arm_UI), // F(0A8)
+ F(arm0B0),F(arm0B1),F(arm0B2),F(arm0B3),F(arm0B4),F(arm0B5),F(arm0B6),F(arm0B7), // F(0B0)
+ F(arm0B0),F(arm0B9),F(arm0B2),F(arm_UI),F(arm0B4),F(arm09D),F(arm0B6),F(arm09F), // F(0B8)
+ F(arm0C0),F(arm0C1),F(arm0C2),F(arm0C3),F(arm0C4),F(arm0C5),F(arm0C6),F(arm0C7), // F(0C0)
+ F(arm0C0),F(arm0C9),F(arm0C2),F(arm0CB),F(arm0C4),F(arm_UI),F(arm0C6),F(arm_UI), // F(0C8)
+ F(arm0D0),F(arm0D1),F(arm0D2),F(arm0D3),F(arm0D4),F(arm0D5),F(arm0D6),F(arm0D7), // F(0D0)
+ F(arm0D0),F(arm0D9),F(arm0D2),F(arm0DB),F(arm0D4),F(arm0DD),F(arm0D6),F(arm0DF), // F(0D8)
+ F(arm0E0),F(arm0E1),F(arm0E2),F(arm0E3),F(arm0E4),F(arm0E5),F(arm0E6),F(arm0E7), // F(0E0)
+ F(arm0E0),F(arm0E9),F(arm0E2),F(arm_UI),F(arm0E4),F(arm_UI),F(arm0E6),F(arm_UI), // F(0E8)
+ F(arm0F0),F(arm0F1),F(arm0F2),F(arm0F3),F(arm0F4),F(arm0F5),F(arm0F6),F(arm0F7), // F(0F0)
+ F(arm0F0),F(arm0F9),F(arm0F2),F(arm_UI),F(arm0F4),F(arm0DD),F(arm0F6),F(arm0DF), // F(0F8)
+
+ F(arm100),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(100)
+ F(arm_UI),F(arm109),F(arm_UI),F(arm10B),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(108)
+ F(arm110),F(arm111),F(arm112),F(arm113),F(arm114),F(arm115),F(arm116),F(arm117), // F(110)
+ F(arm110),F(arm_UI),F(arm112),F(arm11B),F(arm114),F(arm11D),F(arm116),F(arm11F), // F(118)
+ F(arm120),F(arm121),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_BP), // F(120)
+ F(arm_UI),F(arm_UI),F(arm_UI),F(arm12B),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(128)
+ F(arm130),F(arm131),F(arm132),F(arm133),F(arm134),F(arm135),F(arm136),F(arm137), // F(130)
+ F(arm130),F(arm_UI),F(arm132),F(arm13B),F(arm134),F(arm13D),F(arm136),F(arm13F), // F(138)
+ F(arm140),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(140)
+ F(arm_UI),F(arm149),F(arm_UI),F(arm14B),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(148)
+ F(arm150),F(arm151),F(arm152),F(arm153),F(arm154),F(arm155),F(arm156),F(arm157), // F(150)
+ F(arm150),F(arm_UI),F(arm152),F(arm15B),F(arm154),F(arm15D),F(arm156),F(arm15F), // F(158)
+ F(arm160),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(160)
+ F(arm_UI),F(arm_UI),F(arm_UI),F(arm16B),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(168)
+ F(arm170),F(arm171),F(arm172),F(arm173),F(arm174),F(arm175),F(arm176),F(arm177), // F(170)
+ F(arm170),F(arm_UI),F(arm172),F(arm17B),F(arm174),F(arm17D),F(arm176),F(arm17F), // F(178)
+ F(arm180),F(arm181),F(arm182),F(arm183),F(arm184),F(arm185),F(arm186),F(arm187), // F(180)
+ F(arm180),F(arm_UI),F(arm182),F(arm18B),F(arm184),F(arm_UI),F(arm186),F(arm_UI), // F(188)
+ F(arm190),F(arm191),F(arm192),F(arm193),F(arm194),F(arm195),F(arm196),F(arm197), // F(190)
+ F(arm190),F(arm_UI),F(arm192),F(arm19B),F(arm194),F(arm19D),F(arm196),F(arm19F), // F(198)
+ F(arm1A0),F(arm1A1),F(arm1A2),F(arm1A3),F(arm1A4),F(arm1A5),F(arm1A6),F(arm1A7), // F(1A0)
+ F(arm1A0),F(arm_UI),F(arm1A2),F(arm1AB),F(arm1A4),F(arm_UI),F(arm1A6),F(arm_UI), // F(1A8)
+ F(arm1B0),F(arm1B1),F(arm1B2),F(arm1B3),F(arm1B4),F(arm1B5),F(arm1B6),F(arm1B7), // F(1B0)
+ F(arm1B0),F(arm_UI),F(arm1B2),F(arm1BB),F(arm1B4),F(arm1BD),F(arm1B6),F(arm1BF), // F(1B8)
+ F(arm1C0),F(arm1C1),F(arm1C2),F(arm1C3),F(arm1C4),F(arm1C5),F(arm1C6),F(arm1C7), // F(1C0)
+ F(arm1C0),F(arm_UI),F(arm1C2),F(arm1CB),F(arm1C4),F(arm_UI),F(arm1C6),F(arm_UI), // F(1C8)
+ F(arm1D0),F(arm1D1),F(arm1D2),F(arm1D3),F(arm1D4),F(arm1D5),F(arm1D6),F(arm1D7), // F(1D0)
+ F(arm1D0),F(arm_UI),F(arm1D2),F(arm1DB),F(arm1D4),F(arm1DD),F(arm1D6),F(arm1DF), // F(1D8)
+ F(arm1E0),F(arm1E1),F(arm1E2),F(arm1E3),F(arm1E4),F(arm1E5),F(arm1E6),F(arm1E7), // F(1E0)
+ F(arm1E0),F(arm_UI),F(arm1E2),F(arm1EB),F(arm1E4),F(arm_UI),F(arm1E6),F(arm_UI), // F(1E8)
+ F(arm1F0),F(arm1F1),F(arm1F2),F(arm1F3),F(arm1F4),F(arm1F5),F(arm1F6),F(arm1F7), // F(1F0)
+ F(arm1F0),F(arm_UI),F(arm1F2),F(arm1FB),F(arm1F4),F(arm1FD),F(arm1F6),F(arm1FF), // F(1F8)
+
+ REP16(arm200),REP16(arm210),REP16(arm220),REP16(arm230), // 200
+ REP16(arm240),REP16(arm250),REP16(arm260),REP16(arm270), // 240
+ REP16(arm280),REP16(arm290),REP16(arm2A0),REP16(arm2B0), // 280
+ REP16(arm2C0),REP16(arm2D0),REP16(arm2E0),REP16(arm2F0), // 2C0
+ REP16(arm_UI),REP16(arm310),REP16(arm320),REP16(arm330), // 300
+ REP16(arm_UI),REP16(arm350),REP16(arm360),REP16(arm370), // 340
+ REP16(arm380),REP16(arm390),REP16(arm3A0),REP16(arm3B0), // 380
+ REP16(arm3C0),REP16(arm3D0),REP16(arm3E0),REP16(arm3F0), // 3C0
+
+ REP16(arm400),REP16(arm410),REP16(arm400),REP16(arm410), // 400
+ REP16(arm440),REP16(arm450),REP16(arm440),REP16(arm450), // 440
+ REP16(arm480),REP16(arm490),REP16(arm480),REP16(arm490), // 480
+ REP16(arm4C0),REP16(arm4D0),REP16(arm4C0),REP16(arm4D0), // 4C0
+ REP16(arm500),REP16(arm510),REP16(arm520),REP16(arm530), // 500
+ REP16(arm540),REP16(arm550),REP16(arm560),REP16(arm570), // 540
+ REP16(arm580),REP16(arm590),REP16(arm5A0),REP16(arm5B0), // 580
+ REP16(arm5C0),REP16(arm5D0),REP16(arm5E0),REP16(arm5F0), // 5C0
+
+ F(arm600),F(arm_UI),F(arm602),F(arm_UI),F(arm604),F(arm_UI),F(arm606),F(arm_UI), // F(600)
+ F(arm600),F(arm_UI),F(arm602),F(arm_UI),F(arm604),F(arm_UI),F(arm606),F(arm_UI), // F(608)
+ F(arm610),F(arm_UI),F(arm612),F(arm_UI),F(arm614),F(arm_UI),F(arm616),F(arm_UI), // F(610)
+ F(arm610),F(arm_UI),F(arm612),F(arm_UI),F(arm614),F(arm_UI),F(arm616),F(arm_UI), // F(618)
+ F(arm600),F(arm_UI),F(arm602),F(arm_UI),F(arm604),F(arm_UI),F(arm606),F(arm_UI), // F(620)
+ F(arm600),F(arm_UI),F(arm602),F(arm_UI),F(arm604),F(arm_UI),F(arm606),F(arm_UI), // F(628)
+ F(arm610),F(arm_UI),F(arm612),F(arm_UI),F(arm614),F(arm_UI),F(arm616),F(arm_UI), // F(630)
+ F(arm610),F(arm_UI),F(arm612),F(arm_UI),F(arm614),F(arm_UI),F(arm616),F(arm_UI), // F(638)
+ F(arm640),F(arm_UI),F(arm642),F(arm_UI),F(arm644),F(arm_UI),F(arm646),F(arm_UI), // F(640)
+ F(arm640),F(arm_UI),F(arm642),F(arm_UI),F(arm644),F(arm_UI),F(arm646),F(arm_UI), // F(648)
+ F(arm650),F(arm_UI),F(arm652),F(arm_UI),F(arm654),F(arm_UI),F(arm656),F(arm_UI), // F(650)
+ F(arm650),F(arm_UI),F(arm652),F(arm_UI),F(arm654),F(arm_UI),F(arm656),F(arm_UI), // F(658)
+ F(arm640),F(arm_UI),F(arm642),F(arm_UI),F(arm644),F(arm_UI),F(arm646),F(arm_UI), // F(660)
+ F(arm640),F(arm_UI),F(arm642),F(arm_UI),F(arm644),F(arm_UI),F(arm646),F(arm_UI), // F(668)
+ F(arm650),F(arm_UI),F(arm652),F(arm_UI),F(arm654),F(arm_UI),F(arm656),F(arm_UI), // F(670)
+ F(arm650),F(arm_UI),F(arm652),F(arm_UI),F(arm654),F(arm_UI),F(arm656),F(arm_UI), // F(678)
+ F(arm680),F(arm_UI),F(arm682),F(arm_UI),F(arm684),F(arm_UI),F(arm686),F(arm_UI), // F(680)
+ F(arm680),F(arm_UI),F(arm682),F(arm_UI),F(arm684),F(arm_UI),F(arm686),F(arm_UI), // F(688)
+ F(arm690),F(arm_UI),F(arm692),F(arm_UI),F(arm694),F(arm_UI),F(arm696),F(arm_UI), // F(690)
+ F(arm690),F(arm_UI),F(arm692),F(arm_UI),F(arm694),F(arm_UI),F(arm696),F(arm_UI), // F(698)
+ F(arm680),F(arm_UI),F(arm682),F(arm_UI),F(arm684),F(arm_UI),F(arm686),F(arm_UI), // F(6A0)
+ F(arm680),F(arm_UI),F(arm682),F(arm_UI),F(arm684),F(arm_UI),F(arm686),F(arm_UI), // F(6A8)
+ F(arm690),F(arm_UI),F(arm692),F(arm_UI),F(arm694),F(arm_UI),F(arm696),F(arm_UI), // F(6B0)
+ F(arm690),F(arm_UI),F(arm692),F(arm_UI),F(arm694),F(arm_UI),F(arm696),F(arm_UI), // F(6B8)
+ F(arm6C0),F(arm_UI),F(arm6C2),F(arm_UI),F(arm6C4),F(arm_UI),F(arm6C6),F(arm_UI), // F(6C0)
+ F(arm6C0),F(arm_UI),F(arm6C2),F(arm_UI),F(arm6C4),F(arm_UI),F(arm6C6),F(arm_UI), // F(6C8)
+ F(arm6D0),F(arm_UI),F(arm6D2),F(arm_UI),F(arm6D4),F(arm_UI),F(arm6D6),F(arm_UI), // F(6D0)
+ F(arm6D0),F(arm_UI),F(arm6D2),F(arm_UI),F(arm6D4),F(arm_UI),F(arm6D6),F(arm_UI), // F(6D8)
+ F(arm6C0),F(arm_UI),F(arm6C2),F(arm_UI),F(arm6C4),F(arm_UI),F(arm6C6),F(arm_UI), // F(6E0)
+ F(arm6C0),F(arm_UI),F(arm6C2),F(arm_UI),F(arm6C4),F(arm_UI),F(arm6C6),F(arm_UI), // F(6E8)
+ F(arm6D0),F(arm_UI),F(arm6D2),F(arm_UI),F(arm6D4),F(arm_UI),F(arm6D6),F(arm_UI), // F(6F0)
+ F(arm6D0),F(arm_UI),F(arm6D2),F(arm_UI),F(arm6D4),F(arm_UI),F(arm6D6),F(arm_UI), // F(6F8)
+
+ F(arm700),F(arm_UI),F(arm702),F(arm_UI),F(arm704),F(arm_UI),F(arm706),F(arm_UI), // F(700)
+ F(arm700),F(arm_UI),F(arm702),F(arm_UI),F(arm704),F(arm_UI),F(arm706),F(arm_UI), // F(708)
+ F(arm710),F(arm_UI),F(arm712),F(arm_UI),F(arm714),F(arm_UI),F(arm716),F(arm_UI), // F(710)
+ F(arm710),F(arm_UI),F(arm712),F(arm_UI),F(arm714),F(arm_UI),F(arm716),F(arm_UI), // F(718)
+ F(arm720),F(arm_UI),F(arm722),F(arm_UI),F(arm724),F(arm_UI),F(arm726),F(arm_UI), // F(720)
+ F(arm720),F(arm_UI),F(arm722),F(arm_UI),F(arm724),F(arm_UI),F(arm726),F(arm_UI), // F(728)
+ F(arm730),F(arm_UI),F(arm732),F(arm_UI),F(arm734),F(arm_UI),F(arm736),F(arm_UI), // F(730)
+ F(arm730),F(arm_UI),F(arm732),F(arm_UI),F(arm734),F(arm_UI),F(arm736),F(arm_UI), // F(738)
+ F(arm740),F(arm_UI),F(arm742),F(arm_UI),F(arm744),F(arm_UI),F(arm746),F(arm_UI), // F(740)
+ F(arm740),F(arm_UI),F(arm742),F(arm_UI),F(arm744),F(arm_UI),F(arm746),F(arm_UI), // F(748)
+ F(arm750),F(arm_UI),F(arm752),F(arm_UI),F(arm754),F(arm_UI),F(arm756),F(arm_UI), // F(750)
+ F(arm750),F(arm_UI),F(arm752),F(arm_UI),F(arm754),F(arm_UI),F(arm756),F(arm_UI), // F(758)
+ F(arm760),F(arm_UI),F(arm762),F(arm_UI),F(arm764),F(arm_UI),F(arm766),F(arm_UI), // F(760)
+ F(arm760),F(arm_UI),F(arm762),F(arm_UI),F(arm764),F(arm_UI),F(arm766),F(arm_UI), // F(768)
+ F(arm770),F(arm_UI),F(arm772),F(arm_UI),F(arm774),F(arm_UI),F(arm776),F(arm_UI), // F(770)
+ F(arm770),F(arm_UI),F(arm772),F(arm_UI),F(arm774),F(arm_UI),F(arm776),F(arm_UI), // F(778)
+ F(arm780),F(arm_UI),F(arm782),F(arm_UI),F(arm784),F(arm_UI),F(arm786),F(arm_UI), // F(780)
+ F(arm780),F(arm_UI),F(arm782),F(arm_UI),F(arm784),F(arm_UI),F(arm786),F(arm_UI), // F(788)
+ F(arm790),F(arm_UI),F(arm792),F(arm_UI),F(arm794),F(arm_UI),F(arm796),F(arm_UI), // F(790)
+ F(arm790),F(arm_UI),F(arm792),F(arm_UI),F(arm794),F(arm_UI),F(arm796),F(arm_UI), // F(798)
+ F(arm7A0),F(arm_UI),F(arm7A2),F(arm_UI),F(arm7A4),F(arm_UI),F(arm7A6),F(arm_UI), // F(7A0)
+ F(arm7A0),F(arm_UI),F(arm7A2),F(arm_UI),F(arm7A4),F(arm_UI),F(arm7A6),F(arm_UI), // F(7A8)
+ F(arm7B0),F(arm_UI),F(arm7B2),F(arm_UI),F(arm7B4),F(arm_UI),F(arm7B6),F(arm_UI), // F(7B0)
+ F(arm7B0),F(arm_UI),F(arm7B2),F(arm_UI),F(arm7B4),F(arm_UI),F(arm7B6),F(arm_UI), // F(7B8)
+ F(arm7C0),F(arm_UI),F(arm7C2),F(arm_UI),F(arm7C4),F(arm_UI),F(arm7C6),F(arm_UI), // F(7C0)
+ F(arm7C0),F(arm_UI),F(arm7C2),F(arm_UI),F(arm7C4),F(arm_UI),F(arm7C6),F(arm_UI), // F(7C8)
+ F(arm7D0),F(arm_UI),F(arm7D2),F(arm_UI),F(arm7D4),F(arm_UI),F(arm7D6),F(arm_UI), // F(7D0)
+ F(arm7D0),F(arm_UI),F(arm7D2),F(arm_UI),F(arm7D4),F(arm_UI),F(arm7D6),F(arm_UI), // F(7D8)
+ F(arm7E0),F(arm_UI),F(arm7E2),F(arm_UI),F(arm7E4),F(arm_UI),F(arm7E6),F(arm_UI), // F(7E0)
+ F(arm7E0),F(arm_UI),F(arm7E2),F(arm_UI),F(arm7E4),F(arm_UI),F(arm7E6),F(arm_UI), // F(7E8)
+ F(arm7F0),F(arm_UI),F(arm7F2),F(arm_UI),F(arm7F4),F(arm_UI),F(arm7F6),F(arm_UI), // F(7F0)
+ F(arm7F0),F(arm_UI),F(arm7F2),F(arm_UI),F(arm7F4),F(arm_UI),F(arm7F6),F(arm_BP), // F(7F8)
+
+ REP16(arm800),REP16(arm810),REP16(arm820),REP16(arm830), // 800
+ REP16(arm840),REP16(arm850),REP16(arm860),REP16(arm870), // 840
+ REP16(arm880),REP16(arm890),REP16(arm8A0),REP16(arm8B0), // 880
+ REP16(arm8C0),REP16(arm8D0),REP16(arm8E0),REP16(arm8F0), // 8C0
+ REP16(arm900),REP16(arm910),REP16(arm920),REP16(arm930), // 900
+ REP16(arm940),REP16(arm950),REP16(arm960),REP16(arm970), // 940
+ REP16(arm980),REP16(arm990),REP16(arm9A0),REP16(arm9B0), // 980
+ REP16(arm9C0),REP16(arm9D0),REP16(arm9E0),REP16(arm9F0), // 9C0
+
+ REP256(armA00), // A00
+ REP256(armB00), // B00
+ REP256(arm_UI), // C00
+ REP256(arm_UI), // D00
+
+ F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01), // F(E00)
+ F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01), // F(E08)
+ F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01), // F(E10)
+ F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01), // F(E18)
+ REP16(arm_UI), // E20
+ REP16(arm_UI), // E30
+ REP16(arm_UI),REP16(arm_UI),REP16(arm_UI),REP16(arm_UI), // E40
+ REP16(arm_UI),REP16(arm_UI),REP16(arm_UI),REP16(arm_UI), // E80
+ REP16(arm_UI),REP16(arm_UI),REP16(arm_UI),REP16(arm_UI), // EC0
+
+ REP256(armF00), // F00
+};
+
+void (Gigazoid::*const Gigazoid::thumbInsnTable[1024])(u32 opcode) = {
+ F(thumb00_00),F(thumb00_01),F(thumb00_02),F(thumb00_03),F(thumb00_04),F(thumb00_05),F(thumb00_06),F(thumb00_07), // F(00)
+ F(thumb00_08),F(thumb00_09),F(thumb00_0A),F(thumb00_0B),F(thumb00_0C),F(thumb00_0D),F(thumb00_0E),F(thumb00_0F),
+ F(thumb00_10),F(thumb00_11),F(thumb00_12),F(thumb00_13),F(thumb00_14),F(thumb00_15),F(thumb00_16),F(thumb00_17),
+ F(thumb00_18),F(thumb00_19),F(thumb00_1A),F(thumb00_1B),F(thumb00_1C),F(thumb00_1D),F(thumb00_1E),F(thumb00_1F),
+ F(thumb08_00),F(thumb08_01),F(thumb08_02),F(thumb08_03),F(thumb08_04),F(thumb08_05),F(thumb08_06),F(thumb08_07), // F(08)
+ F(thumb08_08),F(thumb08_09),F(thumb08_0A),F(thumb08_0B),F(thumb08_0C),F(thumb08_0D),F(thumb08_0E),F(thumb08_0F),
+ F(thumb08_10),F(thumb08_11),F(thumb08_12),F(thumb08_13),F(thumb08_14),F(thumb08_15),F(thumb08_16),F(thumb08_17),
+ F(thumb08_18),F(thumb08_19),F(thumb08_1A),F(thumb08_1B),F(thumb08_1C),F(thumb08_1D),F(thumb08_1E),F(thumb08_1F),
+ F(thumb10_00),F(thumb10_01),F(thumb10_02),F(thumb10_03),F(thumb10_04),F(thumb10_05),F(thumb10_06),F(thumb10_07), // F(10)
+ F(thumb10_08),F(thumb10_09),F(thumb10_0A),F(thumb10_0B),F(thumb10_0C),F(thumb10_0D),F(thumb10_0E),F(thumb10_0F),
+ F(thumb10_10),F(thumb10_11),F(thumb10_12),F(thumb10_13),F(thumb10_14),F(thumb10_15),F(thumb10_16),F(thumb10_17),
+ F(thumb10_18),F(thumb10_19),F(thumb10_1A),F(thumb10_1B),F(thumb10_1C),F(thumb10_1D),F(thumb10_1E),F(thumb10_1F),
+ F(thumb18_0),F(thumb18_1),F(thumb18_2),F(thumb18_3),F(thumb18_4),F(thumb18_5),F(thumb18_6),F(thumb18_7), // F(18)
+ F(thumb1A_0),F(thumb1A_1),F(thumb1A_2),F(thumb1A_3),F(thumb1A_4),F(thumb1A_5),F(thumb1A_6),F(thumb1A_7),
+ F(thumb1C_0),F(thumb1C_1),F(thumb1C_2),F(thumb1C_3),F(thumb1C_4),F(thumb1C_5),F(thumb1C_6),F(thumb1C_7),
+ F(thumb1E_0),F(thumb1E_1),F(thumb1E_2),F(thumb1E_3),F(thumb1E_4),F(thumb1E_5),F(thumb1E_6),F(thumb1E_7),
+ F(thumb20),F(thumb20),F(thumb20),F(thumb20),F(thumb21),F(thumb21),F(thumb21),F(thumb21), // F(20)
+ F(thumb22),F(thumb22),F(thumb22),F(thumb22),F(thumb23),F(thumb23),F(thumb23),F(thumb23),
+ F(thumb24),F(thumb24),F(thumb24),F(thumb24),F(thumb25),F(thumb25),F(thumb25),F(thumb25),
+ F(thumb26),F(thumb26),F(thumb26),F(thumb26),F(thumb27),F(thumb27),F(thumb27),F(thumb27),
+ F(thumb28),F(thumb28),F(thumb28),F(thumb28),F(thumb29),F(thumb29),F(thumb29),F(thumb29), // F(28)
+ F(thumb2A),F(thumb2A),F(thumb2A),F(thumb2A),F(thumb2B),F(thumb2B),F(thumb2B),F(thumb2B),
+ F(thumb2C),F(thumb2C),F(thumb2C),F(thumb2C),F(thumb2D),F(thumb2D),F(thumb2D),F(thumb2D),
+ F(thumb2E),F(thumb2E),F(thumb2E),F(thumb2E),F(thumb2F),F(thumb2F),F(thumb2F),F(thumb2F),
+ F(thumb30),F(thumb30),F(thumb30),F(thumb30),F(thumb31),F(thumb31),F(thumb31),F(thumb31), // F(30)
+ F(thumb32),F(thumb32),F(thumb32),F(thumb32),F(thumb33),F(thumb33),F(thumb33),F(thumb33),
+ F(thumb34),F(thumb34),F(thumb34),F(thumb34),F(thumb35),F(thumb35),F(thumb35),F(thumb35),
+ F(thumb36),F(thumb36),F(thumb36),F(thumb36),F(thumb37),F(thumb37),F(thumb37),F(thumb37),
+ F(thumb38),F(thumb38),F(thumb38),F(thumb38),F(thumb39),F(thumb39),F(thumb39),F(thumb39), // F(38)
+ F(thumb3A),F(thumb3A),F(thumb3A),F(thumb3A),F(thumb3B),F(thumb3B),F(thumb3B),F(thumb3B),
+ F(thumb3C),F(thumb3C),F(thumb3C),F(thumb3C),F(thumb3D),F(thumb3D),F(thumb3D),F(thumb3D),
+ F(thumb3E),F(thumb3E),F(thumb3E),F(thumb3E),F(thumb3F),F(thumb3F),F(thumb3F),F(thumb3F),
+ F(thumb40_0),F(thumb40_1),F(thumb40_2),F(thumb40_3),F(thumb41_0),F(thumb41_1),F(thumb41_2),F(thumb41_3), // F(40)
+ F(thumb42_0),F(thumb42_1),F(thumb42_2),F(thumb42_3),F(thumb43_0),F(thumb43_1),F(thumb43_2),F(thumb43_3),
+ F(thumbUI),F(thumb44_1),F(thumb44_2),F(thumb44_3),F(thumbUI),F(thumb45_1),F(thumb45_2),F(thumb45_3),
+ F(thumbUI),F(thumb46_1),F(thumb46_2),F(thumb46_3),F(thumb47),F(thumb47),F(thumbUI),F(thumbUI),
+ F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48), // F(48)
+ F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),
+ F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),
+ F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),
+ F(thumb50),F(thumb50),F(thumb50),F(thumb50),F(thumb50),F(thumb50),F(thumb50),F(thumb50), // F(50)
+ F(thumb52),F(thumb52),F(thumb52),F(thumb52),F(thumb52),F(thumb52),F(thumb52),F(thumb52),
+ F(thumb54),F(thumb54),F(thumb54),F(thumb54),F(thumb54),F(thumb54),F(thumb54),F(thumb54),
+ F(thumb56),F(thumb56),F(thumb56),F(thumb56),F(thumb56),F(thumb56),F(thumb56),F(thumb56),
+ F(thumb58),F(thumb58),F(thumb58),F(thumb58),F(thumb58),F(thumb58),F(thumb58),F(thumb58), // F(58)
+ F(thumb5A),F(thumb5A),F(thumb5A),F(thumb5A),F(thumb5A),F(thumb5A),F(thumb5A),F(thumb5A),
+ F(thumb5C),F(thumb5C),F(thumb5C),F(thumb5C),F(thumb5C),F(thumb5C),F(thumb5C),F(thumb5C),
+ F(thumb5E),F(thumb5E),F(thumb5E),F(thumb5E),F(thumb5E),F(thumb5E),F(thumb5E),F(thumb5E),
+ F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60), // F(60)
+ F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),
+ F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),
+ F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),
+ F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68), // F(68)
+ F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),
+ F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),
+ F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),
+ F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70), // F(70)
+ F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),
+ F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),
+ F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),
+ F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78), // F(78)
+ F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),
+ F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),
+ F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),
+ F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80), // F(80)
+ F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),
+ F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),
+ F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),
+ F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88), // F(88)
+ F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),
+ F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),
+ F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),
+ F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90), // F(90)
+ F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),
+ F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),
+ F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),
+ F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98), // F(98)
+ F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),
+ F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),
+ F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),
+ F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0), // F(A0)
+ F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),
+ F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),
+ F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),
+ F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8), // F(A8)
+ F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),
+ F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),
+ F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),
+ F(thumbB0),F(thumbB0),F(thumbB0),F(thumbB0),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), // F(B0)
+ F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),
+ F(thumbB4),F(thumbB4),F(thumbB4),F(thumbB4),F(thumbB5),F(thumbB5),F(thumbB5),F(thumbB5),
+ F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),
+ F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), // F(B8)
+ F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),
+ F(thumbBC),F(thumbBC),F(thumbBC),F(thumbBC),F(thumbBD),F(thumbBD),F(thumbBD),F(thumbBD),
+ F(thumbBP),F(thumbBP),F(thumbBP),F(thumbBP),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),
+ F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0), // F(C0)
+ F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),
+ F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),
+ F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),
+ F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8), // F(C8)
+ F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),
+ F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),
+ F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),
+ F(thumbD0),F(thumbD0),F(thumbD0),F(thumbD0),F(thumbD1),F(thumbD1),F(thumbD1),F(thumbD1), // F(D0)
+ F(thumbD2),F(thumbD2),F(thumbD2),F(thumbD2),F(thumbD3),F(thumbD3),F(thumbD3),F(thumbD3),
+ F(thumbD4),F(thumbD4),F(thumbD4),F(thumbD4),F(thumbD5),F(thumbD5),F(thumbD5),F(thumbD5),
+ F(thumbD6),F(thumbD6),F(thumbD6),F(thumbD6),F(thumbD7),F(thumbD7),F(thumbD7),F(thumbD7),
+ F(thumbD8),F(thumbD8),F(thumbD8),F(thumbD8),F(thumbD9),F(thumbD9),F(thumbD9),F(thumbD9), // F(D8)
+ F(thumbDA),F(thumbDA),F(thumbDA),F(thumbDA),F(thumbDB),F(thumbDB),F(thumbDB),F(thumbDB),
+ F(thumbDC),F(thumbDC),F(thumbDC),F(thumbDC),F(thumbDD),F(thumbDD),F(thumbDD),F(thumbDD),
+ F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbDF),F(thumbDF),F(thumbDF),F(thumbDF),
+ F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0), // F(E0)
+ F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),
+ F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),
+ F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),
+ F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), // F(E8)
+ F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),
+ F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),
+ F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),
+ F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0), // F(F0)
+ F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),
+ F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),
+ F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),
+ F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8), // F(F8)
+ F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),
+ F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),
+ F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),
+};
+
+#undef F
diff --git a/vbanext/port.h b/vbanext/port.h
new file mode 100644
index 0000000000..3b2457042e
--- /dev/null
+++ b/vbanext/port.h
@@ -0,0 +1,57 @@
+#ifndef PORT_H
+#define PORT_H
+
+#include "types.h"
+
+/* if a >= 0 return x else y*/
+#define isel(a, x, y) ((x & (~(a >> 31))) + (y & (a >> 31)))
+
+#ifdef FRONTEND_SUPPORTS_RGB565
+/* 16bit color - RGB565 */
+#define RED_MASK 0xf800
+#define GREEN_MASK 0x7e0
+#define BLUE_MASK 0x1f
+#define RED_EXPAND 3
+#define GREEN_EXPAND 2
+#define BLUE_EXPAND 3
+#define RED_SHIFT 11
+#define GREEN_SHIFT 5
+#define BLUE_SHIFT 0
+#define CONVERT_COLOR(color) (((color & 0x001f) << 11) | ((color & 0x03e0) << 1) | ((color & 0x0200) >> 4) | ((color & 0x7c00) >> 10))
+#else
+/* 16bit color - RGB555 */
+#define RED_MASK 0x7c00
+#define GREEN_MASK 0x3e0
+#define BLUE_MASK 0x1f
+#define RED_EXPAND 3
+#define GREEN_EXPAND 3
+#define BLUE_EXPAND 3
+#define RED_SHIFT 10
+#define GREEN_SHIFT 5
+#define BLUE_SHIFT 0
+#define CONVERT_COLOR(color) ((((color & 0x1f) << 10) | (((color & 0x3e0) >> 5) << 5) | (((color & 0x7c00) >> 10))) & 0x7fff)
+#endif
+
+#ifdef _MSC_VER
+#include
+#define strcasecmp _stricmp
+#endif
+
+#ifdef USE_CACHE_PREFETCH
+#if defined(__ANDROID__)
+#define CACHE_PREFETCH(prefetch) prefetch(&prefetch);
+#elif defined(_XBOX)
+#define CACHE_PREFETCH(prefetch) __dcbt(0, &prefetch);
+#else
+#define CACHE_PREFETCH(prefetch) __dcbt(&prefetch);
+#endif
+#else
+#define CACHE_PREFETCH(prefetch)
+#endif
+
+#define READ16LE(x) *((u16 *)x)
+#define READ32LE(x) *((u32 *)x)
+#define WRITE16LE(x,v) *((u16 *)x) = (v)
+#define WRITE32LE(x,v) *((u32 *)x) = (v)
+
+#endif
diff --git a/vbanext/sound_blargg.h b/vbanext/sound_blargg.h
new file mode 100644
index 0000000000..c80d61e18e
--- /dev/null
+++ b/vbanext/sound_blargg.h
@@ -0,0 +1,57 @@
+#ifndef SOUND_BLARGG_H
+#define SOUND_BLARGG_H
+
+/* Uncomment to have Gb_Apu run at 4x normal clock rate (16777216 Hz), useful in
+a Game Boy Advance emulator. */
+#define GB_APU_OVERCLOCK 4
+
+#ifndef STATIC_CAST
+ #if __GNUC__ >= 4
+ #define STATIC_CAST(T,expr) static_cast (expr)
+ #define CONST_CAST( T,expr) const_cast (expr)
+ #else
+ #define STATIC_CAST(T,expr) ((T) (expr))
+ #define CONST_CAST( T,expr) ((T) (expr))
+ #endif
+#endif
+
+// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1,
+// compiler is assumed to support bool. If undefined, availability is determined.
+#ifndef BLARGG_COMPILER_HAS_BOOL
+ #if defined (__MWERKS__)
+ #if !__option(bool)
+ #define BLARGG_COMPILER_HAS_BOOL 0
+ #endif
+ #elif defined (_MSC_VER)
+ #if _MSC_VER < 1100
+ #define BLARGG_COMPILER_HAS_BOOL 0
+ #endif
+ #elif defined (__GNUC__)
+ // supports bool
+ #elif __cplusplus < 199711
+ #define BLARGG_COMPILER_HAS_BOOL 0
+ #endif
+#endif
+#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL
+ typedef int bool;
+ const bool true = 1;
+ const bool false = 0;
+#endif
+
+/* HAVE_STDINT_H: If defined, use for int8_t etc.*/
+#if defined (HAVE_STDINT_H)
+ #include
+/* HAVE_INTTYPES_H: If defined, use for int8_t etc.*/
+#elif defined (HAVE_INTTYPES_H)
+ #include
+#endif
+
+// If expr yields non-NULL error string, returns it from current function,
+// otherwise continues normally.
+#undef RETURN_ERR
+#define RETURN_ERR( expr ) do { \
+ const char * blargg_return_err_ = (expr); \
+ if ( blargg_return_err_ ) return blargg_return_err_; \
+ } while ( 0 )
+
+#endif // #ifndef SOUND_BLARGG_H
diff --git a/vbanext/types.h b/vbanext/types.h
new file mode 100644
index 0000000000..8433576dcc
--- /dev/null
+++ b/vbanext/types.h
@@ -0,0 +1,33 @@
+// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
+// Copyright (C) 2008 VBA-M development team
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2, or(at your option)
+// any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef __VBA_TYPES_H__
+#define __VBA_TYPES_H__
+
+#include
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+typedef int8_t s8;
+typedef int16_t s16;
+typedef int32_t s32;
+typedef int64_t s64;
+
+#endif // __VBA_TYPES_H__