From 80d1723a3a6627c73caa9ecf2e29891f89c2c3d2 Mon Sep 17 00:00:00 2001 From: goyuken Date: Fri, 8 Aug 2014 00:55:33 +0000 Subject: [PATCH] https://www.youtube.com/watch?v=XwzzyzUcNOQ --- BizHawk.Client.Common/RomLoader.cs | 14 +- .../BizHawk.Emulation.Cores.csproj | 2 + .../Consoles/Nintendo/GBA/LibVBANext.cs | 71 + .../Consoles/Nintendo/GBA/VBANext.cs | 247 + output/dll/libvbanext.dll | Bin 0 -> 428544 bytes vbanext/constarrays.h | 54 + vbanext/instance.cpp | 13286 ++++++++++++++++ vbanext/instance.h | 194 + vbanext/msvs/libvbanext/libvbanext.sln | 20 + vbanext/msvs/libvbanext/libvbanext.vcxproj | 88 + .../libvbanext/libvbanext.vcxproj.filters | 47 + vbanext/optable.inc | 327 + vbanext/port.h | 57 + vbanext/sound_blargg.h | 57 + vbanext/types.h | 33 + 15 files changed, 14494 insertions(+), 3 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibVBANext.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.cs create mode 100644 output/dll/libvbanext.dll create mode 100644 vbanext/constarrays.h create mode 100644 vbanext/instance.cpp create mode 100644 vbanext/instance.h create mode 100644 vbanext/msvs/libvbanext/libvbanext.sln create mode 100644 vbanext/msvs/libvbanext/libvbanext.vcxproj create mode 100644 vbanext/msvs/libvbanext/libvbanext.vcxproj.filters create mode 100644 vbanext/optable.inc create mode 100644 vbanext/port.h create mode 100644 vbanext/sound_blargg.h create mode 100644 vbanext/types.h 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 0000000000000000000000000000000000000000..a7a773b1f1d47d13db59a8feebf4a0ef26b7b6ec GIT binary patch literal 428544 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
MT2vVsAB#C|J0Sy~KBir!m= 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 +#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__