diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 994f7e1922..c433152c8a 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -422,9 +422,17 @@ namespace BizHawk.Client.Common nextEmulator = c64; break; case "GBA": - var gba = new GBA(nextComm); - gba.Load(rom.RomData); - nextEmulator = gba; + if (false) + { + var gba = new GBA(nextComm); + gba.Load(rom.RomData); + nextEmulator = gba; + } + else + { + var gba = new VBANext(rom.RomData, nextComm); + nextEmulator = gba; + } break; case "N64": nextEmulator = new N64(nextComm, game, rom.RomData, diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 706879e63e..5755c00d99 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -238,7 +238,9 @@ + + diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibVBANext.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibVBANext.cs new file mode 100644 index 0000000000..781c19d40a --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibVBANext.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace BizHawk.Emulation.Cores.Nintendo.GBA +{ + public class LibVBANext + { + const string dllname = "libvbanext.dll"; + const CallingConvention cc = CallingConvention.Cdecl; + + [Flags] + public enum Buttons : int + { + A = 1, + B = 2, + Select = 4, + Start = 8, + Right = 16, + Left = 32, + Up = 64, + Down = 128, + R = 256, + L = 512 + } + + /// + /// create a new context + /// + /// + [DllImport(dllname, CallingConvention = cc)] + public static extern IntPtr Create(); + + /// + /// destroy a context + /// + /// + [DllImport(dllname, CallingConvention = cc)] + public static extern void Destroy(IntPtr g); + + /// + /// load a rom + /// + /// + /// + /// + /// + /// + /// success + [DllImport(dllname, CallingConvention = cc)] + public static extern bool LoadRom(IntPtr g, byte[] romfile, uint romfilelen, byte[] biosfile, uint biosfilelen); + + /// + /// hard reset + /// + /// + [DllImport(dllname, CallingConvention = cc)] + public static extern void Reset(IntPtr g); + + /// + /// frame advance + /// + /// + /// + /// 240x160 packed argb32 + [DllImport(dllname, CallingConvention = cc)] + public static extern void FrameAdvance(IntPtr g, Buttons input, int[] videobuffer); + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.cs new file mode 100644 index 0000000000..315b13fa67 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using System.IO; + +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBA +{ + [CoreAttributes("VBA-Next", "TODO", true, false, "cd508312a29ed8c29dacac1b11c2dce56c338a54", "https://github.com/libretro/vba-next")] + public class VBANext : IEmulator, IVideoProvider, ISyncSoundProvider + { + IntPtr Core; + + public VBANext(byte[] romfile, CoreComm nextComm) + { + CoreComm = nextComm; + byte[] biosfile = CoreComm.CoreFileProvider.GetFirmware("GBA", "Bios", true, "GBA bios file is mandatory."); + + if (romfile.Length > 32 * 1024 * 1024) + throw new ArgumentException("ROM is too big to be a GBA ROM!"); + if (biosfile.Length != 16 * 1024) + throw new ArgumentException("BIOS file is not exactly 16K!"); + + Core = LibVBANext.Create(); + if (Core == IntPtr.Zero) + throw new InvalidOperationException("Create() returned nullptr!"); + try + { + if (!LibVBANext.LoadRom(Core, romfile, (uint)romfile.Length, biosfile, (uint)biosfile.Length)) + throw new InvalidOperationException("LoadRom() returned false!"); + } + catch + { + Dispose(); + throw; + } + } + + + + + public void FrameAdvance(bool render, bool rendersound = true) + { + Frame++; + + if (Controller["Power"]) + LibVBANext.Reset(Core); + + IsLagFrame = false; // todo + LibVBANext.FrameAdvance(Core, GetButtons(), videobuff); + + if (IsLagFrame) + LagCount++; + } + + public int Frame { get; private set; } + public int LagCount { get; set; } + public bool IsLagFrame { get; private set; } + + public string SystemId { get { return "GBA"; } } + + public bool DeterministicEmulation { get { return true; } } + + public void ResetCounters() + { + Frame = 0; + LagCount = 0; + IsLagFrame = false; + } + + public string BoardName + { + // could we do something useful here? + get { return "BAMBOOZLED!"; } + } + + public CoreComm CoreComm { get; private set; } + + public void Dispose() + { + if (Core != IntPtr.Zero) + { + LibVBANext.Destroy(Core); + Core = IntPtr.Zero; + } + } + + #region SaveRam + + public byte[] ReadSaveRam() + { + return new byte[16]; + } + + public void StoreSaveRam(byte[] data) + { + } + + public void ClearSaveRam() + { + } + + public bool SaveRamModified + { + get + { + return false; + } + set + { + } + } + + #endregion + + #region SaveStates + + public void SaveStateText(TextWriter writer) + { + } + + public void LoadStateText(TextReader reader) + { + } + + public void SaveStateBinary(BinaryWriter writer) + { + } + + public void LoadStateBinary(BinaryReader reader) + { + } + + public byte[] SaveStateBinary() + { + return new byte[16]; + } + + public bool BinarySaveStatesPreferred + { + get { return true; } + } + + #endregion + + #region Debugging + + public MemoryDomainList MemoryDomains + { + get { return MemoryDomainList.GetDummyList(); } + } + + public Dictionary GetCpuFlagsAndRegisters() + { + throw new NotImplementedException(); + } + + public void SetCpuRegister(string register, int value) + { + throw new NotImplementedException(); + } + + #endregion + + #region Settings + + public object GetSettings() + { + return null; + } + + public object GetSyncSettings() + { + return null; + } + + public bool PutSettings(object o) + { + return false; + } + + public bool PutSyncSettings(object o) + { + return false; + } + + #endregion + + #region Controller + + public ControllerDefinition ControllerDefinition { get { return GBA.GBAController; } } + public IController Controller { get; set; } + + private LibVBANext.Buttons GetButtons() + { + LibVBANext.Buttons ret = 0; + foreach (string s in Enum.GetNames(typeof(LibVBANext.Buttons))) + { + if (Controller[s]) + ret |= (LibVBANext.Buttons)Enum.Parse(typeof(LibVBANext.Buttons), s); + } + return ret; + } + + #endregion + + #region VideoProvider + + int[] videobuff = new int[240 * 160]; + + public IVideoProvider VideoProvider { get { return this; } } + public int[] GetVideoBuffer() { return videobuff; } + public int VirtualWidth { get { return 240; } } + public int VirtualHeight { get { return 160; } } + public int BufferWidth { get { return 240; } } + public int BufferHeight { get { return 160; } } + public int BackgroundColor { get { return unchecked((int)0xff000000); } } + + #endregion + + #region SoundProvider + + short[] soundbuff = new short[2048]; + + public ISoundProvider SoundProvider { get { return null; } } + public ISyncSoundProvider SyncSoundProvider { get { return this; } } + public bool StartAsyncSound() { return false; } + public void EndAsyncSound() { } + + public void GetSamples(out short[] samples, out int nsamp) + { + // TODO + samples = soundbuff; + nsamp = 735; + } + + public void DiscardSamples() + { + } + + #endregion + } +} diff --git a/output/dll/libvbanext.dll b/output/dll/libvbanext.dll new file mode 100644 index 0000000000..a7a773b1f1 Binary files /dev/null and b/output/dll/libvbanext.dll differ diff --git a/vbanext/constarrays.h b/vbanext/constarrays.h new file mode 100644 index 0000000000..0d32b46194 --- /dev/null +++ b/vbanext/constarrays.h @@ -0,0 +1,54 @@ +#ifndef CONSTARRAYS_H +#define CONSTARRAYS_H + +static float const apu_vols [4] = { -0.25f, -0.5f, -1.0f, -0.25f }; + +static const int table [0x40] = +{ + 0xFF10, 0,0xFF11,0xFF12,0xFF13,0xFF14, 0, 0, + 0xFF16,0xFF17, 0, 0,0xFF18,0xFF19, 0, 0, + 0xFF1A, 0,0xFF1B,0xFF1C,0xFF1D,0xFF1E, 0, 0, + 0xFF20,0xFF21, 0, 0,0xFF22,0xFF23, 0, 0, + 0xFF24,0xFF25, 0, 0,0xFF26, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37, + 0xFF38,0xFF39,0xFF3A,0xFF3B,0xFF3C,0xFF3D,0xFF3E,0xFF3F, +}; + +static const uint32_t TIMER_TICKS[4] = {0, 6, 8, 10}; + +static const uint8_t gamepakRamWaitState[4] = { 4, 3, 2, 8 }; + +static const uint32_t objTilesAddress [3] = {0x010000, 0x014000, 0x014000}; + +static const u32 AlphaClampLUT[64] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F +}; + +static const u32 map_sizes_rot[] = { 128, 256, 512, 1024 }; + +static const u32 map_widths[] = { 256, 512, 256, 512 }; +static const u32 map_heights[] = { 256, 256, 512, 512 }; + + +static const int coeff[32] = {0,1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16}; + + +static const uint8_t memoryWait_init[16] = + { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0 }; +static const uint8_t memoryWaitSeq_init[16] = + { 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 4, 4, 8, 8, 4, 0 }; +static const uint8_t memoryWait32_init[16] = + { 0, 0, 5, 0, 0, 1, 1, 0, 7, 7, 9, 9, 13, 13, 4, 0 }; +static const uint8_t memoryWaitSeq32_init[16] = + { 0, 0, 5, 0, 0, 1, 1, 0, 5, 5, 9, 9, 17, 17, 4, 0 }; + + +#endif diff --git a/vbanext/instance.cpp b/vbanext/instance.cpp new file mode 100644 index 0000000000..a99f7245db --- /dev/null +++ b/vbanext/instance.cpp @@ -0,0 +1,13286 @@ +#include +#include +#include +#include + +#include +#include +#include + +#define LSB_FIRST +#ifdef SPEEDHAX +#error NO SPEEDHAX +#endif +#define HAVE_HLE_BIOS + +#include "port.h" + +#include "instance.h" + +#include "sound_blargg.h" + +#include "constarrays.h" + +#define INLINE + +class Gigazoid +{ + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// BEGIN MEMORY.CPP +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/*============================================================ + UTIL +============================================================ */ + +static int utilGetSize(int size) +{ + int res = 1; + + while(res < size) + res <<= 1; + + return res; +} + +/* Not endian safe, but VBA itself doesn't seem to care */ +void utilWriteIntMem(uint8_t *& data, int val) +{ + memcpy(data, &val, sizeof(int)); + data += sizeof(int); +} + +void utilWriteMem(uint8_t *& data, const void *in_data, unsigned size) +{ + memcpy(data, in_data, size); + data += size; +} + +int utilReadIntMem(const uint8_t *& data) +{ + int res; + + memcpy(&res, data, sizeof(int)); + data += sizeof(int); + return res; +} + +void utilReadMem(void *buf, const uint8_t *& data, unsigned size) +{ + memcpy(buf, data, size); + data += size; +} + +/*============================================================ + FLASH +============================================================ */ + + +#define FLASH_READ_ARRAY 0 +#define FLASH_CMD_1 1 +#define FLASH_CMD_2 2 +#define FLASH_AUTOSELECT 3 +#define FLASH_CMD_3 4 +#define FLASH_CMD_4 5 +#define FLASH_CMD_5 6 +#define FLASH_ERASE_COMPLETE 7 +#define FLASH_PROGRAM 8 +#define FLASH_SETBANK 9 + +#ifdef __LIBRETRO__ +extern uint8_t libretro_save_buf[0x20000 + 0x2000]; +uint8_t *flashSaveMemory = libretro_save_buf; +#else +uint8_t flashSaveMemory[FLASH_128K_SZ]; +#endif + +int flashState; // = FLASH_READ_ARRAY; +int flashReadState; // = FLASH_READ_ARRAY; +int flashSize; // = 0x10000; +int flashDeviceID; // = 0x1b; +int flashManufacturerID; // = 0x32; +int flashBank; // = 0; + +void flashInit (void) +{ +#ifdef __LIBRETRO__ + memset(flashSaveMemory, 0xff, 0x20000); +#else + memset(flashSaveMemory, 0xff, sizeof(flashSaveMemory)); +#endif +} + +void flashReset() +{ + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + flashBank = 0; +} + +void flashSetSize(int size) +{ + if(size == 0x10000) { + flashDeviceID = 0x1b; + flashManufacturerID = 0x32; + } else { + flashDeviceID = 0x13; //0x09; + flashManufacturerID = 0x62; //0xc2; + } + // Added to make 64k saves compatible with 128k ones + // (allow wrongfuly set 64k saves to work for Pokemon games) + if ((size == 0x20000) && (flashSize == 0x10000)) + memcpy((uint8_t *)(flashSaveMemory+0x10000), (uint8_t *)(flashSaveMemory), 0x10000); + flashSize = size; +} + +uint8_t flashRead(uint32_t address) +{ + address &= 0xFFFF; + + switch(flashReadState) { + case FLASH_READ_ARRAY: + return flashSaveMemory[(flashBank << 16) + address]; + case FLASH_AUTOSELECT: + switch(address & 0xFF) + { + case 0: + // manufacturer ID + return flashManufacturerID; + case 1: + // device ID + return flashDeviceID; + } + break; + case FLASH_ERASE_COMPLETE: + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + return 0xFF; + }; + return 0; +} + +void flashSaveDecide(uint32_t address, uint8_t byte) +{ + if(address == 0x0e005555) { + saveType = 2; + cpuSaveGameFunc = &Gigazoid::flashWrite; + } else { + saveType = 1; + cpuSaveGameFunc = &Gigazoid::sramWrite; + } + + (this->*cpuSaveGameFunc)(address, byte); +} + +void flashDelayedWrite(uint32_t address, uint8_t byte) +{ + saveType = 2; + cpuSaveGameFunc = &Gigazoid::flashWrite; + flashWrite(address, byte); +} + +void flashWrite(uint32_t address, uint8_t byte) +{ + address &= 0xFFFF; + switch(flashState) { + case FLASH_READ_ARRAY: + if(address == 0x5555 && byte == 0xAA) + flashState = FLASH_CMD_1; + break; + case FLASH_CMD_1: + if(address == 0x2AAA && byte == 0x55) + flashState = FLASH_CMD_2; + else + flashState = FLASH_READ_ARRAY; + break; + case FLASH_CMD_2: + if(address == 0x5555) { + if(byte == 0x90) { + flashState = FLASH_AUTOSELECT; + flashReadState = FLASH_AUTOSELECT; + } else if(byte == 0x80) { + flashState = FLASH_CMD_3; + } else if(byte == 0xF0) { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } else if(byte == 0xA0) { + flashState = FLASH_PROGRAM; + } else if(byte == 0xB0 && flashSize == 0x20000) { + flashState = FLASH_SETBANK; + } else { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + } else { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_CMD_3: + if(address == 0x5555 && byte == 0xAA) { + flashState = FLASH_CMD_4; + } else { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_CMD_4: + if(address == 0x2AAA && byte == 0x55) { + flashState = FLASH_CMD_5; + } else { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_CMD_5: + if(byte == 0x30) { + // SECTOR ERASE + memset(&flashSaveMemory[(flashBank << 16) + (address & 0xF000)], + 0, + 0x1000); + flashReadState = FLASH_ERASE_COMPLETE; + } else if(byte == 0x10) { + // CHIP ERASE + memset(flashSaveMemory, 0, flashSize); + flashReadState = FLASH_ERASE_COMPLETE; + } else { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_AUTOSELECT: + if(byte == 0xF0) { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } else if(address == 0x5555 && byte == 0xAA) + flashState = FLASH_CMD_1; + else { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_PROGRAM: + flashSaveMemory[(flashBank<<16)+address] = byte; + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + break; + case FLASH_SETBANK: + if(address == 0) { + flashBank = (byte & 1); + } + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + break; + } +} + +/*============================================================ + EEPROM +============================================================ */ +int eepromMode; // = EEPROM_IDLE; +int eepromByte; // = 0; +int eepromBits; // = 0; +int eepromAddress; // = 0; + +#ifdef __LIBRETRO__ +// Workaround for broken-by-design GBA save semantics. +extern u8 libretro_save_buf[0x20000 + 0x2000]; +u8 *eepromData = libretro_save_buf + 0x20000; +#else +u8 eepromData[0x2000]; +#endif + +u8 eepromBuffer[16]; +bool eepromInUse; // = false; +int eepromSize; // = 512; + +void eepromInit (void) +{ +#ifdef __LIBRETRO__ + memset(eepromData, 255, 0x2000); +#else + memset(eepromData, 255, sizeof(eepromData)); +#endif +} + +void eepromReset (void) +{ + eepromMode = EEPROM_IDLE; + eepromByte = 0; + eepromBits = 0; + eepromAddress = 0; + eepromInUse = false; + eepromSize = 512; +} + +int eepromRead (void) +{ + switch(eepromMode) + { + case EEPROM_IDLE: + case EEPROM_READADDRESS: + case EEPROM_WRITEDATA: + return 1; + case EEPROM_READDATA: + { + eepromBits++; + if(eepromBits == 4) { + eepromMode = EEPROM_READDATA2; + eepromBits = 0; + eepromByte = 0; + } + return 0; + } + case EEPROM_READDATA2: + { + int data = 0; + int address = eepromAddress << 3; + int mask = 1 << (7 - (eepromBits & 7)); + data = (eepromData[address+eepromByte] & mask) ? 1 : 0; + eepromBits++; + if((eepromBits & 7) == 0) + eepromByte++; + if(eepromBits == 0x40) + eepromMode = EEPROM_IDLE; + return data; + } + default: + return 0; + } + return 1; +} + +void eepromWrite(u8 value) +{ + if(cpuDmaCount == 0) + return; + int bit = value & 1; + switch(eepromMode) { + case EEPROM_IDLE: + eepromByte = 0; + eepromBits = 1; + eepromBuffer[eepromByte] = bit; + eepromMode = EEPROM_READADDRESS; + break; + case EEPROM_READADDRESS: + eepromBuffer[eepromByte] <<= 1; + eepromBuffer[eepromByte] |= bit; + eepromBits++; + if((eepromBits & 7) == 0) { + eepromByte++; + } + if(cpuDmaCount == 0x11 || cpuDmaCount == 0x51) { + if(eepromBits == 0x11) { + eepromInUse = true; + eepromSize = 0x2000; + eepromAddress = ((eepromBuffer[0] & 0x3F) << 8) | + ((eepromBuffer[1] & 0xFF)); + if(!(eepromBuffer[0] & 0x40)) { + eepromBuffer[0] = bit; + eepromBits = 1; + eepromByte = 0; + eepromMode = EEPROM_WRITEDATA; + } else { + eepromMode = EEPROM_READDATA; + eepromByte = 0; + eepromBits = 0; + } + } + } else { + if(eepromBits == 9) { + eepromInUse = true; + eepromAddress = (eepromBuffer[0] & 0x3F); + if(!(eepromBuffer[0] & 0x40)) { + eepromBuffer[0] = bit; + eepromBits = 1; + eepromByte = 0; + eepromMode = EEPROM_WRITEDATA; + } else { + eepromMode = EEPROM_READDATA; + eepromByte = 0; + eepromBits = 0; + } + } + } + break; + case EEPROM_READDATA: + case EEPROM_READDATA2: + // should we reset here? + eepromMode = EEPROM_IDLE; + break; + case EEPROM_WRITEDATA: + eepromBuffer[eepromByte] <<= 1; + eepromBuffer[eepromByte] |= bit; + eepromBits++; + if((eepromBits & 7) == 0) + eepromByte++; + if(eepromBits == 0x40) + { + eepromInUse = true; + // write data; + for(int i = 0; i < 8; i++) + eepromData[(eepromAddress << 3) + i] = eepromBuffer[i]; + } + else if(eepromBits == 0x41) + { + eepromMode = EEPROM_IDLE; + eepromByte = 0; + eepromBits = 0; + } + break; + } +} + +/*============================================================ + SRAM +============================================================ */ + +u8 sramRead(u32 address) +{ + return flashSaveMemory[address & 0xFFFF]; +} + +void sramDelayedWrite(u32 address, u8 byte) +{ + saveType = 1; + cpuSaveGameFunc = &Gigazoid::sramWrite; + sramWrite(address, byte); +} + +void sramWrite(u32 address, u8 byte) +{ + flashSaveMemory[address & 0xFFFF] = byte; +} + +/*============================================================ + RTC +============================================================ */ + +#define IDLE 0 +#define COMMAND 1 +#define DATA 2 +#define READDATA 3 + +typedef struct +{ + u8 byte0; + u8 byte1; + u8 byte2; + u8 command; + int dataLen; + int bits; + int state; + u8 data[12]; + // reserved variables for future + u8 reserved[12]; + bool reserved2; + u32 reserved3; +} RTCCLOCKDATA; + +RTCCLOCKDATA rtcClockData; +bool rtcEnabled; // = false; + +void rtcEnable(bool e) +{ + rtcEnabled = e; +} + +bool rtcIsEnabled (void) +{ + return rtcEnabled; +} + +u16 rtcRead(u32 address) +{ + if(rtcEnabled) + { + switch(address) + { + case 0x80000c8: + return rtcClockData.byte2; + case 0x80000c6: + return rtcClockData.byte1; + case 0x80000c4: + return rtcClockData.byte0; + } + } + + return READ16LE((&rom[address & 0x1FFFFFE])); +} + +static u8 toBCD(u8 value) +{ + value = value % 100; + int l = value % 10; + int h = value / 10; + return h * 16 + l; +} + +bool rtcWrite(u32 address, u16 value) +{ + if(!rtcEnabled) + return false; + + if(address == 0x80000c8) + rtcClockData.byte2 = (u8)value; // enable ? + else if(address == 0x80000c6) + rtcClockData.byte1 = (u8)value; // read/write + else if(address == 0x80000c4) + { + if(rtcClockData.byte2 & 1) + { + if(rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5) + { + rtcClockData.state = COMMAND; + rtcClockData.bits = 0; + rtcClockData.command = 0; + } + else if(!(rtcClockData.byte0 & 1) && (value & 1)) + { // bit transfer + rtcClockData.byte0 = (u8)value; + switch(rtcClockData.state) + { + case COMMAND: + rtcClockData.command |= ((value & 2) >> 1) << (7-rtcClockData.bits); + rtcClockData.bits++; + if(rtcClockData.bits == 8) + { + rtcClockData.bits = 0; + switch(rtcClockData.command) + { + case 0x60: + // not sure what this command does but it doesn't take parameters + // maybe it is a reset or stop + rtcClockData.state = IDLE; + rtcClockData.bits = 0; + break; + case 0x62: + // this sets the control state but not sure what those values are + rtcClockData.state = READDATA; + rtcClockData.dataLen = 1; + break; + case 0x63: + rtcClockData.dataLen = 1; + rtcClockData.data[0] = 0x40; + rtcClockData.state = DATA; + break; + case 0x64: + break; + case 0x65: + { + struct tm *newtime; + time_t long_time; + + time( &long_time ); /* Get time as long integer. */ + newtime = localtime( &long_time ); /* Convert to local time. */ + + rtcClockData.dataLen = 7; + rtcClockData.data[0] = toBCD(newtime->tm_year); + rtcClockData.data[1] = toBCD(newtime->tm_mon+1); + rtcClockData.data[2] = toBCD(newtime->tm_mday); + rtcClockData.data[3] = toBCD(newtime->tm_wday); + rtcClockData.data[4] = toBCD(newtime->tm_hour); + rtcClockData.data[5] = toBCD(newtime->tm_min); + rtcClockData.data[6] = toBCD(newtime->tm_sec); + rtcClockData.state = DATA; + } + break; + case 0x67: + { + struct tm *newtime; + time_t long_time; + + time( &long_time ); /* Get time as long integer. */ + newtime = localtime( &long_time ); /* Convert to local time. */ + + rtcClockData.dataLen = 3; + rtcClockData.data[0] = toBCD(newtime->tm_hour); + rtcClockData.data[1] = toBCD(newtime->tm_min); + rtcClockData.data[2] = toBCD(newtime->tm_sec); + rtcClockData.state = DATA; + } + break; + default: + systemMessage(0, "Unknown RTC command %02x", rtcClockData.command); + rtcClockData.state = IDLE; + break; + } + } + break; + case DATA: + if(rtcClockData.byte1 & 2) + { + } + else + { + rtcClockData.byte0 = (rtcClockData.byte0 & ~2) | + ((rtcClockData.data[rtcClockData.bits >> 3] >> + (rtcClockData.bits & 7)) & 1)*2; + rtcClockData.bits++; + if(rtcClockData.bits == 8*rtcClockData.dataLen) + { + rtcClockData.bits = 0; + rtcClockData.state = IDLE; + } + } + break; + case READDATA: + if(!(rtcClockData.byte1 & 2)) { + } else { + rtcClockData.data[rtcClockData.bits >> 3] = + (rtcClockData.data[rtcClockData.bits >> 3] >> 1) | + ((value << 6) & 128); + rtcClockData.bits++; + if(rtcClockData.bits == 8*rtcClockData.dataLen) { + rtcClockData.bits = 0; + rtcClockData.state = IDLE; + } + } + break; + default: + break; + } + } else + rtcClockData.byte0 = (u8)value; + } + } + return true; +} + +void rtcReset (void) +{ + memset(&rtcClockData, 0, sizeof(rtcClockData)); + + rtcClockData.byte0 = 0; + rtcClockData.byte1 = 0; + rtcClockData.byte2 = 0; + rtcClockData.command = 0; + rtcClockData.dataLen = 0; + rtcClockData.bits = 0; + rtcClockData.state = IDLE; +} + +void rtcSaveGameMem(uint8_t *& data) +{ + utilWriteMem(data, &rtcClockData, sizeof(rtcClockData)); +} + +void rtcReadGameMem(const uint8_t *& data) +{ + utilReadMem(&rtcClockData, data, sizeof(rtcClockData)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// END MEMORY.CPP +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// BEGIN SOUND.CPP +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define NR10 0x60 +#define NR11 0x62 +#define NR12 0x63 +#define NR13 0x64 +#define NR14 0x65 +#define NR21 0x68 +#define NR22 0x69 +#define NR23 0x6c +#define NR24 0x6d +#define NR30 0x70 +#define NR31 0x72 +#define NR32 0x73 +#define NR33 0x74 +#define NR34 0x75 +#define NR41 0x78 +#define NR42 0x79 +#define NR43 0x7c +#define NR44 0x7d +#define NR50 0x80 +#define NR51 0x81 +#define NR52 0x84 + +/* 1/100th of a second */ +#define SOUND_CLOCK_TICKS_ 167772 +#define SOUNDVOLUME 0.5f +#define SOUNDVOLUME_ -1 + +/*============================================================ + CLASS DECLS +============================================================ */ + +class Blip_Buffer +{ + public: + Blip_Buffer::Blip_Buffer() + { + factor_ = INT_MAX; + buffer_ = 0; + buffer_size_ = 0; + sample_rate_ = 0; + clock_rate_ = 0; + length_ = 0; + + clear(); + } + + Blip_Buffer::~Blip_Buffer() + { + if (buffer_) + free(buffer_); + } + + void Blip_Buffer::clear( void) + { + offset_ = 0; + reader_accum_ = 0; + if (buffer_) + memset( buffer_, 0, (buffer_size_ + BLIP_BUFFER_EXTRA_) * sizeof (int32_t) ); + } + + const char * Blip_Buffer::set_sample_rate( long new_rate, int msec ) + { + /* start with maximum length that resampled time can represent*/ + long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - BLIP_BUFFER_EXTRA_ - 64; + if ( msec != 0) + { + long s = (new_rate * (msec + 1) + 999) / 1000; + if ( s < new_size ) + new_size = s; + } + + if ( buffer_size_ != new_size ) + { + void* p = realloc( buffer_, (new_size + BLIP_BUFFER_EXTRA_) * sizeof *buffer_ ); + if ( !p ) + return "Out of memory"; + buffer_ = (int32_t *) p; + } + + buffer_size_ = new_size; + + /* update things based on the sample rate*/ + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + + /* update these since they depend on sample rate*/ + if ( clock_rate_ ) + factor_ = clock_rate_factor( clock_rate_); + + clear(); + + return 0; + } + + /* Sets number of source time units per second */ + + uint32_t Blip_Buffer::clock_rate_factor( long rate ) const + { + double ratio = (double) sample_rate_ / rate; + int32_t factor = (int32_t) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); + return (uint32_t) factor; + } + long clock_rate_; + int length_; /* Length of buffer in milliseconds*/ + long sample_rate_; /* Current output sample rate*/ + uint32_t factor_; + uint32_t offset_; + int32_t * buffer_; + int32_t buffer_size_; + int32_t reader_accum_; + private: + Blip_Buffer( const Blip_Buffer& ); + Blip_Buffer& operator = ( const Blip_Buffer& ); +}; + +class Blip_Synth +{ + public: + Blip_Buffer* buf; + int delta_factor; + + Blip_Synth() + { + } + + void volume( double v ) { delta_factor = int ((v * 1.0) * (1L << BLIP_SAMPLE_BITS) + 0.5); } + INLINE void Blip_Synth::offset_resampled( uint32_t time, int delta, Blip_Buffer* blip_buf ) const + { + int32_t left, right, phase; + int32_t *buf; + + delta *= delta_factor; + buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); + phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & BLIP_RES_MIN_ONE); + + left = buf [0] + delta; + + right = (delta >> BLIP_PHASE_BITS) * phase; + + left -= right; + right += buf [1]; + + buf [0] = left; + buf [1] = right; + } + + INLINE void Blip_Synth::offset( int32_t t, int delta, Blip_Buffer* buf ) const + { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } + void offset_inline( int32_t t, int delta, Blip_Buffer* buf ) const { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } + void offset_inline( int32_t t, int delta ) const { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } +}; + +#define TRIGGER_MASK 0x80 +#define LENGTH_ENABLED 0x40 + +#define VOLUME_SHIFT_PLUS_FOUR 6 +#define SIZE20_MASK 0x20 + + + +#define reload_sweep_timer() \ + sweep_delay = (regs [0] & PERIOD_MASK) >> 4; \ + if ( !sweep_delay ) \ + sweep_delay = 8; + +class Gb_Osc +{ + public: + Blip_Buffer* outputs [4]; /* NULL, right, left, center*/ + Blip_Buffer* output; /* where to output sound*/ + uint8_t * regs; /* osc's 5 registers*/ + int mode; /* mode_dmg, mode_cgb, mode_agb*/ + int dac_off_amp; /* amplitude when DAC is off*/ + int last_amp; /* current amplitude in Blip_Buffer*/ + Blip_Synth const* good_synth; + Blip_Synth const* med_synth; + + int delay; /* clocks until frequency timer expires*/ + int length_ctr; /* length counter*/ + unsigned phase; /* waveform phase (or equivalent)*/ + bool enabled; /* internal enabled flag*/ + + void Gb_Osc::clock_length() + { + if ( (regs [4] & LENGTH_ENABLED) && length_ctr ) + { + if ( --length_ctr <= 0 ) + enabled = false; + } + } + void Gb_Osc::reset() + { + output = 0; + last_amp = 0; + delay = 0; + phase = 0; + enabled = false; + } + protected: + INLINE void Gb_Osc::update_amp( int32_t time, int new_amp ) + { + int delta = new_amp - last_amp; + if ( delta ) + { + last_amp = new_amp; + med_synth->offset( time, delta, output ); + } + } + int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data ) + { + int data = regs [4]; + + if ( (frame_phase & 1) && !(old_data & LENGTH_ENABLED) && length_ctr ) + { + if ( (data & LENGTH_ENABLED)) + length_ctr--; + } + + if ( data & TRIGGER_MASK ) + { + enabled = true; + if ( !length_ctr ) + { + length_ctr = max_len; + if ( (frame_phase & 1) && (data & LENGTH_ENABLED) ) + length_ctr--; + } + } + + if ( !length_ctr ) + enabled = false; + + return data & TRIGGER_MASK; + } +}; + +class Gb_Env : public Gb_Osc +{ + public: + int env_delay; + int volume; + bool env_enabled; + + void Gb_Env::clock_envelope() + { + if ( env_enabled && --env_delay <= 0 && reload_env_timer() ) + { + int v = volume + (regs [2] & 0x08 ? +1 : -1); + if ( 0 <= v && v <= 15 ) + volume = v; + else + env_enabled = false; + } + } + bool Gb_Env::write_register( int frame_phase, int reg, int old, int data ) + { + int const max_len = 64; + + switch ( reg ) + { + case 1: + length_ctr = max_len - (data & (max_len - 1)); + break; + + case 2: + if ( !GB_ENV_DAC_ENABLED() ) + enabled = false; + + zombie_volume( old, data ); + + if ( (data & 7) && env_delay == 8 ) + { + env_delay = 1; + clock_envelope(); // TODO: really happens at next length clock + } + break; + + case 4: + if ( write_trig( frame_phase, max_len, old ) ) + { + volume = regs [2] >> 4; + reload_env_timer(); + env_enabled = true; + if ( frame_phase == 7 ) + env_delay++; + if ( !GB_ENV_DAC_ENABLED() ) + enabled = false; + return true; + } + } + return false; + } + + void reset() + { + env_delay = 0; + volume = 0; + Gb_Osc::reset(); + } + private: + INLINE void Gb_Env::zombie_volume( int old, int data ) + { + int v = volume; + + // CGB-05 behavior, very close to AGB behavior as well + if ( (old ^ data) & 8 ) + { + if ( !(old & 8) ) + { + v++; + if ( old & 7 ) + v++; + } + + v = 16 - v; + } + else if ( (old & 0x0F) == 8 ) + v++; + volume = v & 0x0F; + } + INLINE int Gb_Env::reload_env_timer() + { + int raw = regs [2] & 7; + env_delay = (raw ? raw : 8); + return raw; + } +}; + +class Gb_Square : public Gb_Env +{ + public: + bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data ) + { + bool result = Gb_Env::write_register( frame_phase, reg, old_data, data ); + if ( result ) + delay = (delay & (CLK_MUL_MUL_4 - 1)) + period(); + return result; + } + void Gb_Square::run( int32_t time, int32_t end_time ) + { + /* Calc duty and phase*/ + static unsigned char const duty_offsets [4] = { 1, 1, 3, 7 }; + static unsigned char const duties [4] = { 1, 2, 4, 6 }; + int const duty_code = regs [1] >> 6; + int32_t duty_offset = duty_offsets [duty_code]; + int32_t duty = duties [duty_code]; + /* AGB uses inverted duty*/ + duty_offset -= duty; + duty = 8 - duty; + int ph = (phase + duty_offset) & 7; + + /* Determine what will be generated*/ + int vol = 0; + Blip_Buffer* const out = output; + if ( out ) + { + int amp = dac_off_amp; + if ( GB_ENV_DAC_ENABLED() ) + { + if ( enabled ) + vol = volume; + + amp = -(vol >> 1); + + /* Play inaudible frequencies as constant amplitude*/ + if ( GB_OSC_FREQUENCY() >= 0x7FA && delay < CLK_MUL_MUL_32 ) + { + amp += (vol * duty) >> 3; + vol = 0; + } + + if ( ph < duty ) + { + amp += vol; + vol = -vol; + } + } + update_amp( time, amp ); + } + + /* Generate wave*/ + time += delay; + if ( time < end_time ) + { + int const per = period(); + if ( !vol ) + { + /* Maintain phase when not playing*/ + int count = (end_time - time + per - 1) / per; + ph += count; /* will be masked below*/ + time += (int32_t) count * per; + } + else + { + /* Output amplitude transitions*/ + int delta = vol; + do + { + ph = (ph + 1) & 7; + if ( ph == 0 || ph == duty ) + { + good_synth->offset_inline( time, delta, out ); + delta = -delta; + } + time += per; + } + while ( time < end_time ); + + if ( delta != vol ) + last_amp -= delta; + } + phase = (ph - duty_offset) & 7; + } + delay = time - end_time; + } + + void reset() + { + Gb_Env::reset(); + delay = 0x40000000; /* TODO: something less hacky (never clocked until first trigger)*/ + } + private: + /* Frequency timer period*/ + int period() const { return (2048 - GB_OSC_FREQUENCY()) * (CLK_MUL_MUL_4); } +}; + +class Gb_Sweep_Square : public Gb_Square +{ + public: + int sweep_freq; + int sweep_delay; + bool sweep_enabled; + bool sweep_neg; + + void Gb_Sweep_Square::clock_sweep() + { + if ( --sweep_delay <= 0 ) + { + reload_sweep_timer(); + if ( sweep_enabled && (regs [0] & PERIOD_MASK) ) + { + calc_sweep( true ); + calc_sweep( false ); + } + } + } + INLINE void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data ) + { + if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) ) + enabled = false; // sweep negate disabled after used + + if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) ) + { + sweep_freq = GB_OSC_FREQUENCY(); + sweep_neg = false; + reload_sweep_timer(); + sweep_enabled = (regs [0] & (PERIOD_MASK | SHIFT_MASK)) != 0; + if ( regs [0] & SHIFT_MASK ) + calc_sweep( false ); + } + } + + void reset() + { + sweep_freq = 0; + sweep_delay = 0; + sweep_enabled = false; + sweep_neg = false; + Gb_Square::reset(); + } + private: + void Gb_Sweep_Square::calc_sweep( bool update ) + { + int shift, delta, freq; + + shift = regs [0] & SHIFT_MASK; + delta = sweep_freq >> shift; + sweep_neg = (regs [0] & 0x08) != 0; + freq = sweep_freq + (sweep_neg ? -delta : delta); + + if ( freq > 0x7FF ) + enabled = false; + else if ( shift && update ) + { + sweep_freq = freq; + + regs [3] = freq & 0xFF; + regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07); + } + } +}; + +class Gb_Noise : public Gb_Env +{ + public: + int divider; /* noise has more complex frequency divider setup*/ + + /* Quickly runs LFSR for a large number of clocks. For use when noise is generating*/ + /* no sound.*/ + unsigned run_lfsr( unsigned s, unsigned mask, int count ) + { + /* optimization used in several places:*/ + /* ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n)*/ + + if ( mask == 0x4000 ) + { + if ( count >= 32767 ) + count %= 32767; + + /* Convert from Fibonacci to Galois configuration,*/ + /* shifted left 1 bit*/ + s ^= (s & 1) * 0x8000; + + /* Each iteration is equivalent to clocking LFSR 255 times*/ + while ( (count -= 255) > 0 ) + s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3); + count += 255; + + /* Each iteration is equivalent to clocking LFSR 15 times*/ + /* (interesting similarity to single clocking below)*/ + while ( (count -= 15) > 0 ) + s ^= ((s & 2) * (3 << 13)) ^ (s >> 1); + count += 15; + + /* Remaining singles*/ + do{ + --count; + s = ((s & 2) * (3 << 13)) ^ (s >> 1); + }while(count >= 0); + + /* Convert back to Fibonacci configuration*/ + s &= 0x7FFF; + } + else if ( count < 8) + { + /* won't fully replace upper 8 bits, so have to do the unoptimized way*/ + do{ + --count; + s = (s >> 1 | mask) ^ (mask & -((s - 1) & 2)); + }while(count >= 0); + } + else + { + if ( count > 127 ) + { + count %= 127; + if ( !count ) + count = 127; /* must run at least once*/ + } + + /* Need to keep one extra bit of history*/ + s = s << 1 & 0xFF; + + /* Convert from Fibonacci to Galois configuration,*/ + /* shifted left 2 bits*/ + s ^= (s & 2) << 7; + + /* Each iteration is equivalent to clocking LFSR 7 times*/ + /* (interesting similarity to single clocking below)*/ + while ( (count -= 7) > 0 ) + s ^= ((s & 4) * (3 << 5)) ^ (s >> 1); + count += 7; + + /* Remaining singles*/ + while ( --count >= 0 ) + s = ((s & 4) * (3 << 5)) ^ (s >> 1); + + /* Convert back to Fibonacci configuration and*/ + /* repeat last 8 bits above significant 7*/ + s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F); + } + + return s; + } + + void Gb_Noise::run( int32_t time, int32_t end_time ) + { + /* Determine what will be generated*/ + int vol = 0; + Blip_Buffer* const out = output; + if ( out ) + { + int amp = dac_off_amp; + if ( GB_ENV_DAC_ENABLED() ) + { + if ( enabled ) + vol = volume; + + amp = -(vol >> 1); + + if ( !(phase & 1) ) + { + amp += vol; + vol = -vol; + } + } + + /* AGB negates final output*/ + vol = -vol; + amp = -amp; + + update_amp( time, amp ); + } + + /* Run timer and calculate time of next LFSR clock*/ + static unsigned char const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 }; + int const period1 = period1s [regs [3] & 7] * CLK_MUL; + { + int extra = (end_time - time) - delay; + int const per2 = GB_NOISE_PERIOD2(8); + time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1; + + int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1); + divider = (divider - count) & PERIOD2_MASK; + delay = count * period1 - extra; + } + + /* Generate wave*/ + if ( time < end_time ) + { + unsigned const mask = GB_NOISE_LFSR_MASK(); + unsigned bits = phase; + + int per = GB_NOISE_PERIOD2( period1 * 8 ); + if ( GB_NOISE_PERIOD2_INDEX() >= 0xE ) + { + time = end_time; + } + else if ( !vol ) + { + /* Maintain phase when not playing*/ + int count = (end_time - time + per - 1) / per; + time += (int32_t) count * per; + bits = run_lfsr( bits, ~mask, count ); + } + else + { + /* Output amplitude transitions*/ + int delta = -vol; + do + { + unsigned changed = bits + 1; + bits = bits >> 1 & mask; + if ( changed & 2 ) + { + bits |= ~mask; + delta = -delta; + med_synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + + if ( delta == vol ) + last_amp += delta; + } + phase = bits; + } + } + INLINE void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data ) + { + if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) ) + { + phase = 0x7FFF; + delay += CLK_MUL_MUL_8; + } + } + + void reset() + { + divider = 0; + Gb_Env::reset(); + delay = CLK_MUL_MUL_4; /* TODO: remove?*/ + } +}; + +class Gb_Wave : public Gb_Osc +{ + public: + int sample_buf; /* last wave RAM byte read (hardware has this as well)*/ + int agb_mask; /* 0xFF if AGB features enabled, 0 otherwise*/ + uint8_t* wave_ram; /* 32 bytes (64 nybbles), stored in APU*/ + + INLINE void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data ) + { + switch ( reg ) + { + case 0: + if ( !GB_WAVE_DAC_ENABLED() ) + enabled = false; + break; + + case 1: + length_ctr = 256 - data; + break; + + case 4: + bool was_enabled = enabled; + if ( write_trig( frame_phase, 256, old_data ) ) + { + if ( !GB_WAVE_DAC_ENABLED() ) + enabled = false; + phase = 0; + delay = period() + CLK_MUL_MUL_6; + } + } + } + void Gb_Wave::run( int32_t time, int32_t end_time ) + { + /* Calc volume*/ + static unsigned char const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; + int const volume_idx = regs [2] >> 5 & (agb_mask | 3); /* 2 bits on DMG/CGB, 3 on AGB*/ + int const volume_mul = volumes [volume_idx]; + + /* Determine what will be generated*/ + int playing = false; + Blip_Buffer* const out = output; + if ( out ) + { + int amp = dac_off_amp; + if ( GB_WAVE_DAC_ENABLED() ) + { + /* Play inaudible frequencies as constant amplitude*/ + amp = 128; /* really depends on average of all samples in wave*/ + + /* if delay is larger, constant amplitude won't start yet*/ + if ( GB_OSC_FREQUENCY() <= 0x7FB || delay > CLK_MUL_MUL_15 ) + { + if ( volume_mul ) + playing = (int) enabled; + + amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing; + } + + amp = ((amp * volume_mul) >> VOLUME_SHIFT_PLUS_FOUR) - DAC_BIAS; + } + update_amp( time, amp ); + } + + /* Generate wave*/ + time += delay; + if ( time < end_time ) + { + unsigned char const* wave = wave_ram; + + /* wave size and bank*/ + int const flags = regs [0] & agb_mask; + int const wave_mask = (flags & SIZE20_MASK) | 0x1F; + int swap_banks = 0; + if ( flags & BANK40_MASK) + { + swap_banks = flags & SIZE20_MASK; + wave += BANK_SIZE_DIV_TWO - (swap_banks >> 1); + } + + int ph = phase ^ swap_banks; + ph = (ph + 1) & wave_mask; /* pre-advance*/ + + int const per = period(); + if ( !playing ) + { + /* Maintain phase when not playing*/ + int count = (end_time - time + per - 1) / per; + ph += count; /* will be masked below*/ + time += (int32_t) count * per; + } + else + { + /* Output amplitude transitions*/ + int lamp = last_amp + DAC_BIAS; + do + { + /* Extract nybble*/ + int nybble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0; + ph = (ph + 1) & wave_mask; + + /* Scale by volume*/ + int amp = (nybble * volume_mul) >> VOLUME_SHIFT_PLUS_FOUR; + + int delta = amp - lamp; + if ( delta ) + { + lamp = amp; + med_synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + last_amp = lamp - DAC_BIAS; + } + ph = (ph - 1) & wave_mask; /* undo pre-advance and mask position*/ + + /* Keep track of last byte read*/ + if ( enabled ) + sample_buf = wave [ph >> 1]; + + phase = ph ^ swap_banks; /* undo swapped banks*/ + } + delay = time - end_time; + } + + /* Reads/writes wave RAM*/ + INLINE int Gb_Wave::read( unsigned addr ) const + { + int index; + + if(enabled) + index = access( addr ); + else + index = addr & 0x0F; + + unsigned char const * wave_bank = &wave_ram[(~regs[0] & BANK40_MASK) >> 2 & agb_mask]; + + return (index < 0 ? 0xFF : wave_bank[index]); + } + + INLINE void Gb_Wave::write( unsigned addr, int data ) + { + int index; + + if(enabled) + index = access( addr ); + else + index = addr & 0x0F; + + unsigned char * wave_bank = &wave_ram[(~regs[0] & BANK40_MASK) >> 2 & agb_mask]; + + if ( index >= 0 ) + wave_bank[index] = data;; + } + + + void reset() + { + sample_buf = 0; + Gb_Osc::reset(); + } + + private: + friend class Gb_Apu; + + /* Frequency timer period*/ + int period() const { return (2048 - GB_OSC_FREQUENCY()) * (CLK_MUL_MUL_2); } + + void Gb_Wave::corrupt_wave() + { + int pos = ((phase + 1) & BANK_SIZE_MIN_ONE) >> 1; + if ( pos < 4 ) + wave_ram [0] = wave_ram [pos]; + else + for ( int i = 4; --i >= 0; ) + wave_ram [i] = wave_ram [(pos & ~3) + i]; + } + + /* Wave index that would be accessed, or -1 if no access would occur*/ + int Gb_Wave::access( unsigned addr ) const + { + addr = (phase & BANK_SIZE_MIN_ONE) >> 1; + return addr & 0x0F; + } +}; + +/*============================================================ + INLINE CLASS FUNCS +============================================================ */ + +int16_t soundFinalWave [1600]; +static const long soundSampleRate = 44100; // = 22050; +int SOUND_CLOCK_TICKS; // = SOUND_CLOCK_TICKS_; +int soundTicks; // = SOUND_CLOCK_TICKS_; + +int soundEnableFlag; // = 0x3ff; /* emulator channels enabled*/ + +typedef struct +{ + int last_amp; + int last_time; + int shift; + Blip_Buffer* output; +} gba_pcm_t; + +typedef struct +{ + bool enabled; + uint8_t fifo [32]; + int count; + int dac; + int readIndex; + int writeIndex; + int which; + int timer; + gba_pcm_t pcm; +} gba_pcm_fifo_t; + +gba_pcm_fifo_t pcm [2]; + + +Blip_Synth pcm_synth; // 32 kHz, 16 kHz, 8 kHz + +Blip_Buffer bufs_buffer [BUFS_SIZE]; +int mixer_samples_read; + +void gba_pcm_init (void) +{ + pcm[0].pcm.output = 0; + pcm[0].pcm.last_time = 0; + pcm[0].pcm.last_amp = 0; + pcm[0].pcm.shift = 0; + + pcm[1].pcm.output = 0; + pcm[1].pcm.last_time = 0; + pcm[1].pcm.last_amp = 0; + pcm[1].pcm.shift = 0; +} + +void gba_pcm_apply_control( int pcm_idx, int idx ) +{ + int ch = 0; + pcm[pcm_idx].pcm.shift = ~ioMem [SGCNT0_H] >> (2 + idx) & 1; + + if ( (ioMem [NR52] & 0x80) ) + ch = ioMem [SGCNT0_H+1] >> (idx << 2) & 3; + + Blip_Buffer* out = 0; + switch ( ch ) + { + case 1: + out = &bufs_buffer[1]; + break; + case 2: + out = &bufs_buffer[0]; + break; + case 3: + out = &bufs_buffer[2]; + break; + } + + if ( pcm[pcm_idx].pcm.output != out ) + { + if ( pcm[pcm_idx].pcm.output ) + pcm_synth.offset( SOUND_CLOCK_TICKS - soundTicks, -pcm[pcm_idx].pcm.last_amp, pcm[pcm_idx].pcm.output ); + pcm[pcm_idx].pcm.last_amp = 0; + pcm[pcm_idx].pcm.output = out; + } +} + +/*============================================================ + GB APU +============================================================ */ + +/* 0: Square 1, 1: Square 2, 2: Wave, 3: Noise */ +#define OSC_COUNT 4 + +/* Resets hardware to initial power on state BEFORE boot ROM runs. Mode selects*/ +/* sound hardware. Additional AGB wave features are enabled separately.*/ +#define MODE_AGB 2 + +#define START_ADDR 0xFF10 +#define END_ADDR 0xFF3F + +/* Reads and writes must be within the START_ADDR to END_ADDR range, inclusive.*/ +/* Addresses outside this range are not mapped to the sound hardware.*/ +#define REGISTER_COUNT 48 +#define REGS_SIZE 64 + +/* Clock rate that sound hardware runs at. + * formula: 4194304 * 4 + * */ +#define CLOCK_RATE 16777216 + +struct gb_apu_t +{ + bool reduce_clicks_; + uint8_t regs[REGS_SIZE]; // last values written to registers + int32_t last_time; // time sound emulator has been run to + int32_t frame_time; // time of next frame sequencer action + int32_t frame_period; // clocks between each frame sequencer step + int32_t frame_phase; // phase of next frame sequencer step + double volume_; + Gb_Osc* oscs [OSC_COUNT]; + Gb_Sweep_Square square1; + Gb_Square square2; + Gb_Wave wave; + Gb_Noise noise; + Blip_Synth good_synth; + Blip_Synth med_synth; +} gb_apu; + +// Format of save state. Should be stable across versions of the library, +// with earlier versions properly opening later save states. Includes some +// room for expansion so the state size shouldn't increase. +struct gb_apu_state_t +{ + // Values stored as plain int so your code can read/write them easily. + // Structure can NOT be written to disk, since format is not portable. + typedef int val_t; + + enum { format0 = 0x50414247 }; + + val_t format; // format of all following data + val_t version; // later versions just add fields to end + + unsigned char regs [0x40]; + val_t frame_time; + val_t frame_phase; + + val_t sweep_freq; + val_t sweep_delay; + val_t sweep_enabled; + val_t sweep_neg; + val_t noise_divider; + val_t wave_buf; + + val_t delay [4]; + val_t length_ctr [4]; + val_t phase [4]; + val_t enabled [4]; + + val_t env_delay [3]; + val_t env_volume [3]; + val_t env_enabled [3]; + + val_t unused [13]; // for future expansion +}; + +#define VOL_REG 0xFF24 +#define STEREO_REG 0xFF25 +#define STATUS_REG 0xFF26 +#define WAVE_RAM 0xFF30 +#define POWER_MASK 0x80 + +#define OSC_COUNT 4 + +void gb_apu_reduce_clicks( bool reduce ) +{ + gb_apu.reduce_clicks_ = reduce; + + /* Click reduction makes DAC off generate same output as volume 0*/ + int dac_off_amp = 0; + + gb_apu.oscs[0]->dac_off_amp = dac_off_amp; + gb_apu.oscs[1]->dac_off_amp = dac_off_amp; + gb_apu.oscs[2]->dac_off_amp = dac_off_amp; + gb_apu.oscs[3]->dac_off_amp = dac_off_amp; + + /* AGB always eliminates clicks on wave channel using same method*/ + gb_apu.wave.dac_off_amp = -DAC_BIAS; +} + +void gb_apu_synth_volume( int iv ) +{ + double v = gb_apu.volume_ * 0.60 / OSC_COUNT / 15 /*steps*/ / 8 /*master vol range*/ * iv; + gb_apu.good_synth.volume( v ); + gb_apu.med_synth .volume( v ); +} + +void gb_apu_apply_volume (void) +{ + int data, left, right, vol_tmp; + data = gb_apu.regs [VOL_REG - START_ADDR]; + left = data >> 4 & 7; + right = data & 7; + vol_tmp = left < right ? right : left; + gb_apu_synth_volume( vol_tmp + 1 ); +} + +void gb_apu_silence_osc( Gb_Osc& o ) +{ + int delta; + + delta = -o.last_amp; + if ( delta ) + { + o.last_amp = 0; + if ( o.output ) + { + gb_apu.med_synth.offset( gb_apu.last_time, delta, o.output ); + } + } +} + +void gb_apu_run_until_( int32_t end_time ) +{ + int32_t time; + + do{ + /* run oscillators*/ + time = end_time; + if ( time > gb_apu.frame_time ) + time = gb_apu.frame_time; + + gb_apu.square1.run( gb_apu.last_time, time ); + gb_apu.square2.run( gb_apu.last_time, time ); + gb_apu.wave .run( gb_apu.last_time, time ); + gb_apu.noise .run( gb_apu.last_time, time ); + gb_apu.last_time = time; + + if ( time == end_time ) + break; + + /* run frame sequencer*/ + gb_apu.frame_time += gb_apu.frame_period * CLK_MUL; + switch ( gb_apu.frame_phase++ ) + { + case 2: + case 6: + /* 128 Hz*/ + gb_apu.square1.clock_sweep(); + case 0: + case 4: + /* 256 Hz*/ + gb_apu.square1.clock_length(); + gb_apu.square2.clock_length(); + gb_apu.wave .clock_length(); + gb_apu.noise .clock_length(); + break; + + case 7: + /* 64 Hz*/ + gb_apu.frame_phase = 0; + gb_apu.square1.clock_envelope(); + gb_apu.square2.clock_envelope(); + gb_apu.noise .clock_envelope(); + } + }while(1); +} + +void gb_apu_write_osc( int index, int reg, int old_data, int data ) +{ + reg -= index * 5; + switch ( index ) + { + case 0: + gb_apu.square1.write_register( gb_apu.frame_phase, reg, old_data, data ); + break; + case 1: + gb_apu.square2.write_register( gb_apu.frame_phase, reg, old_data, data ); + break; + case 2: + gb_apu.wave.write_register( gb_apu.frame_phase, reg, old_data, data ); + break; + case 3: + gb_apu.noise.write_register( gb_apu.frame_phase, reg, old_data, data ); + break; + } +} + +INLINE int gb_apu_calc_output( int osc ) +{ + int bits = gb_apu.regs [STEREO_REG - START_ADDR] >> osc; + return (bits >> 3 & 2) | (bits & 1); +} + +void gb_apu_write_register( int32_t time, unsigned addr, int data ) +{ + int reg = addr - START_ADDR; + if ( (unsigned) reg >= REGISTER_COUNT ) + return; + + if ( addr < STATUS_REG && !(gb_apu.regs [STATUS_REG - START_ADDR] & POWER_MASK) ) + return; /* Power is off*/ + + if ( time > gb_apu.last_time ) + gb_apu_run_until_( time ); + + if ( addr >= WAVE_RAM ) + { + gb_apu.wave.write( addr, data ); + } + else + { + int old_data = gb_apu.regs [reg]; + gb_apu.regs [reg] = data; + + if ( addr < VOL_REG ) + gb_apu_write_osc( reg / 5, reg, old_data, data ); /* Oscillator*/ + else if ( addr == VOL_REG && data != old_data ) + { + /* Master volume*/ + for ( int i = OSC_COUNT; --i >= 0; ) + gb_apu_silence_osc( *gb_apu.oscs [i] ); + + gb_apu_apply_volume(); + } + else if ( addr == STEREO_REG ) + { + /* Stereo panning*/ + for ( int i = OSC_COUNT; --i >= 0; ) + { + Gb_Osc& o = *gb_apu.oscs [i]; + Blip_Buffer* out = o.outputs [gb_apu_calc_output( i )]; + if ( o.output != out ) + { + gb_apu_silence_osc( o ); + o.output = out; + } + } } + else if ( addr == STATUS_REG && (data ^ old_data) & POWER_MASK ) + { + /* Power control*/ + gb_apu.frame_phase = 0; + for ( int i = OSC_COUNT; --i >= 0; ) + gb_apu_silence_osc( *gb_apu.oscs [i] ); + + for ( int i = 0; i < 32; i++ ) + gb_apu.regs [i] = 0; + + gb_apu.square1.reset(); + gb_apu.square2.reset(); + gb_apu.wave .reset(); + gb_apu.noise .reset(); + + gb_apu_apply_volume(); + + gb_apu.square1.length_ctr = 64; + gb_apu.square2.length_ctr = 64; + gb_apu.wave .length_ctr = 256; + gb_apu.noise .length_ctr = 64; + + gb_apu.regs [STATUS_REG - START_ADDR] = data; + } + } +} + +void gb_apu_reset( uint32_t mode, bool agb_wave ) +{ + /* Hardware mode*/ + mode = MODE_AGB; /* using AGB wave features implies AGB hardware*/ + gb_apu.wave.agb_mask = 0xFF; + gb_apu.oscs [0]->mode = mode; + gb_apu.oscs [1]->mode = mode; + gb_apu.oscs [2]->mode = mode; + gb_apu.oscs [3]->mode = mode; + gb_apu_reduce_clicks( gb_apu.reduce_clicks_ ); + + /* Reset state*/ + gb_apu.frame_time = 0; + gb_apu.last_time = 0; + gb_apu.frame_phase = 0; + + for ( int i = 0; i < 32; i++ ) + gb_apu.regs [i] = 0; + + gb_apu.square1.reset(); + gb_apu.square2.reset(); + gb_apu.wave .reset(); + gb_apu.noise .reset(); + + gb_apu_apply_volume(); + + gb_apu.square1.length_ctr = 64; + gb_apu.square2.length_ctr = 64; + gb_apu.wave .length_ctr = 256; + gb_apu.noise .length_ctr = 64; + + /* Load initial wave RAM*/ + static unsigned char const initial_wave [2] [16] = { + {0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA}, + {0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF}, + }; + for ( int b = 2; --b >= 0; ) + { + /* Init both banks (does nothing if not in AGB mode)*/ + gb_apu_write_register( 0, 0xFF1A, b * 0x40 ); + for ( unsigned i = 0; i < sizeof initial_wave [0]; i++ ) + gb_apu_write_register( 0, i + WAVE_RAM, initial_wave [1] [i] ); + } +} + +void gb_apu_new(void) +{ + int i; + + gb_apu.wave.wave_ram = &gb_apu.regs [WAVE_RAM - START_ADDR]; + + gb_apu.oscs [0] = &gb_apu.square1; + gb_apu.oscs [1] = &gb_apu.square2; + gb_apu.oscs [2] = &gb_apu.wave; + gb_apu.oscs [3] = &gb_apu.noise; + + for ( i = OSC_COUNT; --i >= 0; ) + { + Gb_Osc& o = *gb_apu.oscs [i]; + o.regs = &gb_apu.regs [i * 5]; + o.output = 0; + o.outputs [0] = 0; + o.outputs [1] = 0; + o.outputs [2] = 0; + o.outputs [3] = 0; + o.good_synth = &gb_apu.good_synth; + o.med_synth = &gb_apu.med_synth; + } + + gb_apu.reduce_clicks_ = false; + gb_apu.frame_period = 4194304 / 512; /* 512 Hz*/ + + gb_apu.volume_ = 1.0; + gb_apu_reset(MODE_AGB, false); +} + + + +void gb_apu_set_output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right, int osc ) +{ + int i; + + i = osc; + do + { + Gb_Osc& o = *gb_apu.oscs [i]; + o.outputs [1] = right; + o.outputs [2] = left; + o.outputs [3] = center; + o.output = o.outputs [gb_apu_calc_output( i )]; + ++i; + } + while ( i < osc ); +} + +void gb_apu_volume( double v ) +{ + if ( gb_apu.volume_ != v ) + { + gb_apu.volume_ = v; + gb_apu_apply_volume(); + } +} + +void gb_apu_apply_stereo (void) +{ + int i; + + for ( i = OSC_COUNT; --i >= 0; ) + { + Gb_Osc& o = *gb_apu.oscs [i]; + Blip_Buffer* out = o.outputs [gb_apu_calc_output( i )]; + if ( o.output != out ) + { + gb_apu_silence_osc( o ); + o.output = out; + } + } +} + + +/*============================================================ + GB OSCS +============================================================ */ + + +/*============================================================ + BLIP BUFFER +============================================================ */ + +/* Blip_Buffer 0.4.1. http://www.slack.net/~ant */ + +#define FIXED_SHIFT 12 +#define SAL_FIXED_SHIFT 4096 +#define TO_FIXED( f ) int ((f) * SAL_FIXED_SHIFT) +#define FROM_FIXED( f ) ((f) >> FIXED_SHIFT) + + + +/*============================================================ + STEREO BUFFER +============================================================ */ + +/* Uses three buffers (one for center) and outputs stereo sample pairs. */ + +#define STEREO_BUFFER_SAMPLES_AVAILABLE() ((long)(bufs_buffer[0].offset_ - mixer_samples_read) << 1) +#define stereo_buffer_samples_avail() ((((bufs_buffer [0].offset_ >> BLIP_BUFFER_ACCURACY) - mixer_samples_read) << 1)) + + +const char * stereo_buffer_set_sample_rate( long rate, int msec ) +{ + mixer_samples_read = 0; + for ( int i = BUFS_SIZE; --i >= 0; ) + RETURN_ERR( bufs_buffer [i].set_sample_rate( rate, msec ) ); + return 0; +} + +void stereo_buffer_clock_rate( long rate ) +{ + bufs_buffer[2].factor_ = bufs_buffer [2].clock_rate_factor( rate ); + bufs_buffer[1].factor_ = bufs_buffer [1].clock_rate_factor( rate ); + bufs_buffer[0].factor_ = bufs_buffer [0].clock_rate_factor( rate ); +} + +void stereo_buffer_clear (void) +{ + mixer_samples_read = 0; + bufs_buffer [2].clear(); + bufs_buffer [1].clear(); + bufs_buffer [0].clear(); +} + +/* mixers use a single index value to improve performance on register-challenged processors + * offset goes from negative to zero*/ + +INLINE void stereo_buffer_mixer_read_pairs( int16_t* out, int count ) +{ + /* TODO: if caller never marks buffers as modified, uses mono*/ + /* except that buffer isn't cleared, so caller can encounter*/ + /* subtle problems and not realize the cause.*/ + mixer_samples_read += count; + int16_t* outtemp = out + count * STEREO; + + /* do left + center and right + center separately to reduce register load*/ + Blip_Buffer* buf = &bufs_buffer [2]; + { + --buf; + --outtemp; + + BLIP_READER_BEGIN( side, *buf ); + BLIP_READER_BEGIN( center, bufs_buffer[2] ); + + BLIP_READER_ADJ_( side, mixer_samples_read ); + BLIP_READER_ADJ_( center, mixer_samples_read ); + + int offset = -count; + do + { + int s = (center_reader_accum + side_reader_accum) >> 14; + BLIP_READER_NEXT_IDX_( side, offset ); + BLIP_READER_NEXT_IDX_( center, offset ); + BLIP_CLAMP( s, s ); + + ++offset; /* before write since out is decremented to slightly before end*/ + outtemp [offset * STEREO] = (int16_t) s; + }while ( offset ); + + BLIP_READER_END( side, *buf ); + } + { + --buf; + --outtemp; + + BLIP_READER_BEGIN( side, *buf ); + BLIP_READER_BEGIN( center, bufs_buffer[2] ); + + BLIP_READER_ADJ_( side, mixer_samples_read ); + BLIP_READER_ADJ_( center, mixer_samples_read ); + + int offset = -count; + do + { + int s = (center_reader_accum + side_reader_accum) >> 14; + BLIP_READER_NEXT_IDX_( side, offset ); + BLIP_READER_NEXT_IDX_( center, offset ); + BLIP_CLAMP( s, s ); + + ++offset; /* before write since out is decremented to slightly before end*/ + outtemp [offset * STEREO] = (int16_t) s; + }while ( offset ); + + BLIP_READER_END( side, *buf ); + + /* only end center once*/ + BLIP_READER_END( center, bufs_buffer[2] ); + } +} + +void blip_buffer_remove_all_samples( long count ) +{ + uint32_t new_offset = (uint32_t)count << BLIP_BUFFER_ACCURACY; + /* BLIP BUFFER #1 */ + bufs_buffer[0].offset_ -= new_offset; + bufs_buffer[1].offset_ -= new_offset; + bufs_buffer[2].offset_ -= new_offset; + + /* copy remaining samples to beginning and clear old samples*/ + long remain = (bufs_buffer[0].offset_ >> BLIP_BUFFER_ACCURACY) + BLIP_BUFFER_EXTRA_; + memmove( bufs_buffer[0].buffer_, bufs_buffer[0].buffer_ + count, remain * sizeof *bufs_buffer[0].buffer_ ); + memset( bufs_buffer[0].buffer_ + remain, 0, count * sizeof(*bufs_buffer[0].buffer_)); + + remain = (bufs_buffer[1].offset_ >> BLIP_BUFFER_ACCURACY) + BLIP_BUFFER_EXTRA_; + memmove( bufs_buffer[1].buffer_, bufs_buffer[1].buffer_ + count, remain * sizeof *bufs_buffer[1].buffer_ ); + memset( bufs_buffer[1].buffer_ + remain, 0, count * sizeof(*bufs_buffer[1].buffer_)); + + remain = (bufs_buffer[2].offset_ >> BLIP_BUFFER_ACCURACY) + BLIP_BUFFER_EXTRA_; + memmove( bufs_buffer[2].buffer_, bufs_buffer[2].buffer_ + count, remain * sizeof *bufs_buffer[2].buffer_ ); + memset( bufs_buffer[2].buffer_ + remain, 0, count * sizeof(*bufs_buffer[2].buffer_)); +} + +long stereo_buffer_read_samples( int16_t * out, long out_size ) +{ + int pair_count; + + out_size = (STEREO_BUFFER_SAMPLES_AVAILABLE() < out_size) ? STEREO_BUFFER_SAMPLES_AVAILABLE() : out_size; + + pair_count = int (out_size >> 1); + if ( pair_count ) + { + stereo_buffer_mixer_read_pairs( out, pair_count ); + blip_buffer_remove_all_samples( mixer_samples_read ); + mixer_samples_read = 0; + } + return out_size; +} + +void gba_to_gb_sound_parallel( int * __restrict addr, int * __restrict addr2 ) +{ + uint32_t addr1_table = *addr - 0x60; + uint32_t addr2_table = *addr2 - 0x60; + *addr = table [addr1_table]; + *addr2 = table [addr2_table]; +} + +void pcm_fifo_write_control( int data, int data2) +{ + pcm[0].enabled = (data & 0x0300) ? true : false; + pcm[0].timer = (data & 0x0400) ? 1 : 0; + + if ( data & 0x0800 ) + { + // Reset + pcm[0].writeIndex = 0; + pcm[0].readIndex = 0; + pcm[0].count = 0; + pcm[0].dac = 0; + memset(pcm[0].fifo, 0, sizeof(pcm[0].fifo)); + } + + gba_pcm_apply_control( 0, pcm[0].which ); + + if(pcm[0].pcm.output) + { + int time = SOUND_CLOCK_TICKS - soundTicks; + + pcm[0].dac = (int8_t)pcm[0].dac >> pcm[0].pcm.shift; + int delta = pcm[0].dac - pcm[0].pcm.last_amp; + if ( delta ) + { + pcm[0].pcm.last_amp = pcm[0].dac; + pcm_synth.offset( time, delta, pcm[0].pcm.output ); + } + pcm[0].pcm.last_time = time; + } + + pcm[1].enabled = (data2 & 0x0300) ? true : false; + pcm[1].timer = (data2 & 0x0400) ? 1 : 0; + + if ( data2 & 0x0800 ) + { + // Reset + pcm[1].writeIndex = 0; + pcm[1].readIndex = 0; + pcm[1].count = 0; + pcm[1].dac = 0; + memset( pcm[1].fifo, 0, sizeof(pcm[1].fifo)); + } + + gba_pcm_apply_control( 1, pcm[1].which ); + + if(pcm[1].pcm.output) + { + int time = SOUND_CLOCK_TICKS - soundTicks; + + pcm[1].dac = (int8_t)pcm[1].dac >> pcm[1].pcm.shift; + int delta = pcm[1].dac - pcm[1].pcm.last_amp; + if ( delta ) + { + pcm[1].pcm.last_amp = pcm[1].dac; + pcm_synth.offset( time, delta, pcm[1].pcm.output ); + } + pcm[1].pcm.last_time = time; + } +} + +void soundEvent_u16_parallel(uint32_t address[]) +{ + for(int i = 0; i < 8; i++) + { + switch ( address[i] ) + { + case SGCNT0_H: + //Begin of Write SGCNT0_H + WRITE16LE( &ioMem [SGCNT0_H], 0 & 0x770F ); + pcm_fifo_write_control(0, 0); + + gb_apu_volume( apu_vols [ioMem [SGCNT0_H] & 3] ); + //End of SGCNT0_H + break; + + case FIFOA_L: + case FIFOA_H: + pcm[0].fifo [pcm[0].writeIndex ] = 0; + pcm[0].fifo [pcm[0].writeIndex+1] = 0; + pcm[0].count += 2; + pcm[0].writeIndex = (pcm[0].writeIndex + 2) & 31; + WRITE16LE( &ioMem[address[i]], 0 ); + break; + + case FIFOB_L: + case FIFOB_H: + pcm[1].fifo [pcm[1].writeIndex ] = 0; + pcm[1].fifo [pcm[1].writeIndex+1] = 0; + pcm[1].count += 2; + pcm[1].writeIndex = (pcm[1].writeIndex + 2) & 31; + WRITE16LE( &ioMem[address[i]], 0 ); + break; + + case 0x88: + WRITE16LE( &ioMem[address[i]], 0 ); + break; + + default: + { + int gb_addr[2] = {address[i] & ~1, address[i] | 1}; + uint32_t address_array[2] = {address[i] & ~ 1, address[i] | 1}; + uint8_t data_array[2] = {0}; + gba_to_gb_sound_parallel(&gb_addr[0], &gb_addr[1]); + soundEvent_u8_parallel(gb_addr, address_array, data_array); + break; + } + } + } +} + +void gba_pcm_fifo_timer_overflowed( unsigned pcm_idx ) +{ + if ( pcm[pcm_idx].count <= 16 ) + { + // Need to fill FIFO + CPUCheckDMA( 3, pcm[pcm_idx].which ? 4 : 2 ); + + if ( pcm[pcm_idx].count <= 16 ) + { + // Not filled by DMA, so fill with 16 bytes of silence + int reg = pcm[pcm_idx].which ? FIFOB_L : FIFOA_L; + + uint32_t address_array[8] = {reg, reg+2, reg, reg+2, reg, reg+2, reg, reg+2}; + soundEvent_u16_parallel(address_array); + } + } + + // Read next sample from FIFO + pcm[pcm_idx].count--; + pcm[pcm_idx].dac = pcm[pcm_idx].fifo [pcm[pcm_idx].readIndex]; + pcm[pcm_idx].readIndex = (pcm[pcm_idx].readIndex + 1) & 31; + + if(pcm[pcm_idx].pcm.output) + { + int time = SOUND_CLOCK_TICKS - soundTicks; + + pcm[pcm_idx].dac = (int8_t)pcm[pcm_idx].dac >> pcm[pcm_idx].pcm.shift; + int delta = pcm[pcm_idx].dac - pcm[pcm_idx].pcm.last_amp; + if ( delta ) + { + pcm[pcm_idx].pcm.last_amp = pcm[pcm_idx].dac; + pcm_synth.offset( time, delta, pcm[pcm_idx].pcm.output ); + } + pcm[pcm_idx].pcm.last_time = time; + } +} + +void soundEvent_u8_parallel(int gb_addr[], uint32_t address[], uint8_t data[]) +{ + for(uint32_t i = 0; i < 2; i++) + { + ioMem[address[i]] = data[i]; + gb_apu_write_register( SOUND_CLOCK_TICKS - soundTicks, gb_addr[i], data[i] ); + + if ( address[i] == NR52 ) + { + gba_pcm_apply_control(0, 0 ); + gba_pcm_apply_control(1, 1 ); + } + // TODO: what about byte writes to SGCNT0_H etc.? + } +} + +void soundEvent_u8(int gb_addr, uint32_t address, uint8_t data) +{ + ioMem[address] = data; + gb_apu_write_register( SOUND_CLOCK_TICKS - soundTicks, gb_addr, data ); + + if ( address == NR52 ) + { + gba_pcm_apply_control(0, 0 ); + gba_pcm_apply_control(1, 1 ); + } + // TODO: what about byte writes to SGCNT0_H etc.? +} + + +void soundEvent_u16(uint32_t address, uint16_t data) +{ + switch ( address ) + { + case SGCNT0_H: + //Begin of Write SGCNT0_H + WRITE16LE( &ioMem [SGCNT0_H], data & 0x770F ); + pcm_fifo_write_control( data, data >> 4); + + gb_apu_volume( apu_vols [ioMem [SGCNT0_H] & 3] ); + //End of SGCNT0_H + break; + + case FIFOA_L: + case FIFOA_H: + pcm[0].fifo [pcm[0].writeIndex ] = data & 0xFF; + pcm[0].fifo [pcm[0].writeIndex+1] = data >> 8; + pcm[0].count += 2; + pcm[0].writeIndex = (pcm[0].writeIndex + 2) & 31; + WRITE16LE( &ioMem[address], data ); + break; + + case FIFOB_L: + case FIFOB_H: + pcm[1].fifo [pcm[1].writeIndex ] = data & 0xFF; + pcm[1].fifo [pcm[1].writeIndex+1] = data >> 8; + pcm[1].count += 2; + pcm[1].writeIndex = (pcm[1].writeIndex + 2) & 31; + WRITE16LE( &ioMem[address], data ); + break; + + case 0x88: + data &= 0xC3FF; + WRITE16LE( &ioMem[address], data ); + break; + + default: + { + int gb_addr[2] = {address & ~1, address | 1}; + uint32_t address_array[2] = {address & ~ 1, address | 1}; + uint8_t data_array[2] = {(uint8_t)data, (uint8_t)(data >> 8)}; + gba_to_gb_sound_parallel(&gb_addr[0], &gb_addr[1]); + soundEvent_u8_parallel(gb_addr, address_array, data_array); + break; + } + } +} + +void soundTimerOverflow(int timer) +{ + if ( timer == pcm[0].timer && pcm[0].enabled ) + gba_pcm_fifo_timer_overflowed(0); + if ( timer == pcm[1].timer && pcm[1].enabled ) + gba_pcm_fifo_timer_overflowed(1); +} + +void process_sound_tick_fn (void) +{ + // Run sound hardware to present + pcm[0].pcm.last_time -= SOUND_CLOCK_TICKS; + if ( pcm[0].pcm.last_time < -2048 ) + pcm[0].pcm.last_time = -2048; + + pcm[1].pcm.last_time -= SOUND_CLOCK_TICKS; + if ( pcm[1].pcm.last_time < -2048 ) + pcm[1].pcm.last_time = -2048; + + /* Emulates sound hardware up to a specified time, ends current time + frame, then starts a new frame at time 0 */ + + if(SOUND_CLOCK_TICKS > gb_apu.last_time) + gb_apu_run_until_( SOUND_CLOCK_TICKS ); + + gb_apu.frame_time -= SOUND_CLOCK_TICKS; + gb_apu.last_time -= SOUND_CLOCK_TICKS; + + bufs_buffer[2].offset_ += SOUND_CLOCK_TICKS * bufs_buffer[2].factor_; + bufs_buffer[1].offset_ += SOUND_CLOCK_TICKS * bufs_buffer[1].factor_; + bufs_buffer[0].offset_ += SOUND_CLOCK_TICKS * bufs_buffer[0].factor_; + + + // dump all the samples available + // VBA will only ever store 1 frame worth of samples + int numSamples = stereo_buffer_read_samples( (int16_t*) soundFinalWave, stereo_buffer_samples_avail()); + systemOnWriteDataToSoundBuffer(soundFinalWave, numSamples); +} + +void apply_muting (void) +{ + // PCM + gba_pcm_apply_control(1, 0 ); + gba_pcm_apply_control(1, 1 ); + + // APU + gb_apu_set_output( &bufs_buffer[2], &bufs_buffer[0], &bufs_buffer[1], 0 ); + gb_apu_set_output( &bufs_buffer[2], &bufs_buffer[0], &bufs_buffer[1], 1 ); + gb_apu_set_output( &bufs_buffer[2], &bufs_buffer[0], &bufs_buffer[1], 2 ); + gb_apu_set_output( &bufs_buffer[2], &bufs_buffer[0], &bufs_buffer[1], 3 ); +} + + +void remake_stereo_buffer (void) +{ + if ( !ioMem ) + return; + + // Clears pointers kept to old stereo_buffer + gba_pcm_init(); + + // Stereo_Buffer + + mixer_samples_read = 0; + stereo_buffer_set_sample_rate( soundSampleRate, BLIP_DEFAULT_LENGTH ); + stereo_buffer_clock_rate( CLOCK_RATE ); + + // PCM + pcm [0].which = 0; + pcm [1].which = 1; + + // APU + gb_apu_new(); + gb_apu_reset( MODE_AGB, true ); + + stereo_buffer_clear(); + + soundTicks = SOUND_CLOCK_TICKS; + + apply_muting(); + + gb_apu_volume(apu_vols [ioMem [SGCNT0_H] & 3] ); + + pcm_synth.volume( 0.66 / 256 * SOUNDVOLUME_ ); +} + +void soundReset (void) +{ + remake_stereo_buffer(); + //Begin of Reset APU + gb_apu_reset( MODE_AGB, true ); + + stereo_buffer_clear(); + + soundTicks = SOUND_CLOCK_TICKS; + //End of Reset APU + + SOUND_CLOCK_TICKS = SOUND_CLOCK_TICKS_; + soundTicks = SOUND_CLOCK_TICKS_; + + // Sound Event (NR52) + int gb_addr = table[NR52 - 0x60]; + if ( gb_addr ) + { + ioMem[NR52] = 0x80; + gb_apu_write_register( SOUND_CLOCK_TICKS - soundTicks, gb_addr, 0x80 ); + + gba_pcm_apply_control(0, 0 ); + gba_pcm_apply_control(1, 1 ); + } + + // TODO: what about byte writes to SGCNT0_H etc.? + // End of Sound Event (NR52) +} +/* +void soundSetSampleRate(long sampleRate) +{ + if ( soundSampleRate != sampleRate ) + { + soundSampleRate = sampleRate; + remake_stereo_buffer(); + } +} +*/ + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// END SOUND.CPP +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// BEGIN GBA.CPP +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/*============================================================ + GBA INLINE +============================================================ */ + +#define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value); +#define ARM_PREFETCH_NEXT cpuPrefetch[1] = CPUReadMemoryQuick(bus.armNextPC+4); +#define THUMB_PREFETCH_NEXT cpuPrefetch[1] = CPUReadHalfWordQuick(bus.armNextPC+2); + +#define ARM_PREFETCH \ + {\ + cpuPrefetch[0] = CPUReadMemoryQuick(bus.armNextPC);\ + cpuPrefetch[1] = CPUReadMemoryQuick(bus.armNextPC+4);\ + } + +#define THUMB_PREFETCH \ + {\ + cpuPrefetch[0] = CPUReadHalfWordQuick(bus.armNextPC);\ + cpuPrefetch[1] = CPUReadHalfWordQuick(bus.armNextPC+2);\ + } + +#ifdef USE_SWITICKS +extern int SWITicks; +#endif +int cpuNextEvent; // = 0; +bool holdState; // = false; +uint32_t cpuPrefetch[2]; +int cpuTotalTicks; // = 0; +uint8_t memoryWait[16]; +uint8_t memoryWaitSeq[16]; +uint8_t memoryWait32[16]; +uint8_t memoryWaitSeq32[16]; + +uint8_t biosProtected[4]; +uint8_t cpuBitsSet[256]; + +bool N_FLAG; // = 0; +bool C_FLAG; // = 0; +bool Z_FLAG; // = 0; +bool V_FLAG; // = 0; +bool armState; // = true; +bool armIrqEnable; // = true; +int armMode; // = 0x1f; + +typedef enum +{ + REG_DISPCNT = 0x000, + REG_DISPSTAT = 0x002, + REG_VCOUNT = 0x003, + REG_BG0CNT = 0x004, + REG_BG1CNT = 0x005, + REG_BG2CNT = 0x006, + REG_BG3CNT = 0x007, + REG_BG0HOFS = 0x08, + REG_BG0VOFS = 0x09, + REG_BG1HOFS = 0x0A, + REG_BG1VOFS = 0x0B, + REG_BG2HOFS = 0x0C, + REG_BG2VOFS = 0x0D, + REG_BG3HOFS = 0x0E, + REG_BG3VOFS = 0x0F, + REG_BG2PA = 0x10, + REG_BG2PB = 0x11, + REG_BG2PC = 0x12, + REG_BG2PD = 0x13, + REG_BG2X_L = 0x14, + REG_BG2X_H = 0x15, + REG_BG2Y_L = 0x16, + REG_BG2Y_H = 0x17, + REG_BG3PA = 0x18, + REG_BG3PB = 0x19, + REG_BG3PC = 0x1A, + REG_BG3PD = 0x1B, + REG_BG3X_L = 0x1C, + REG_BG3X_H = 0x1D, + REG_BG3Y_L = 0x1E, + REG_BG3Y_H = 0x1F, + REG_WIN0H = 0x20, + REG_WIN1H = 0x21, + REG_WIN0V = 0x22, + REG_WIN1V = 0x23, + REG_WININ = 0x24, + REG_WINOUT = 0x25, + REG_BLDCNT = 0x28, + REG_BLDALPHA = 0x29, + REG_BLDY = 0x2A, + REG_TM0D = 0x80, + REG_TM0CNT = 0x81, + REG_TM1D = 0x82, + REG_TM1CNT = 0x83, + REG_TM2D = 0x84, + REG_TM2CNT = 0x85, + REG_TM3D = 0x86, + REG_TM3CNT = 0x87, + REG_P1 = 0x098, + REG_P1CNT = 0x099, + REG_RCNT = 0x9A, + REG_IE = 0x100, + REG_IF = 0x101, + REG_IME = 0x104, + REG_HALTCNT = 0x180 +} hardware_register; + +uint16_t io_registers[1024 * 16]; + +u16 MOSAIC; + +uint16_t BG2X_L ; +uint16_t BG2X_H ; +uint16_t BG2Y_L ; +uint16_t BG2Y_H ; +uint16_t BG3X_L ; +uint16_t BG3X_H ; +uint16_t BG3Y_L ; +uint16_t BG3Y_H ; +uint16_t BLDMOD ; +uint16_t COLEV ; +uint16_t COLY ; +uint16_t DM0SAD_L ; +uint16_t DM0SAD_H ; +uint16_t DM0DAD_L ; +uint16_t DM0DAD_H ; +uint16_t DM0CNT_L ; +uint16_t DM0CNT_H ; +uint16_t DM1SAD_L ; +uint16_t DM1SAD_H ; +uint16_t DM1DAD_L ; +uint16_t DM1DAD_H ; +uint16_t DM1CNT_L ; +uint16_t DM1CNT_H ; +uint16_t DM2SAD_L ; +uint16_t DM2SAD_H ; +uint16_t DM2DAD_L ; +uint16_t DM2DAD_H ; +uint16_t DM2CNT_L ; +uint16_t DM2CNT_H ; +uint16_t DM3SAD_L ; +uint16_t DM3SAD_H ; +uint16_t DM3DAD_L ; +uint16_t DM3DAD_H ; +uint16_t DM3CNT_L ; +uint16_t DM3CNT_H ; + +uint8_t timerOnOffDelay ; +uint16_t timer0Value ; +uint32_t dma0Source ; +uint32_t dma0Dest ; +uint32_t dma1Source ; +uint32_t dma1Dest ; +uint32_t dma2Source ; +uint32_t dma2Dest ; +uint32_t dma3Source ; +uint32_t dma3Dest ; +void (Gigazoid::*cpuSaveGameFunc)(uint32_t,uint8_t); +bool fxOn ; +bool windowOn ; + +int cpuDmaTicksToUpdate; + +int IRQTicks; +bool intState; + +bus_t bus; +graphics_t graphics; + +memoryMap map[256]; +int clockTicks; + +int romSize; // = 0x2000000; +uint32_t line[6][240]; +bool gfxInWin[2][240]; +int lineOBJpixleft[128]; +int joy; + +int gfxBG2Changed; +int gfxBG3Changed; + +int gfxBG2X; +int gfxBG2Y; +int gfxBG3X; +int gfxBG3Y; + +bool ioReadable[0x400]; +int gbaSaveType; // used to remember the save type on reset + +//int gfxLastVCOUNT = 0; + +// Waitstates when accessing data + +#define DATATICKS_ACCESS_BUS_PREFETCH(address, value) \ + int addr = (address >> 24) & 15; \ + if ((addr>=0x08) || (addr < 0x02)) \ + { \ + bus.busPrefetchCount=0; \ + bus.busPrefetch=false; \ + } \ + else if (bus.busPrefetch) \ + { \ + int waitState = value; \ + waitState = (1 & ~waitState) | (waitState & waitState); \ + bus.busPrefetchCount = ((bus.busPrefetchCount+1)<> 24) & 15]) +#define DATATICKS_ACCESS_32BIT_SEQ(address) (memoryWaitSeq32[(address >> 24) & 15]) +#define DATATICKS_ACCESS_16BIT(address) (memoryWait[(address >> 24) & 15]) +#define DATATICKS_ACCESS_16BIT_SEQ(address) (memoryWaitSeq[(address >> 24) & 15]) + +// Waitstates when executing opcode +INLINE int codeTicksAccess(u32 address, u8 bit32) // THUMB NON SEQ +{ + int addr, ret; + + addr = (address>>24) & 15; + + if (unsigned(addr - 0x08) <= 5) + { + if (bus.busPrefetchCount&0x1) + { + if (bus.busPrefetchCount&0x2) + { + bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>2) | (bus.busPrefetchCount&0xFFFFFF00); + return 0; + } + bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>1) | (bus.busPrefetchCount&0xFFFFFF00); + return memoryWaitSeq[addr]-1; + } + } + bus.busPrefetchCount = 0; + + if(bit32) /* ARM NON SEQ */ + ret = memoryWait32[addr]; + else /* THUMB NON SEQ */ + ret = memoryWait[addr]; + + return ret; +} + +INLINE int codeTicksAccessSeq16(u32 address) // THUMB SEQ +{ + int addr = (address>>24) & 15; + + if (unsigned(addr - 0x08) <= 5) + { + if (bus.busPrefetchCount&0x1) + { + bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>1) | (bus.busPrefetchCount&0xFFFFFF00); + return 0; + } + else if (bus.busPrefetchCount>0xFF) + { + bus.busPrefetchCount=0; + return memoryWait[addr]; + } + } + else + bus.busPrefetchCount = 0; + + return memoryWaitSeq[addr]; +} + +INLINE int codeTicksAccessSeq32(u32 address) // ARM SEQ +{ + int addr = (address>>24)&15; + + if (unsigned(addr - 0x08) <= 5) + { + if (bus.busPrefetchCount&0x1) + { + if (bus.busPrefetchCount&0x2) + { + bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>2) | (bus.busPrefetchCount&0xFFFFFF00); + return 0; + } + bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>1) | (bus.busPrefetchCount&0xFFFFFF00); + return memoryWaitSeq[addr]; + } + else if (bus.busPrefetchCount > 0xFF) + { + bus.busPrefetchCount=0; + return memoryWait32[addr]; + } + } + return memoryWaitSeq32[addr]; +} + +#define CPUReadByteQuick(addr) map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] +#define CPUReadHalfWordQuick(addr) READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) +#define CPUReadMemoryQuick(addr) READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +bool stopState; +#ifdef USE_MOTION_SENSOR +extern bool cpuEEPROMSensorEnabled; +#endif +bool timer0On ; +int timer0Ticks ; +int timer0Reload ; +int timer0ClockReload ; +uint16_t timer1Value ; +bool timer1On ; +int timer1Ticks ; +int timer1Reload ; +int timer1ClockReload ; +uint16_t timer2Value ; +bool timer2On ; +int timer2Ticks ; +int timer2Reload ; +int timer2ClockReload ; +uint16_t timer3Value ; +bool timer3On ; +int timer3Ticks ; +int timer3Reload ; +int timer3ClockReload ; + +INLINE u32 CPUReadMemory(u32 address) +{ + u32 value; + switch(address >> 24) + { + case 0: + /* BIOS */ + if(bus.reg[15].I >> 24) + { + if(address < 0x4000) + value = READ32LE(((u32 *)&biosProtected)); + else goto unreadable; + } + else + value = READ32LE(((u32 *)&bios[address & 0x3FFC])); + break; + case 0x02: + /* external work RAM */ + value = READ32LE(((u32 *)&workRAM[address & 0x3FFFC])); + break; + case 0x03: + /* internal work RAM */ + value = READ32LE(((u32 *)&internalRAM[address & 0x7ffC])); + break; + case 0x04: + /* I/O registers */ + if((address < 0x4000400) && ioReadable[address & 0x3fc]) + { + if(ioReadable[(address & 0x3fc) + 2]) + value = READ32LE(((u32 *)&ioMem[address & 0x3fC])); + else + value = READ16LE(((u16 *)&ioMem[address & 0x3fc])); + } + else + goto unreadable; + break; + case 0x05: + /* palette RAM */ + value = READ32LE(((u32 *)&graphics.paletteRAM[address & 0x3fC])); + break; + case 0x06: + /* VRAM */ + address = (address & 0x1fffc); + if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) + { + value = 0; + break; + } + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + value = READ32LE(((u32 *)&vram[address])); + break; + case 0x07: + /* OAM RAM */ + value = READ32LE(((u32 *)&oam[address & 0x3FC])); + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + /* gamepak ROM */ + value = READ32LE(((u32 *)&rom[address&0x1FFFFFC])); + break; + case 0x0D: + value = eepromRead(); + break; + case 14: + case 15: + value = flashRead(address) * 0x01010101; + break; + default: +unreadable: + if(armState) + value = CPUReadHalfWordQuick(bus.reg[15].I + (address & 2)); + else + value = CPUReadHalfWordQuick(bus.reg[15].I); + } + + if(address & 3) { + int shift = (address & 3) << 3; + value = (value >> shift) | (value << (32 - shift)); + } + return value; +} + +INLINE u32 CPUReadHalfWord(u32 address) +{ + u32 value; + + switch(address >> 24) + { + case 0: + if (bus.reg[15].I >> 24) + { + if(address < 0x4000) + value = READ16LE(((u16 *)&biosProtected[address&2])); + else + goto unreadable; + } + else + value = READ16LE(((u16 *)&bios[address & 0x3FFE])); + break; + case 2: + value = READ16LE(((u16 *)&workRAM[address & 0x3FFFE])); + break; + case 3: + value = READ16LE(((u16 *)&internalRAM[address & 0x7ffe])); + break; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3fe]) + { + value = READ16LE(((u16 *)&ioMem[address & 0x3fe])); + if (((address & 0x3fe)>0xFF) && ((address & 0x3fe)<0x10E)) + { + if (((address & 0x3fe) == 0x100) && timer0On) + value = 0xFFFF - ((timer0Ticks-cpuTotalTicks) >> timer0ClockReload); + else + if (((address & 0x3fe) == 0x104) && timer1On && !(io_registers[REG_TM1CNT] & 4)) + value = 0xFFFF - ((timer1Ticks-cpuTotalTicks) >> timer1ClockReload); + else + if (((address & 0x3fe) == 0x108) && timer2On && !(io_registers[REG_TM2CNT] & 4)) + value = 0xFFFF - ((timer2Ticks-cpuTotalTicks) >> timer2ClockReload); + else + if (((address & 0x3fe) == 0x10C) && timer3On && !(io_registers[REG_TM3CNT] & 4)) + value = 0xFFFF - ((timer3Ticks-cpuTotalTicks) >> timer3ClockReload); + } + } + else goto unreadable; + break; + case 5: + value = READ16LE(((u16 *)&graphics.paletteRAM[address & 0x3fe])); + break; + case 6: + address = (address & 0x1fffe); + if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) + { + value = 0; + break; + } + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + value = READ16LE(((u16 *)&vram[address])); + break; + case 7: + value = READ16LE(((u16 *)&oam[address & 0x3fe])); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) + value = rtcRead(address); + else + value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE])); + break; + case 13: + value = eepromRead(); + break; + case 14: + value = flashRead(address) * 0x0101; + break; + default: +unreadable: + { + int param = bus.reg[15].I; + if(armState) + param += (address & 2); + value = CPUReadHalfWordQuick(param); + } + break; + } + + if(address & 1) + value = (value >> 8) | (value << 24); + + return value; +} + +INLINE u16 CPUReadHalfWordSigned(u32 address) +{ + u16 value = CPUReadHalfWord(address); + if((address & 1)) + value = (s8)value; + return value; +} + +INLINE u8 CPUReadByte(u32 address) +{ + switch(address >> 24) + { + case 0: + if (bus.reg[15].I >> 24) + { + if(address < 0x4000) + return biosProtected[address & 3]; + else + goto unreadable; + } + return bios[address & 0x3FFF]; + case 2: + return workRAM[address & 0x3FFFF]; + case 3: + return internalRAM[address & 0x7fff]; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3ff]) + return ioMem[address & 0x3ff]; + else goto unreadable; + case 5: + return graphics.paletteRAM[address & 0x3ff]; + case 6: + address = (address & 0x1ffff); + if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) + return 0; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + return vram[address]; + case 7: + return oam[address & 0x3ff]; + case 8: + case 9: + case 10: + case 11: + case 12: + return rom[address & 0x1FFFFFF]; + case 13: + return eepromRead(); + case 14: +#ifdef USE_MOTION_SENSOR + if(cpuEEPROMSensorEnabled) + { + switch(address & 0x00008f00) + { + case 0x8200: + return systemGetSensorX() & 255; + case 0x8300: + return (systemGetSensorX() >> 8)|0x80; + case 0x8400: + return systemGetSensorY() & 255; + case 0x8500: + return systemGetSensorY() >> 8; + } + } +#endif + return flashRead(address); + default: +unreadable: + if(armState) + return CPUReadByteQuick(bus.reg[15].I+(address & 3)); + else + return CPUReadByteQuick(bus.reg[15].I+(address & 1)); + } +} + +INLINE void CPUWriteMemory(u32 address, u32 value) +{ + switch(address >> 24) + { + case 0x02: + WRITE32LE(((u32 *)&workRAM[address & 0x3FFFC]), value); + break; + case 0x03: + WRITE32LE(((u32 *)&internalRAM[address & 0x7ffC]), value); + break; + case 0x04: + if(address < 0x4000400) + { + CPUUpdateRegister((address & 0x3FC), value & 0xFFFF); + CPUUpdateRegister((address & 0x3FC) + 2, (value >> 16)); + } + break; + case 0x05: + WRITE32LE(((u32 *)&graphics.paletteRAM[address & 0x3FC]), value); + break; + case 0x06: + address = (address & 0x1fffc); + if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + + + WRITE32LE(((u32 *)&vram[address]), value); + break; + case 0x07: + WRITE32LE(((u32 *)&oam[address & 0x3fc]), value); + break; + case 0x0D: + if(cpuEEPROMEnabled) { + eepromWrite(value); + break; + } + break; + case 0x0E: + if((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled) + (this->*cpuSaveGameFunc)(address, (u8)value); + break; + default: + break; + } +} + +INLINE void CPUWriteHalfWord(u32 address, u16 value) +{ + switch(address >> 24) + { + case 2: + WRITE16LE(((u16 *)&workRAM[address & 0x3FFFE]),value); + break; + case 3: + WRITE16LE(((u16 *)&internalRAM[address & 0x7ffe]), value); + break; + case 4: + if(address < 0x4000400) + CPUUpdateRegister(address & 0x3fe, value); + break; + case 5: + WRITE16LE(((u16 *)&graphics.paletteRAM[address & 0x3fe]), value); + break; + case 6: + address = (address & 0x1fffe); + if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + WRITE16LE(((u16 *)&vram[address]), value); + break; + case 7: + WRITE16LE(((u16 *)&oam[address & 0x3fe]), value); + break; + case 8: + case 9: + if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) + if(!rtcWrite(address, value)) + break; + break; + case 13: + if(cpuEEPROMEnabled) + eepromWrite((u8)value); + break; + case 14: + if((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled) + (this->*cpuSaveGameFunc)(address, (u8)value); + break; + default: + break; + } +} + +INLINE void CPUWriteByte(u32 address, u8 b) +{ + switch(address >> 24) + { + case 2: + workRAM[address & 0x3FFFF] = b; + break; + case 3: + internalRAM[address & 0x7fff] = b; + break; + case 4: + if(address < 0x4000400) + { + switch(address & 0x3FF) + { + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x68: + case 0x69: + case 0x6c: + case 0x6d: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x78: + case 0x79: + case 0x7c: + case 0x7d: + case 0x80: + case 0x81: + case 0x84: + case 0x85: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + { + int gb_addr = table[(address & 0xFF) - 0x60]; + soundEvent_u8(gb_addr, address&0xFF, b); + } + break; + case 0x301: // HALTCNT, undocumented + if(b == 0x80) + stopState = true; + holdState = 1; + cpuNextEvent = cpuTotalTicks; + break; + default: // every other register + { + u32 lowerBits = address & 0x3fe; + uint16_t param; + if(address & 1) + param = (READ16LE(&ioMem[lowerBits]) & 0x00FF) | (b << 8); + else + param = (READ16LE(&ioMem[lowerBits]) & 0xFF00) | b; + + CPUUpdateRegister(lowerBits, param); + } + break; + } + } + break; + case 5: + // no need to switch + *((u16 *)&graphics.paletteRAM[address & 0x3FE]) = (b << 8) | b; + break; + case 6: + address = (address & 0x1fffe); + if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + + // no need to switch + // byte writes to OBJ VRAM are ignored + if ((address) < objTilesAddress[((io_registers[REG_DISPCNT] & 7)+1)>>2]) + *((u16 *)&vram[address]) = (b << 8) | b; + break; + case 7: + // no need to switch + // byte writes to OAM are ignored + // *((u16 *)&oam[address & 0x3FE]) = (b << 8) | b; + break; + case 13: + if(cpuEEPROMEnabled) + eepromWrite(b); + break; + case 14: + if ((saveType != 5) && ((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled)) + { + (this->*cpuSaveGameFunc)(address, b); + break; + } + default: + break; + } +} + + +/*============================================================ + BIOS +============================================================ */ + +void BIOS_RegisterRamReset(u32 flags) +{ + // no need to trace here. this is only called directly from GBA.cpp + // to emulate bios initialization + + CPUUpdateRegister(0x0, 0x80); + + if(flags) + { + if(flags & 0x01) + memset(workRAM, 0, 0x40000); // clear work RAM + + if(flags & 0x02) + memset(internalRAM, 0, 0x7e00); // don't clear 0x7e00-0x7fff, clear internal RAM + + if(flags & 0x04) + memset(graphics.paletteRAM, 0, 0x400); // clear palette RAM + + if(flags & 0x08) + memset(vram, 0, 0x18000); // clear VRAM + + if(flags & 0x10) + memset(oam, 0, 0x400); // clean OAM + + if(flags & 0x80) { + int i; + for(i = 0; i < 0x10; i++) + CPUUpdateRegister(0x200+i*2, 0); + + for(i = 0; i < 0xF; i++) + CPUUpdateRegister(0x4+i*2, 0); + + for(i = 0; i < 0x20; i++) + CPUUpdateRegister(0x20+i*2, 0); + + for(i = 0; i < 0x18; i++) + CPUUpdateRegister(0xb0+i*2, 0); + + CPUUpdateRegister(0x130, 0); + CPUUpdateRegister(0x20, 0x100); + CPUUpdateRegister(0x30, 0x100); + CPUUpdateRegister(0x26, 0x100); + CPUUpdateRegister(0x36, 0x100); + } + + if(flags & 0x20) { + int i; + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x110+i*2, 0); + CPUUpdateRegister(0x134, 0x8000); + for(i = 0; i < 7; i++) + CPUUpdateRegister(0x140+i*2, 0); + } + + if(flags & 0x40) { + int i; + CPUWriteByte(0x4000084, 0); + CPUWriteByte(0x4000084, 0x80); + CPUWriteMemory(0x4000080, 0x880e0000); + CPUUpdateRegister(0x88, CPUReadHalfWord(0x4000088)&0x3ff); + CPUWriteByte(0x4000070, 0x70); + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x90+i*2, 0); + CPUWriteByte(0x4000070, 0); + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x90+i*2, 0); + CPUWriteByte(0x4000084, 0); + } + } +} + +void BIOS_SoftReset (void) +{ + armState = true; + armMode = 0x1F; + armIrqEnable = false; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + bus.reg[13].I = 0x03007F00; + bus.reg[14].I = 0x00000000; + bus.reg[16].I = 0x00000000; + bus.reg[R13_IRQ].I = 0x03007FA0; + bus.reg[R14_IRQ].I = 0x00000000; + bus.reg[SPSR_IRQ].I = 0x00000000; + bus.reg[R13_SVC].I = 0x03007FE0; + bus.reg[R14_SVC].I = 0x00000000; + bus.reg[SPSR_SVC].I = 0x00000000; + u8 b = internalRAM[0x7ffa]; + + memset(&internalRAM[0x7e00], 0, 0x200); + + if(b) { + bus.armNextPC = 0x02000000; + bus.reg[15].I = 0x02000004; + } else { + bus.armNextPC = 0x08000000; + bus.reg[15].I = 0x08000004; + } +} + +#define BIOS_GET_BIOS_CHECKSUM() bus.reg[0].I=0xBAAE187F; + +#define BIOS_REGISTER_RAM_RESET() BIOS_RegisterRamReset(bus.reg[0].I); + +#define CPU_UPDATE_CPSR() \ +{ \ + uint32_t CPSR; \ + CPSR = bus.reg[16].I & 0x40; \ + if(N_FLAG) \ + CPSR |= 0x80000000; \ + if(Z_FLAG) \ + CPSR |= 0x40000000; \ + if(C_FLAG) \ + CPSR |= 0x20000000; \ + if(V_FLAG) \ + CPSR |= 0x10000000; \ + if(!armState) \ + CPSR |= 0x00000020; \ + if(!armIrqEnable) \ + CPSR |= 0x80; \ + CPSR |= (armMode & 0x1F); \ + bus.reg[16].I = CPSR; \ +} + +#define CPU_SOFTWARE_INTERRUPT() \ +{ \ + uint32_t PC = bus.reg[15].I; \ + bool savedArmState = armState; \ + if(armMode != 0x13) \ + CPUSwitchMode(0x13, true, false); \ + bus.reg[14].I = PC - (savedArmState ? 4 : 2); \ + bus.reg[15].I = 0x08; \ + armState = true; \ + armIrqEnable = false; \ + bus.armNextPC = 0x08; \ + ARM_PREFETCH; \ + bus.reg[15].I += 4; \ +} + +void CPUUpdateFlags(bool breakLoop) +{ + uint32_t CPSR = bus.reg[16].I; + + N_FLAG = (CPSR & 0x80000000) ? true: false; + Z_FLAG = (CPSR & 0x40000000) ? true: false; + C_FLAG = (CPSR & 0x20000000) ? true: false; + V_FLAG = (CPSR & 0x10000000) ? true: false; + armState = (CPSR & 0x20) ? false : true; + armIrqEnable = (CPSR & 0x80) ? false : true; + if (breakLoop && armIrqEnable && (io_registers[REG_IF] & io_registers[REG_IE]) && (io_registers[REG_IME] & 1)) + cpuNextEvent = cpuTotalTicks; +} + +void CPUSoftwareInterrupt(int comment) +{ + if(armState) + comment >>= 16; + + CPU_SOFTWARE_INTERRUPT(); +} + + +/*============================================================ + GBA ARM CORE +============================================================ */ + +#ifdef _MSC_VER + // Disable "empty statement" warnings + #pragma warning(disable: 4390) + // Visual C's inline assembler treats "offset" as a reserved word, so we + // tell it otherwise. If you want to use it, write "OFFSET" in capitals. + #define offset offset_ +#endif + +void armUnknownInsn(u32 opcode) +{ + u32 PC = bus.reg[15].I; + bool savedArmState = armState; + if(armMode != 0x1b ) + CPUSwitchMode(0x1b, true, false); + bus.reg[14].I = PC - (savedArmState ? 4 : 2); + bus.reg[15].I = 0x04; + armState = true; + armIrqEnable = false; + bus.armNextPC = 0x04; + ARM_PREFETCH; + bus.reg[15].I += 4; +} + +// Common macros ////////////////////////////////////////////////////////// + +#define NEG(i) ((i) >> 31) +#define POS(i) ((~(i)) >> 31) + +// The following macros are used for optimization; any not defined for a +// particular compiler/CPU combination default to the C core versions. +// +// ALU_INIT_C: Used at the beginning of ALU instructions (AND/EOR/...). +// (ALU_INIT_NC) Can consist of variable declarations, like the C core, +// or the start of a continued assembly block, like the +// x86-optimized version. The _C version is used when the +// carry flag from the shift operation is needed (logical +// operations that set condition codes, like ANDS); the +// _NC version is used when the carry result is ignored. +// VALUE_XXX: Retrieve the second operand's value for an ALU instruction. +// The _C and _NC versions are used the same way as ALU_INIT. +// OP_XXX: ALU operations. XXX is the instruction name. +// SETCOND_NONE: Used in multiply instructions in place of SETCOND_MUL +// when the condition codes are not set. Usually empty. +// SETCOND_MUL: Used in multiply instructions to set the condition codes. +// ROR_IMM_MSR: Used to rotate the immediate operand for MSR. +// ROR_OFFSET: Used to rotate the `offset' parameter for LDR and STR +// instructions. +// RRX_OFFSET: Used to rotate (RRX) the `offset' parameter for LDR and +// STR instructions. + +// C core + +#define C_SETCOND_LOGICAL \ + N_FLAG = ((s32)res < 0) ? true : false; \ + Z_FLAG = (res == 0) ? true : false; \ + C_FLAG = C_OUT; +#define C_SETCOND_ADD \ + N_FLAG = ((s32)res < 0) ? true : false; \ + Z_FLAG = (res == 0) ? true : false; \ + V_FLAG = ((NEG(lhs) & NEG(rhs) & POS(res)) | \ + (POS(lhs) & POS(rhs) & NEG(res))) ? true : false;\ + C_FLAG = ((NEG(lhs) & NEG(rhs)) | \ + (NEG(lhs) & POS(res)) | \ + (NEG(rhs) & POS(res))) ? true : false; +#define C_SETCOND_SUB \ + N_FLAG = ((s32)res < 0) ? true : false; \ + Z_FLAG = (res == 0) ? true : false; \ + V_FLAG = ((NEG(lhs) & POS(rhs) & POS(res)) | \ + (POS(lhs) & NEG(rhs) & NEG(res))) ? true : false;\ + C_FLAG = ((NEG(lhs) & POS(rhs)) | \ + (NEG(lhs) & POS(res)) | \ + (POS(rhs) & POS(res))) ? true : false; + +#ifndef ALU_INIT_C + #define ALU_INIT_C \ + int dest = (opcode>>12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; +#endif +// OP Rd,Rb,Rm LSL # +#ifndef VALUE_LSL_IMM_C + #define VALUE_LSL_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (!shift) { /* LSL #0 most common? */ \ + value = bus.reg[opcode & 0x0F].I; \ + } else { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (32 - shift)) & 1 ? true : false; \ + value = v << shift; \ + } +#endif +// OP Rd,Rb,Rm LSL Rs +#ifndef VALUE_LSL_REG_C + #define VALUE_LSL_REG_C \ + unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \ + if (shift) { \ + if (shift == 32) { \ + value = 0; \ + C_OUT = (bus.reg[opcode & 0x0F].I & 1 ? true : false);\ + } else if (shift < 32) { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (32 - shift)) & 1 ? true : false;\ + value = v << shift; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } else { \ + value = bus.reg[opcode & 0x0F].I; \ + } +#endif +// OP Rd,Rb,Rm LSR # +#ifndef VALUE_LSR_IMM_C + #define VALUE_LSR_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (shift) { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = v >> shift; \ + } else { \ + value = 0; \ + C_OUT = (bus.reg[opcode & 0x0F].I & 0x80000000) ? true : false;\ + } +#endif +// OP Rd,Rb,Rm LSR Rs +#ifndef VALUE_LSR_REG_C + #define VALUE_LSR_REG_C \ + unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \ + if (shift) { \ + if (shift == 32) { \ + value = 0; \ + C_OUT = (bus.reg[opcode & 0x0F].I & 0x80000000 ? true : false);\ + } else if (shift < 32) { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false;\ + value = v >> shift; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } else { \ + value = bus.reg[opcode & 0x0F].I; \ + } +#endif +// OP Rd,Rb,Rm ASR # +#ifndef VALUE_ASR_IMM_C + #define VALUE_ASR_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (shift) { \ + s32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;\ + value = v >> (int)shift; \ + } else { \ + if (bus.reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + C_OUT = true; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } +#endif +// OP Rd,Rb,Rm ASR Rs +#ifndef VALUE_ASR_REG_C + #define VALUE_ASR_REG_C \ + unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \ + if (shift < 32) { \ + if (shift) { \ + s32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;\ + value = v >> (int)shift; \ + } else { \ + value = bus.reg[opcode & 0x0F].I; \ + } \ + } else { \ + if (bus.reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + C_OUT = true; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } +#endif +// OP Rd,Rb,Rm ROR # +#ifndef VALUE_ROR_IMM_C + #define VALUE_ROR_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (shift) { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } else { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v & 1) ? true : false; \ + value = ((v >> 1) | \ + (C_FLAG << 31)); \ + } +#endif +// OP Rd,Rb,Rm ROR Rs +#ifndef VALUE_ROR_REG_C + #define VALUE_ROR_REG_C \ + unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \ + if (shift & 0x1F) { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } else { \ + value = bus.reg[opcode & 0x0F].I; \ + if (shift) \ + C_OUT = (value & 0x80000000 ? true : false);\ + } +#endif +// OP Rd,Rb,# ROR # +#ifndef VALUE_IMM_C + #define VALUE_IMM_C \ + int shift = (opcode & 0xF00) >> 7; \ + if (shift) { \ + u32 v = opcode & 0xFF; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } else { \ + value = opcode & 0xFF; \ + } +#endif + +// Make the non-carry versions default to the carry versions +// (this is fine for C--the compiler will optimize the dead code out) +#ifndef ALU_INIT_NC + #define ALU_INIT_NC ALU_INIT_C +#endif +#ifndef VALUE_LSL_IMM_NC + #define VALUE_LSL_IMM_NC VALUE_LSL_IMM_C +#endif +#ifndef VALUE_LSL_REG_NC + #define VALUE_LSL_REG_NC VALUE_LSL_REG_C +#endif +#ifndef VALUE_LSR_IMM_NC + #define VALUE_LSR_IMM_NC VALUE_LSR_IMM_C +#endif +#ifndef VALUE_LSR_REG_NC + #define VALUE_LSR_REG_NC VALUE_LSR_REG_C +#endif +#ifndef VALUE_ASR_IMM_NC + #define VALUE_ASR_IMM_NC VALUE_ASR_IMM_C +#endif +#ifndef VALUE_ASR_REG_NC + #define VALUE_ASR_REG_NC VALUE_ASR_REG_C +#endif +#ifndef VALUE_ROR_IMM_NC + #define VALUE_ROR_IMM_NC VALUE_ROR_IMM_C +#endif +#ifndef VALUE_ROR_REG_NC + #define VALUE_ROR_REG_NC VALUE_ROR_REG_C +#endif +#ifndef VALUE_IMM_NC + #define VALUE_IMM_NC VALUE_IMM_C +#endif + +#define C_CHECK_PC(SETCOND) if (dest != 15) { SETCOND } +#ifndef OP_AND + #define OP_AND \ + u32 res = bus.reg[(opcode>>16)&15].I & value; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_ANDS + #define OP_ANDS OP_AND C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_EOR + #define OP_EOR \ + u32 res = bus.reg[(opcode>>16)&15].I ^ value; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_EORS + #define OP_EORS OP_EOR C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_SUB + #define OP_SUB \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_SUBS + #define OP_SUBS OP_SUB C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_RSB + #define OP_RSB \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = rhs - lhs; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_RSBS + #define OP_RSBS OP_RSB C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_ADD + #define OP_ADD \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_ADDS + #define OP_ADDS OP_ADD C_CHECK_PC(C_SETCOND_ADD) +#endif +#ifndef OP_ADC + #define OP_ADC \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs + (u32)C_FLAG; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_ADCS + #define OP_ADCS OP_ADC C_CHECK_PC(C_SETCOND_ADD) +#endif +#ifndef OP_SBC + #define OP_SBC \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs - !((u32)C_FLAG); \ + bus.reg[dest].I = res; +#endif +#ifndef OP_SBCS + #define OP_SBCS OP_SBC C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_RSC + #define OP_RSC \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = rhs - lhs - !((u32)C_FLAG); \ + bus.reg[dest].I = res; +#endif +#ifndef OP_RSCS + #define OP_RSCS OP_RSC C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_TST + #define OP_TST \ + u32 res = bus.reg[(opcode >> 16) & 0x0F].I & value; \ + C_SETCOND_LOGICAL; +#endif +#ifndef OP_TEQ + #define OP_TEQ \ + u32 res = bus.reg[(opcode >> 16) & 0x0F].I ^ value; \ + C_SETCOND_LOGICAL; +#endif +#ifndef OP_CMP + #define OP_CMP \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + C_SETCOND_SUB; +#endif +#ifndef OP_CMN + #define OP_CMN \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + C_SETCOND_ADD; +#endif +#ifndef OP_ORR + #define OP_ORR \ + u32 res = bus.reg[(opcode >> 16) & 0x0F].I | value; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_ORRS + #define OP_ORRS OP_ORR C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_MOV + #define OP_MOV \ + u32 res = value; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_MOVS + #define OP_MOVS OP_MOV C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_BIC + #define OP_BIC \ + u32 res = bus.reg[(opcode >> 16) & 0x0F].I & (~value); \ + bus.reg[dest].I = res; +#endif +#ifndef OP_BICS + #define OP_BICS OP_BIC C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_MVN + #define OP_MVN \ + u32 res = ~value; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_MVNS + #define OP_MVNS OP_MVN C_CHECK_PC(C_SETCOND_LOGICAL) +#endif + +#ifndef SETCOND_NONE + #define SETCOND_NONE /*nothing*/ +#endif +#ifndef SETCOND_MUL + #define SETCOND_MUL \ + N_FLAG = ((s32)bus.reg[dest].I < 0) ? true : false; \ + Z_FLAG = bus.reg[dest].I ? false : true; +#endif +#ifndef SETCOND_MULL + #define SETCOND_MULL \ + N_FLAG = (bus.reg[dest].I & 0x80000000) ? true : false;\ + Z_FLAG = bus.reg[dest].I || bus.reg[acc].I ? false : true; +#endif + +#ifndef ROR_IMM_MSR + #define ROR_IMM_MSR \ + u32 v = opcode & 0xff; \ + value = ((v << (32 - shift)) | (v >> shift)); +#endif +#ifndef ROR_OFFSET + #define ROR_OFFSET \ + offset = ((offset << (32 - shift)) | (offset >> shift)); +#endif +#ifndef RRX_OFFSET + #define RRX_OFFSET \ + offset = ((offset >> 1) | ((int)C_FLAG << 31)); +#endif + +// ALU ops (except multiply) ////////////////////////////////////////////// + +// ALU_INIT: init code (ALU_INIT_C or ALU_INIT_NC) +// GETVALUE: load value and shift/rotate (VALUE_XXX) +// OP: ALU operation (OP_XXX) +// MODECHANGE: MODECHANGE_NO or MODECHANGE_YES +// ISREGSHIFT: 1 for insns of the form ...,Rn LSL/etc Rs; 0 otherwise +// ALU_INIT, GETVALUE and OP are concatenated in order. +#define ALU_INSN(ALU_INIT, GETVALUE, OP, MODECHANGE, ISREGSHIFT) \ + ALU_INIT GETVALUE OP; \ + if ((opcode & 0x0000F000) != 0x0000F000) { \ + clockTicks = 1 + ISREGSHIFT \ + + codeTicksAccessSeq32(bus.armNextPC); \ + } else { \ + MODECHANGE; \ + if (armState) { \ + bus.reg[15].I &= 0xFFFFFFFC; \ + bus.armNextPC = bus.reg[15].I; \ + bus.reg[15].I += 4; \ + ARM_PREFETCH; \ + } else { \ + bus.reg[15].I &= 0xFFFFFFFE; \ + bus.armNextPC = bus.reg[15].I; \ + bus.reg[15].I += 2; \ + THUMB_PREFETCH; \ + } \ + clockTicks = 3 + ISREGSHIFT \ + + codeTicksAccess(bus.armNextPC, BITS_32) \ + + ((codeTicksAccessSeq32(bus.armNextPC)) << 1); \ + } + +#define MODECHANGE_NO /*nothing*/ +#define MODECHANGE_YES if(armMode != (bus.reg[17].I & 0x1f)) CPUSwitchMode(bus.reg[17].I & 0x1f, false, true); + +#define DEFINE_ALU_INSN_C(CODE1, CODE2, OP, MODECHANGE) \ + void arm##CODE1##0(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##1(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE1##2(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##3(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE1##4(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##5(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE1##6(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##7(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE2##0(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); } +#define DEFINE_ALU_INSN_NC(CODE1, CODE2, OP, MODECHANGE) \ + void arm##CODE1##0(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##1(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE1##2(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##3(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE1##4(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##5(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE1##6(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##7(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE2##0(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); } + +// AND +DEFINE_ALU_INSN_NC(00, 20, AND, NO) +// ANDS +DEFINE_ALU_INSN_C (01, 21, ANDS, YES) + +// EOR +DEFINE_ALU_INSN_NC(02, 22, EOR, NO) +// EORS +DEFINE_ALU_INSN_C (03, 23, EORS, YES) + +// SUB +DEFINE_ALU_INSN_NC(04, 24, SUB, NO) +// SUBS +DEFINE_ALU_INSN_NC(05, 25, SUBS, YES) + +// RSB +DEFINE_ALU_INSN_NC(06, 26, RSB, NO) +// RSBS +DEFINE_ALU_INSN_NC(07, 27, RSBS, YES) + +// ADD +DEFINE_ALU_INSN_NC(08, 28, ADD, NO) +// ADDS +DEFINE_ALU_INSN_NC(09, 29, ADDS, YES) + +// ADC +DEFINE_ALU_INSN_NC(0A, 2A, ADC, NO) +// ADCS +DEFINE_ALU_INSN_NC(0B, 2B, ADCS, YES) + +// SBC +DEFINE_ALU_INSN_NC(0C, 2C, SBC, NO) +// SBCS +DEFINE_ALU_INSN_NC(0D, 2D, SBCS, YES) + +// RSC +DEFINE_ALU_INSN_NC(0E, 2E, RSC, NO) +// RSCS +DEFINE_ALU_INSN_NC(0F, 2F, RSCS, YES) + +// TST +DEFINE_ALU_INSN_C (11, 31, TST, NO) + +// TEQ +DEFINE_ALU_INSN_C (13, 33, TEQ, NO) + +// CMP +DEFINE_ALU_INSN_NC(15, 35, CMP, NO) + +// CMN +DEFINE_ALU_INSN_NC(17, 37, CMN, NO) + +// ORR +DEFINE_ALU_INSN_NC(18, 38, ORR, NO) +// ORRS +DEFINE_ALU_INSN_C (19, 39, ORRS, YES) + +// MOV +DEFINE_ALU_INSN_NC(1A, 3A, MOV, NO) +// MOVS +DEFINE_ALU_INSN_C (1B, 3B, MOVS, YES) + +// BIC +DEFINE_ALU_INSN_NC(1C, 3C, BIC, NO) +// BICS +DEFINE_ALU_INSN_C (1D, 3D, BICS, YES) + +// MVN +DEFINE_ALU_INSN_NC(1E, 3E, MVN, NO) +// MVNS +DEFINE_ALU_INSN_C (1F, 3F, MVNS, YES) + +// Multiply instructions ////////////////////////////////////////////////// + +// OP: OP_MUL, OP_MLA etc. +// SETCOND: SETCOND_NONE, SETCOND_MUL, or SETCOND_MULL +// CYCLES: base cycle count (1, 2, or 3) +#define MUL_INSN(OP, SETCOND, CYCLES) \ + int mult = (opcode & 0x0F); \ + u32 rs = bus.reg[(opcode >> 8) & 0x0F].I; \ + int acc = (opcode >> 12) & 0x0F; /* or destLo */ \ + int dest = (opcode >> 16) & 0x0F; /* or destHi */ \ + OP; \ + SETCOND; \ + if ((s32)rs < 0) \ + rs = ~rs; \ + if ((rs & 0xFFFF0000) == 0) \ + clockTicks += 1; \ + else if ((rs & 0xFF000000) == 0) \ + clockTicks += 2; \ + else \ + clockTicks += 3; \ + if (bus.busPrefetchCount == 0) \ + bus.busPrefetchCount = ((bus.busPrefetchCount+1)<> 32); +#define OP_MLAL(SIGN) \ + SIGN##64 res = ((SIGN##64)bus.reg[dest].I<<32 | bus.reg[acc].I)\ + + ((SIGN##64)(SIGN##32)bus.reg[mult].I \ + * (SIGN##64)(SIGN##32)rs); \ + bus.reg[acc].I = (u32)res; \ + bus.reg[dest].I = (u32)(res >> 32); +#define OP_UMULL OP_MULL(u) +#define OP_UMLAL OP_MLAL(u) +#define OP_SMULL OP_MULL(s) +#define OP_SMLAL OP_MLAL(s) + +// MUL Rd, Rm, Rs + void arm009(u32 opcode) { MUL_INSN(OP_MUL, SETCOND_NONE, 1); } +// MULS Rd, Rm, Rs + void arm019(u32 opcode) { MUL_INSN(OP_MUL, SETCOND_MUL, 1); } + +// MLA Rd, Rm, Rs, Rn + void arm029(u32 opcode) { MUL_INSN(OP_MLA, SETCOND_NONE, 2); } +// MLAS Rd, Rm, Rs, Rn + void arm039(u32 opcode) { MUL_INSN(OP_MLA, SETCOND_MUL, 2); } + +// UMULL RdLo, RdHi, Rn, Rs + void arm089(u32 opcode) { MUL_INSN(OP_UMULL, SETCOND_NONE, 2); } +// UMULLS RdLo, RdHi, Rn, Rs + void arm099(u32 opcode) { MUL_INSN(OP_UMULL, SETCOND_MULL, 2); } + +// UMLAL RdLo, RdHi, Rn, Rs + void arm0A9(u32 opcode) { MUL_INSN(OP_UMLAL, SETCOND_NONE, 3); } +// UMLALS RdLo, RdHi, Rn, Rs + void arm0B9(u32 opcode) { MUL_INSN(OP_UMLAL, SETCOND_MULL, 3); } + +// SMULL RdLo, RdHi, Rm, Rs + void arm0C9(u32 opcode) { MUL_INSN(OP_SMULL, SETCOND_NONE, 2); } +// SMULLS RdLo, RdHi, Rm, Rs + void arm0D9(u32 opcode) { MUL_INSN(OP_SMULL, SETCOND_MULL, 2); } + +// SMLAL RdLo, RdHi, Rm, Rs + void arm0E9(u32 opcode) { MUL_INSN(OP_SMLAL, SETCOND_NONE, 3); } +// SMLALS RdLo, RdHi, Rm, Rs + void arm0F9(u32 opcode) { MUL_INSN(OP_SMLAL, SETCOND_MULL, 3); } + +// Misc instructions ////////////////////////////////////////////////////// + +// SWP Rd, Rm, [Rn] + void arm109(u32 opcode) +{ + u32 address = bus.reg[(opcode >> 16) & 15].I; + u32 temp = CPUReadMemory(address); + CPUWriteMemory(address, bus.reg[opcode&15].I); + bus.reg[(opcode >> 12) & 15].I = temp; + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 4 + (dataticks_value << 1) + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// SWPB Rd, Rm, [Rn] + void arm149(u32 opcode) +{ + u32 address = bus.reg[(opcode >> 16) & 15].I; + u32 temp = CPUReadByte(address); + CPUWriteByte(address, bus.reg[opcode&15].B.B0); + bus.reg[(opcode>>12)&15].I = temp; + u32 dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 4 + (dataticks_value << 1) + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// MRS Rd, CPSR + void arm100(u32 opcode) +{ + if ((opcode & 0x0FFF0FFF) == 0x010F0000) + { + CPU_UPDATE_CPSR(); + bus.reg[(opcode >> 12) & 0x0F].I = bus.reg[16].I; + } + else + armUnknownInsn(opcode); +} + +// MRS Rd, SPSR + void arm140(u32 opcode) +{ + if ((opcode & 0x0FFF0FFF) == 0x014F0000) + bus.reg[(opcode >> 12) & 0x0F].I = bus.reg[17].I; + else + armUnknownInsn(opcode); +} + +// MSR CPSR_fields, Rm + void arm120(u32 opcode) +{ + if ((opcode & 0x0FF0FFF0) == 0x0120F000) + { + CPU_UPDATE_CPSR(); + u32 value = bus.reg[opcode & 15].I; + u32 newValue = bus.reg[16].I; + if (armMode > 0x10) { + if (opcode & 0x00010000) + newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); + } + if (opcode & 0x00080000) + newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); + newValue |= 0x10; + if(armMode != (newValue & 0x1F)) + CPUSwitchMode(newValue & 0x1F, false, true); + bus.reg[16].I = newValue; + CPUUpdateFlags(1); + if (!armState) { // this should not be allowed, but it seems to work + THUMB_PREFETCH; + bus.reg[15].I = bus.armNextPC + 2; + } + } + else + armUnknownInsn(opcode); +} + +// MSR SPSR_fields, Rm + void arm160(u32 opcode) +{ + if ((opcode & 0x0FF0FFF0) == 0x0160F000) + { + u32 value = bus.reg[opcode & 15].I; + if (armMode > 0x10 && armMode < 0x1F) + { + if (opcode & 0x00010000) + bus.reg[17].I = (bus.reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + bus.reg[17].I = (bus.reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + bus.reg[17].I = (bus.reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); + if (opcode & 0x00080000) + bus.reg[17].I = (bus.reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); + } + } + else + armUnknownInsn(opcode); +} + +// MSR CPSR_fields, # + void arm320(u32 opcode) +{ + if ((opcode & 0x0FF0F000) == 0x0320F000) + { + CPU_UPDATE_CPSR(); + u32 value = opcode & 0xFF; + int shift = (opcode & 0xF00) >> 7; + if (shift) { + ROR_IMM_MSR; + } + u32 newValue = bus.reg[16].I; + if (armMode > 0x10) { + if (opcode & 0x00010000) + newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); + } + if (opcode & 0x00080000) + newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); + + newValue |= 0x10; + + if(armMode != (newValue & 0x1F)) + CPUSwitchMode(newValue & 0x1F, false, true); + bus.reg[16].I = newValue; + CPUUpdateFlags(1); + if (!armState) { // this should not be allowed, but it seems to work + THUMB_PREFETCH; + bus.reg[15].I = bus.armNextPC + 2; + } + } + else + armUnknownInsn(opcode); +} + +// MSR SPSR_fields, # + void arm360(u32 opcode) +{ + if ((opcode & 0x0FF0F000) == 0x0360F000) { + if (armMode > 0x10 && armMode < 0x1F) { + u32 value = opcode & 0xFF; + int shift = (opcode & 0xF00) >> 7; + if (shift) { + ROR_IMM_MSR; + } + if (opcode & 0x00010000) + bus.reg[17].I = (bus.reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + bus.reg[17].I = (bus.reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + bus.reg[17].I = (bus.reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); + if (opcode & 0x00080000) + bus.reg[17].I = (bus.reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); + } + } + else + armUnknownInsn(opcode); +} + +// BX Rm + void arm121(u32 opcode) +{ + if ((opcode & 0x0FFFFFF0) == 0x012FFF10) { + int base = opcode & 0x0F; + bus.busPrefetchCount = 0; + armState = bus.reg[base].I & 1 ? false : true; + if (armState) { + bus.reg[15].I = bus.reg[base].I & 0xFFFFFFFC; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + ARM_PREFETCH; + clockTicks = 3 + (codeTicksAccessSeq32(bus.armNextPC)<<1) + + codeTicksAccess(bus.armNextPC, BITS_32); + } else { + bus.reg[15].I = bus.reg[base].I & 0xFFFFFFFE; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = 3 + (codeTicksAccessSeq16(bus.armNextPC)<<1) + + codeTicksAccess(bus.armNextPC, BITS_16); + } + } + else + armUnknownInsn(opcode); +} + +// Load/store ///////////////////////////////////////////////////////////// + +#define OFFSET_IMM \ + int offset = opcode & 0xFFF; +#define OFFSET_IMM8 \ + int offset = ((opcode & 0x0F) | ((opcode>>4) & 0xF0)); +#define OFFSET_REG \ + int offset = bus.reg[opcode & 15].I; +#define OFFSET_LSL \ + int offset = bus.reg[opcode & 15].I << ((opcode>>7) & 31); +#define OFFSET_LSR \ + int shift = (opcode >> 7) & 31; \ + int offset = shift ? bus.reg[opcode & 15].I >> shift : 0; +#define OFFSET_ASR \ + int shift = (opcode >> 7) & 31; \ + int offset; \ + if (shift) \ + offset = (int)((s32)bus.reg[opcode & 15].I >> shift);\ + else if (bus.reg[opcode & 15].I & 0x80000000) \ + offset = 0xFFFFFFFF; \ + else \ + offset = 0; +#define OFFSET_ROR \ + int shift = (opcode >> 7) & 31; \ + u32 offset = bus.reg[opcode & 15].I; \ + if (shift) { \ + ROR_OFFSET; \ + } else { \ + RRX_OFFSET; \ + } + +#define ADDRESS_POST (bus.reg[base].I) +#define ADDRESS_PREDEC (bus.reg[base].I - offset) +#define ADDRESS_PREINC (bus.reg[base].I + offset) + +#define OP_STR CPUWriteMemory(address, bus.reg[dest].I) +#define OP_STRH CPUWriteHalfWord(address, bus.reg[dest].W.W0) +#define OP_STRB CPUWriteByte(address, bus.reg[dest].B.B0) +#define OP_LDR bus.reg[dest].I = CPUReadMemory(address) +#define OP_LDRH bus.reg[dest].I = CPUReadHalfWord(address) +#define OP_LDRB bus.reg[dest].I = CPUReadByte(address) +#define OP_LDRSH bus.reg[dest].I = (s16)CPUReadHalfWordSigned(address) +#define OP_LDRSB bus.reg[dest].I = (s8)CPUReadByte(address) + +#define WRITEBACK_NONE /*nothing*/ +#define WRITEBACK_PRE bus.reg[base].I = address +#define WRITEBACK_POSTDEC bus.reg[base].I = address - offset +#define WRITEBACK_POSTINC bus.reg[base].I = address + offset + +#define LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS) \ + if (bus.busPrefetchCount == 0) \ + bus.busPrefetch = bus.busPrefetchEnable; \ + int dest = (opcode >> 12) & 15; \ + int base = (opcode >> 16) & 15; \ + CALC_OFFSET; \ + u32 address = CALC_ADDRESS; + +#define STR(CALC_OFFSET, CALC_ADDRESS, STORE_DATA, WRITEBACK1, WRITEBACK2, SIZE) \ + LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS); \ + WRITEBACK1; \ + STORE_DATA; \ + WRITEBACK2; \ + int dataticks_val; \ + if(SIZE == 32) \ + dataticks_val = DATATICKS_ACCESS_32BIT(address); \ + else \ + dataticks_val = DATATICKS_ACCESS_16BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_val); \ + clockTicks = 2 + dataticks_val + codeTicksAccess(bus.armNextPC, BITS_32); + +#define LDR(CALC_OFFSET, CALC_ADDRESS, LOAD_DATA, WRITEBACK, SIZE) \ + LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS); \ + LOAD_DATA; \ + if (dest != base) \ + { \ + WRITEBACK; \ + } \ + clockTicks = 0; \ + int dataticks_value; \ + if (dest == 15) { \ + bus.reg[15].I &= 0xFFFFFFFC; \ + bus.armNextPC = bus.reg[15].I; \ + bus.reg[15].I += 4; \ + ARM_PREFETCH; \ + dataticks_value = DATATICKS_ACCESS_32BIT_SEQ(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 2 + (dataticks_value << 1);\ + } \ + if(SIZE == 32) \ + dataticks_value = DATATICKS_ACCESS_32BIT(address); \ + else \ + dataticks_value = DATATICKS_ACCESS_16BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_32); +#define STR_POSTDEC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTDEC, SIZE) +#define STR_POSTINC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTINC, SIZE) +#define STR_PREDEC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE) +#define STR_PREDEC_WB(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE) +#define STR_PREINC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE) +#define STR_PREINC_WB(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE) +#define LDR_POSTDEC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTDEC, SIZE) +#define LDR_POSTINC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTINC, SIZE) +#define LDR_PREDEC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_NONE, SIZE) +#define LDR_PREDEC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_PRE, SIZE) +#define LDR_PREINC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_NONE, SIZE) +#define LDR_PREINC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_PRE, SIZE) + +// STRH Rd, [Rn], -Rm + void arm00B(u32 opcode) { STR_POSTDEC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn], #-offset + void arm04B(u32 opcode) { STR_POSTDEC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn], Rm + void arm08B(u32 opcode) { STR_POSTINC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn], #offset + void arm0CB(u32 opcode) { STR_POSTINC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, -Rm] + void arm10B(u32 opcode) { STR_PREDEC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, -Rm]! + void arm12B(u32 opcode) { STR_PREDEC_WB(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, -#offset] + void arm14B(u32 opcode) { STR_PREDEC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, -#offset]! + void arm16B(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, Rm] + void arm18B(u32 opcode) { STR_PREINC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, Rm]! + void arm1AB(u32 opcode) { STR_PREINC_WB(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, #offset] + void arm1CB(u32 opcode) { STR_PREINC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, #offset]! + void arm1EB(u32 opcode) { STR_PREINC_WB(OFFSET_IMM8, OP_STRH, 16); } + +// LDRH Rd, [Rn], -Rm + void arm01B(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn], #-offset + void arm05B(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn], Rm + void arm09B(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn], #offset + void arm0DB(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, -Rm] + void arm11B(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, -Rm]! + void arm13B(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, -#offset] + void arm15B(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, -#offset]! + void arm17B(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, Rm] + void arm19B(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, Rm]! + void arm1BB(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, #offset] + void arm1DB(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, #offset]! + void arm1FB(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRH, 16); } + +// LDRSB Rd, [Rn], -Rm + void arm01D(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn], #-offset + void arm05D(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn], Rm + void arm09D(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn], #offset + void arm0DD(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -Rm] + void arm11D(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -Rm]! + void arm13D(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -#offset] + void arm15D(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -#offset]! + void arm17D(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, Rm] + void arm19D(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, Rm]! + void arm1BD(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, #offset] + void arm1DD(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, #offset]! + void arm1FD(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSB, 16); } + +// LDRSH Rd, [Rn], -Rm + void arm01F(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn], #-offset + void arm05F(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn], Rm + void arm09F(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn], #offset + void arm0DF(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -Rm] + void arm11F(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -Rm]! + void arm13F(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -#offset] + void arm15F(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -#offset]! + void arm17F(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, Rm] + void arm19F(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, Rm]! + void arm1BF(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, #offset] + void arm1DF(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, #offset]! + void arm1FF(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSH, 16); } + +// STR[T] Rd, [Rn], -# +// Note: STR and STRT do the same thing on the GBA (likewise for LDR/LDRT etc) + void arm400(u32 opcode) { STR_POSTDEC(OFFSET_IMM, OP_STR, 32); } +// LDR[T] Rd, [Rn], -# + void arm410(u32 opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDR, 32); } +// STRB[T] Rd, [Rn], -# + void arm440(u32 opcode) { STR_POSTDEC(OFFSET_IMM, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], -# + void arm450(u32 opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDRB, 16); } +// STR[T] Rd, [Rn], # + void arm480(u32 opcode) { STR_POSTINC(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn], # + void arm490(u32 opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDR, 32); } +// STRB[T] Rd, [Rn], # + void arm4C0(u32 opcode) { STR_POSTINC(OFFSET_IMM, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], # + void arm4D0(u32 opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDRB, 16); } +// STR Rd, [Rn, -#] + void arm500(u32 opcode) { STR_PREDEC(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, -#] + void arm510(u32 opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDR, 32); } +// STR Rd, [Rn, -#]! + void arm520(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, -#]! + void arm530(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDR, 32); } +// STRB Rd, [Rn, -#] + void arm540(u32 opcode) { STR_PREDEC(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, -#] + void arm550(u32 opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDRB, 16); } +// STRB Rd, [Rn, -#]! + void arm560(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, -#]! + void arm570(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDRB, 16); } +// STR Rd, [Rn, #] + void arm580(u32 opcode) { STR_PREINC(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, #] + void arm590(u32 opcode) { LDR_PREINC(OFFSET_IMM, OP_LDR, 32); } +// STR Rd, [Rn, #]! + void arm5A0(u32 opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, #]! + void arm5B0(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDR, 32); } +// STRB Rd, [Rn, #] + void arm5C0(u32 opcode) { STR_PREINC(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, #] + void arm5D0(u32 opcode) { LDR_PREINC(OFFSET_IMM, OP_LDRB, 16); } +// STRB Rd, [Rn, #]! + void arm5E0(u32 opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, #]! + void arm5F0(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDRB, 16); } + +// STR[T] Rd, [Rn], -Rm, LSL # + void arm600(u32 opcode) { STR_POSTDEC(OFFSET_LSL, OP_STR, 32); } +// STR[T] Rd, [Rn], -Rm, LSR # + void arm602(u32 opcode) { STR_POSTDEC(OFFSET_LSR, OP_STR, 32); } +// STR[T] Rd, [Rn], -Rm, ASR # + void arm604(u32 opcode) { STR_POSTDEC(OFFSET_ASR, OP_STR, 32); } +// STR[T] Rd, [Rn], -Rm, ROR # + void arm606(u32 opcode) { STR_POSTDEC(OFFSET_ROR, OP_STR, 32); } +// LDR[T] Rd, [Rn], -Rm, LSL # + void arm610(u32 opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDR, 32); } +// LDR[T] Rd, [Rn], -Rm, LSR # + void arm612(u32 opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], -Rm, ASR # + void arm614(u32 opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], -Rm, ROR # + void arm616(u32 opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDR, 32); } +// STRB[T] Rd, [Rn], -Rm, LSL # + void arm640(u32 opcode) { STR_POSTDEC(OFFSET_LSL, OP_STRB, 16); } +// STRB[T] Rd, [Rn], -Rm, LSR # + void arm642(u32 opcode) { STR_POSTDEC(OFFSET_LSR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], -Rm, ASR # + void arm644(u32 opcode) { STR_POSTDEC(OFFSET_ASR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], -Rm, ROR # + void arm646(u32 opcode) { STR_POSTDEC(OFFSET_ROR, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], -Rm, LSL # + void arm650(u32 opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], -Rm, LSR # + void arm652(u32 opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], -Rm, ASR # + void arm654(u32 opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn], -Rm, ROR # + void arm656(u32 opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDRB, 16); } +// STR[T] Rd, [Rn], Rm, LSL # + void arm680(u32 opcode) { STR_POSTINC(OFFSET_LSL, OP_STR, 32); } +// STR[T] Rd, [Rn], Rm, LSR # + void arm682(u32 opcode) { STR_POSTINC(OFFSET_LSR, OP_STR, 32); } +// STR[T] Rd, [Rn], Rm, ASR # + void arm684(u32 opcode) { STR_POSTINC(OFFSET_ASR, OP_STR, 32); } +// STR[T] Rd, [Rn], Rm, ROR # + void arm686(u32 opcode) { STR_POSTINC(OFFSET_ROR, OP_STR, 32); } +// LDR[T] Rd, [Rn], Rm, LSL # + void arm690(u32 opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDR, 32); } +// LDR[T] Rd, [Rn], Rm, LSR # + void arm692(u32 opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], Rm, ASR # + void arm694(u32 opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], Rm, ROR # + void arm696(u32 opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDR, 32); } +// STRB[T] Rd, [Rn], Rm, LSL # + void arm6C0(u32 opcode) { STR_POSTINC(OFFSET_LSL, OP_STRB, 16); } +// STRB[T] Rd, [Rn], Rm, LSR # + void arm6C2(u32 opcode) { STR_POSTINC(OFFSET_LSR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], Rm, ASR # + void arm6C4(u32 opcode) { STR_POSTINC(OFFSET_ASR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], Rm, ROR # + void arm6C6(u32 opcode) { STR_POSTINC(OFFSET_ROR, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], Rm, LSL # + void arm6D0(u32 opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], Rm, LSR # + void arm6D2(u32 opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], Rm, ASR # + void arm6D4(u32 opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], Rm, ROR # + void arm6D6(u32 opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDRB, 16); } +// STR Rd, [Rn, -Rm, LSL #] + void arm700(u32 opcode) { STR_PREDEC(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, -Rm, LSR #] + void arm702(u32 opcode) { STR_PREDEC(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ASR #] + void arm704(u32 opcode) { STR_PREDEC(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ROR #] + void arm706(u32 opcode) { STR_PREDEC(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, -Rm, LSL #] + void arm710(u32 opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, LSR #] + void arm712(u32 opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ASR #] + void arm714(u32 opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ROR #] + void arm716(u32 opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDR, 32); } +// STR Rd, [Rn, -Rm, LSL #]! + void arm720(u32 opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, -Rm, LSR #]! + void arm722(u32 opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ASR #]! + void arm724(u32 opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ROR #]! + void arm726(u32 opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, -Rm, LSL #]! + void arm730(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, LSR #]! + void arm732(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ASR #]! + void arm734(u32 opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ROR #]! + void arm736(u32 opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDR, 32); } +// STRB Rd, [Rn, -Rm, LSL #] + void arm740(u32 opcode) { STR_PREDEC(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, LSR #] + void arm742(u32 opcode) { STR_PREDEC(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ASR #] + void arm744(u32 opcode) { STR_PREDEC(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ROR #] + void arm746(u32 opcode) { STR_PREDEC(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, -Rm, LSL #] + void arm750(u32 opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, LSR #] + void arm752(u32 opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ASR #] + void arm754(u32 opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ROR #] + void arm756(u32 opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDRB, 16); } +// STRB Rd, [Rn, -Rm, LSL #]! + void arm760(u32 opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, LSR #]! + void arm762(u32 opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ASR #]! + void arm764(u32 opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ROR #]! + void arm766(u32 opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, -Rm, LSL #]! + void arm770(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, LSR #]! + void arm772(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ASR #]! + void arm774(u32 opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ROR #]! + void arm776(u32 opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDRB, 16); } +// STR Rd, [Rn, Rm, LSL #] + void arm780(u32 opcode) { STR_PREINC(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, Rm, LSR #] + void arm782(u32 opcode) { STR_PREINC(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ASR #] + void arm784(u32 opcode) { STR_PREINC(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ROR #] + void arm786(u32 opcode) { STR_PREINC(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, Rm, LSL #] + void arm790(u32 opcode) { LDR_PREINC(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, LSR #] + void arm792(u32 opcode) { LDR_PREINC(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ASR #] + void arm794(u32 opcode) { LDR_PREINC(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ROR #] + void arm796(u32 opcode) { LDR_PREINC(OFFSET_ROR, OP_LDR, 32); } +// STR Rd, [Rn, Rm, LSL #]! + void arm7A0(u32 opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, Rm, LSR #]! + void arm7A2(u32 opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ASR #]! + void arm7A4(u32 opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ROR #]! + void arm7A6(u32 opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, Rm, LSL #]! + void arm7B0(u32 opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, LSR #]! + void arm7B2(u32 opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ASR #]! + void arm7B4(u32 opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ROR #]! + void arm7B6(u32 opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDR, 32); } +// STRB Rd, [Rn, Rm, LSL #] + void arm7C0(u32 opcode) { STR_PREINC(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, LSR #] + void arm7C2(u32 opcode) { STR_PREINC(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ASR #] + void arm7C4(u32 opcode) { STR_PREINC(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ROR #] + void arm7C6(u32 opcode) { STR_PREINC(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, Rm, LSL #] + void arm7D0(u32 opcode) { LDR_PREINC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, LSR #] + void arm7D2(u32 opcode) { LDR_PREINC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ASR #] + void arm7D4(u32 opcode) { LDR_PREINC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ROR #] + void arm7D6(u32 opcode) { LDR_PREINC(OFFSET_ROR, OP_LDRB, 16); } +// STRB Rd, [Rn, Rm, LSL #]! + void arm7E0(u32 opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, LSR #]! + void arm7E2(u32 opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ASR #]! + void arm7E4(u32 opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ROR #]! + void arm7E6(u32 opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, Rm, LSL #]! + void arm7F0(u32 opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, LSR #]! + void arm7F2(u32 opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ASR #]! + void arm7F4(u32 opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ROR #]! + void arm7F6(u32 opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDRB, 16); } + +// STM/LDM //////////////////////////////////////////////////////////////// + +#define STM_REG(bit,num) \ + if (opcode & (1U<<(bit))) { \ + CPUWriteMemory(address, bus.reg[(num)].I); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + address += 4; \ + } +#define STMW_REG(bit,num) \ + if (opcode & (1U<<(bit))) { \ + CPUWriteMemory(address, bus.reg[(num)].I); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + bus.reg[base].I = temp; \ + count++; \ + address += 4; \ + } +#define LDM_REG(bit,num) \ + if (opcode & (1U<<(bit))) { \ + int dataticks_value; \ + bus.reg[(num)].I = CPUReadMemory(address); \ + dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + address += 4; \ + } +#define STM_LOW(STORE_REG) \ + STORE_REG(0, 0); \ + STORE_REG(1, 1); \ + STORE_REG(2, 2); \ + STORE_REG(3, 3); \ + STORE_REG(4, 4); \ + STORE_REG(5, 5); \ + STORE_REG(6, 6); \ + STORE_REG(7, 7); +#define STM_HIGH(STORE_REG) \ + STORE_REG(8, 8); \ + STORE_REG(9, 9); \ + STORE_REG(10, 10); \ + STORE_REG(11, 11); \ + STORE_REG(12, 12); \ + STORE_REG(13, 13); \ + STORE_REG(14, 14); +#define STM_HIGH_2(STORE_REG) \ + if (armMode == 0x11) { \ + STORE_REG(8, R8_FIQ); \ + STORE_REG(9, R9_FIQ); \ + STORE_REG(10, R10_FIQ); \ + STORE_REG(11, R11_FIQ); \ + STORE_REG(12, R12_FIQ); \ + } else { \ + STORE_REG(8, 8); \ + STORE_REG(9, 9); \ + STORE_REG(10, 10); \ + STORE_REG(11, 11); \ + STORE_REG(12, 12); \ + } \ + if (armMode != 0x10 && armMode != 0x1F) { \ + STORE_REG(13, R13_USR); \ + STORE_REG(14, R14_USR); \ + } else { \ + STORE_REG(13, 13); \ + STORE_REG(14, 14); \ + } +#define STM_PC \ + if (opcode & (1U<<15)) { \ + CPUWriteMemory(address, bus.reg[15].I+4); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + } +#define STMW_PC \ + if (opcode & (1U<<15)) { \ + CPUWriteMemory(address, bus.reg[15].I+4); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + bus.reg[base].I = temp; \ + count++; \ + } +#define LDM_LOW \ + LDM_REG(0, 0); \ + LDM_REG(1, 1); \ + LDM_REG(2, 2); \ + LDM_REG(3, 3); \ + LDM_REG(4, 4); \ + LDM_REG(5, 5); \ + LDM_REG(6, 6); \ + LDM_REG(7, 7); +#define LDM_HIGH \ + LDM_REG(8, 8); \ + LDM_REG(9, 9); \ + LDM_REG(10, 10); \ + LDM_REG(11, 11); \ + LDM_REG(12, 12); \ + LDM_REG(13, 13); \ + LDM_REG(14, 14); +#define LDM_HIGH_2 \ + if (armMode == 0x11) { \ + LDM_REG(8, R8_FIQ); \ + LDM_REG(9, R9_FIQ); \ + LDM_REG(10, R10_FIQ); \ + LDM_REG(11, R11_FIQ); \ + LDM_REG(12, R12_FIQ); \ + } else { \ + LDM_REG(8, 8); \ + LDM_REG(9, 9); \ + LDM_REG(10, 10); \ + LDM_REG(11, 11); \ + LDM_REG(12, 12); \ + } \ + if (armMode != 0x10 && armMode != 0x1F) { \ + LDM_REG(13, R13_USR); \ + LDM_REG(14, R14_USR); \ + } else { \ + LDM_REG(13, 13); \ + LDM_REG(14, 14); \ + } +#define STM_ALL \ + STM_LOW(STM_REG); \ + STM_HIGH(STM_REG); \ + STM_PC; +#define STMW_ALL \ + STM_LOW(STMW_REG); \ + STM_HIGH(STMW_REG); \ + STMW_PC; +#define LDM_ALL \ + LDM_LOW; \ + LDM_HIGH; \ + if (opcode & (1U<<15)) { \ + bus.reg[15].I = CPUReadMemory(address); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + } \ + if (opcode & (1U<<15)) { \ + bus.armNextPC = bus.reg[15].I; \ + bus.reg[15].I += 4; \ + ARM_PREFETCH; \ + clockTicks += 1 + codeTicksAccessSeq32(bus.armNextPC);\ + } +#define STM_ALL_2 \ + STM_LOW(STM_REG); \ + STM_HIGH_2(STM_REG); \ + STM_PC; +#define STMW_ALL_2 \ + STM_LOW(STMW_REG); \ + STM_HIGH_2(STMW_REG); \ + STMW_PC; +#define LDM_ALL_2 \ + LDM_LOW; \ + if (opcode & (1U<<15)) { \ + LDM_HIGH; \ + bus.reg[15].I = CPUReadMemory(address); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + } else { \ + LDM_HIGH_2; \ + } +#define LDM_ALL_2B \ + if (opcode & (1U<<15)) { \ + if(armMode != (bus.reg[17].I & 0x1F)) \ + CPUSwitchMode(bus.reg[17].I & 0x1F, false, true); \ + if (armState) { \ + bus.armNextPC = bus.reg[15].I & 0xFFFFFFFC; \ + bus.reg[15].I = bus.armNextPC + 4; \ + ARM_PREFETCH; \ + } else { \ + bus.armNextPC = bus.reg[15].I & 0xFFFFFFFE; \ + bus.reg[15].I = bus.armNextPC + 2; \ + THUMB_PREFETCH; \ + } \ + clockTicks += 1 + codeTicksAccessSeq32(bus.armNextPC);\ + } + + +// STMDA Rn, {Rlist} + void arm800(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDA Rn, {Rlist} + void arm810(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMDA Rn!, {Rlist} + void arm820(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + int count = 0; + STMW_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDA Rn!, {Rlist} + void arm830(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; +} + +// STMDA Rn, {Rlist}^ + void arm840(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDA Rn, {Rlist}^ + void arm850(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMDA Rn!, {Rlist}^ + void arm860(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + int count = 0; + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDA Rn!, {Rlist}^ + void arm870(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMIA Rn, {Rlist} + void arm880(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIA Rn, {Rlist} + void arm890(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMIA Rn!, {Rlist} + void arm8A0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIA Rn!, {Rlist} + void arm8B0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; +} + +// STMIA Rn, {Rlist}^ + void arm8C0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIA Rn, {Rlist}^ + void arm8D0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMIA Rn!, {Rlist}^ + void arm8E0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIA Rn!, {Rlist}^ + void arm8F0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMDB Rn, {Rlist} + void arm900(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDB Rn, {Rlist} + void arm910(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMDB Rn!, {Rlist} + void arm920(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STMW_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDB Rn!, {Rlist} + void arm930(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; +} + +// STMDB Rn, {Rlist}^ + void arm940(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDB Rn, {Rlist}^ + void arm950(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMDB Rn!, {Rlist}^ + void arm960(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDB Rn!, {Rlist}^ + void arm970(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMIB Rn, {Rlist} + void arm980(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIB Rn, {Rlist} + void arm990(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMIB Rn!, {Rlist} + void arm9A0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIB Rn!, {Rlist} + void arm9B0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; +} + +// STMIB Rn, {Rlist}^ + void arm9C0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIB Rn, {Rlist}^ + void arm9D0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMIB Rn!, {Rlist}^ + void arm9E0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIB Rn!, {Rlist}^ + void arm9F0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// B/BL/SWI and (unimplemented) coproc support //////////////////////////// + +// B + void armA00(u32 opcode) +{ + int codeTicksVal = 0; + int ct = 0; + int offset = opcode & 0x00FFFFFF; + if (offset & 0x00800000) + offset |= 0xFF000000; // negative offset + bus.reg[15].I += offset<<2; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + ARM_PREFETCH; + + codeTicksVal = codeTicksAccessSeq32(bus.armNextPC); + ct = codeTicksVal + 3; + ct += 2 + codeTicksAccess(bus.armNextPC, BITS_32) + codeTicksVal; + + bus.busPrefetchCount = 0; + clockTicks = ct; +} + +// BL + void armB00(u32 opcode) +{ + int codeTicksVal = 0; + int ct = 0; + + int offset = opcode & 0x00FFFFFF; + if (offset & 0x00800000) + offset |= 0xFF000000; // negative offset + bus.reg[14].I = bus.reg[15].I - 4; + bus.reg[15].I += offset<<2; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + ARM_PREFETCH; + + codeTicksVal = codeTicksAccessSeq32(bus.armNextPC); + ct = codeTicksVal + 3; + ct += 2 + codeTicksAccess(bus.armNextPC, BITS_32) + codeTicksVal; + + bus.busPrefetchCount = 0; + clockTicks = ct; +} + +#define armE01 armUnknownInsn + +// SWI + void armF00(u32 opcode) +{ + int codeTicksVal = 0; + int ct = 0; + + codeTicksVal = codeTicksAccessSeq32(bus.armNextPC); + ct = codeTicksVal + 3; + ct += 2 + codeTicksAccess(bus.armNextPC, BITS_32) + codeTicksVal; + + bus.busPrefetchCount = 0; + + clockTicks = ct; + CPUSoftwareInterrupt(opcode & 0x00FFFFFF); + +} + +// Instruction table ////////////////////////////////////////////////////// + +static void (Gigazoid::*const armInsnTable[4096])(u32 opcode); + +// Wrapper routine (execution loop) /////////////////////////////////////// +int armExecute (void) +{ + CACHE_PREFETCH(clockTicks); + + u32 cond1; + u32 cond2; + + int ct = 0; + + do + { + + clockTicks = 0; + + if ((bus.armNextPC & 0x0803FFFF) == 0x08020000) + bus.busPrefetchCount = 0x100; + + u32 opcode = cpuPrefetch[0]; + cpuPrefetch[0] = cpuPrefetch[1]; + + bus.busPrefetch = false; + int32_t busprefetch_mask = ((bus.busPrefetchCount & 0xFFFFFE00) | -(bus.busPrefetchCount & 0xFFFFFE00)) >> 31; + bus.busPrefetchCount = (0x100 | (bus.busPrefetchCount & 0xFF) & busprefetch_mask) | (bus.busPrefetchCount & ~busprefetch_mask); +#if 0 + if (bus.busPrefetchCount & 0xFFFFFE00) + bus.busPrefetchCount = 0x100 | (bus.busPrefetchCount & 0xFF); +#endif + + + int oldArmNextPC = bus.armNextPC; + + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + ARM_PREFETCH_NEXT; + + int cond = opcode >> 28; + bool cond_res = true; + if (cond != 0x0E) { // most opcodes are AL (always) + switch(cond) { + case 0x00: // EQ + cond_res = Z_FLAG; + break; + case 0x01: // NE + cond_res = !Z_FLAG; + break; + case 0x02: // CS + cond_res = C_FLAG; + break; + case 0x03: // CC + cond_res = !C_FLAG; + break; + case 0x04: // MI + cond_res = N_FLAG; + break; + case 0x05: // PL + cond_res = !N_FLAG; + break; + case 0x06: // VS + cond_res = V_FLAG; + break; + case 0x07: // VC + cond_res = !V_FLAG; + break; + case 0x08: // HI + cond_res = C_FLAG && !Z_FLAG; + break; + case 0x09: // LS + cond_res = !C_FLAG || Z_FLAG; + break; + case 0x0A: // GE + cond_res = N_FLAG == V_FLAG; + break; + case 0x0B: // LT + cond_res = N_FLAG != V_FLAG; + break; + case 0x0C: // GT + cond_res = !Z_FLAG &&(N_FLAG == V_FLAG); + break; + case 0x0D: // LE + cond_res = Z_FLAG || (N_FLAG != V_FLAG); + break; + case 0x0E: // AL (impossible, checked above) + cond_res = true; + break; + case 0x0F: + default: + // ??? + cond_res = false; + break; + } + } + + if (cond_res) + { + cond1 = (opcode>>16)&0xFF0; + cond2 = (opcode>>4)&0x0F; + + (this->*armInsnTable[(cond1| cond2)])(opcode); + + } + ct = clockTicks; + + if (ct < 0) + return 0; + + /// better pipelining + + if (ct == 0) + clockTicks = 1 + codeTicksAccessSeq32(oldArmNextPC); + + cpuTotalTicks += clockTicks; + +#ifdef USE_SWITICKS + } while (cpuTotalTicks> 31) +#define POS(i) ((~(i)) >> 31) + +// C core +#ifndef ADDCARRY + #define ADDCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & NEG(b)) |\ + (NEG(a) & POS(c)) |\ + (NEG(b) & POS(c))) ? true : false; +#endif + +#ifndef ADDOVERFLOW + #define ADDOVERFLOW(a, b, c) \ + V_FLAG = ((NEG(a) & NEG(b) & POS(c)) |\ + (POS(a) & POS(b) & NEG(c))) ? true : false; +#endif + +#ifndef SUBCARRY + #define SUBCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & POS(b)) |\ + (NEG(a) & POS(c)) |\ + (POS(b) & POS(c))) ? true : false; +#endif + +#ifndef SUBOVERFLOW + #define SUBOVERFLOW(a, b, c)\ + V_FLAG = ((NEG(a) & POS(b) & POS(c)) |\ + (POS(a) & NEG(b) & NEG(c))) ? true : false; +#endif + +#ifndef ADD_RD_RS_RN + #define ADD_RD_RS_RN(N) \ + {\ + u32 lhs = bus.reg[source].I;\ + u32 rhs = bus.reg[N].I;\ + u32 res = lhs + rhs;\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef ADD_RD_RS_O3 + #define ADD_RD_RS_O3(N) \ + {\ + u32 lhs = bus.reg[source].I;\ + u32 rhs = N;\ + u32 res = lhs + rhs;\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef ADD_RD_RS_O3_0 +# define ADD_RD_RS_O3_0 ADD_RD_RS_O3 +#endif + +#ifndef ADD_RN_O8 + #define ADD_RN_O8(d) \ + {\ + u32 lhs = bus.reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs + rhs;\ + bus.reg[(d)].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef CMN_RD_RS + #define CMN_RD_RS \ + {\ + u32 lhs = bus.reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef ADC_RD_RS + #define ADC_RD_RS \ + {\ + u32 lhs = bus.reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs + (u32)C_FLAG;\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef SUB_RD_RS_RN + #define SUB_RD_RS_RN(N) \ + {\ + u32 lhs = bus.reg[source].I;\ + u32 rhs = bus.reg[N].I;\ + u32 res = lhs - rhs;\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef SUB_RD_RS_O3 + #define SUB_RD_RS_O3(N) \ + {\ + u32 lhs = bus.reg[source].I;\ + u32 rhs = N;\ + u32 res = lhs - rhs;\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef SUB_RD_RS_O3_0 +# define SUB_RD_RS_O3_0 SUB_RD_RS_O3 +#endif +#ifndef SUB_RN_O8 + #define SUB_RN_O8(d) \ + {\ + u32 lhs = bus.reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs - rhs;\ + bus.reg[(d)].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef MOV_RN_O8 + #define MOV_RN_O8(d) \ + {\ + u32 val;\ + val = (opcode & 255);\ + bus.reg[d].I = val;\ + N_FLAG = false;\ + Z_FLAG = (val ? false : true);\ + } +#endif +#ifndef CMP_RN_O8 + #define CMP_RN_O8(d) \ + {\ + u32 lhs = bus.reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs - rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef SBC_RD_RS + #define SBC_RD_RS \ + {\ + u32 lhs = bus.reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs - !((u32)C_FLAG);\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef LSL_RD_RM_I5 + #define LSL_RD_RM_I5 \ + {\ + C_FLAG = (bus.reg[source].I >> (32 - shift)) & 1 ? true : false;\ + value = bus.reg[source].I << shift;\ + } +#endif +#ifndef LSL_RD_RS + #define LSL_RD_RS \ + {\ + C_FLAG = (bus.reg[dest].I >> (32 - value)) & 1 ? true : false;\ + value = bus.reg[dest].I << value;\ + } +#endif +#ifndef LSR_RD_RM_I5 + #define LSR_RD_RM_I5 \ + {\ + C_FLAG = (bus.reg[source].I >> (shift - 1)) & 1 ? true : false;\ + value = bus.reg[source].I >> shift;\ + } +#endif +#ifndef LSR_RD_RS + #define LSR_RD_RS \ + {\ + C_FLAG = (bus.reg[dest].I >> (value - 1)) & 1 ? true : false;\ + value = bus.reg[dest].I >> value;\ + } +#endif +#ifndef ASR_RD_RM_I5 + #define ASR_RD_RM_I5 \ + {\ + C_FLAG = ((s32)bus.reg[source].I >> (int)(shift - 1)) & 1 ? true : false;\ + value = (s32)bus.reg[source].I >> (int)shift;\ + } +#endif +#ifndef ASR_RD_RS + #define ASR_RD_RS \ + {\ + C_FLAG = ((s32)bus.reg[dest].I >> (int)(value - 1)) & 1 ? true : false;\ + value = (s32)bus.reg[dest].I >> (int)value;\ + } +#endif +#ifndef ROR_RD_RS + #define ROR_RD_RS \ + {\ + C_FLAG = (bus.reg[dest].I >> (value - 1)) & 1 ? true : false;\ + value = ((bus.reg[dest].I << (32 - value)) |\ + (bus.reg[dest].I >> value));\ + } +#endif +#ifndef NEG_RD_RS + #define NEG_RD_RS \ + {\ + u32 lhs = bus.reg[source].I;\ + u32 rhs = 0;\ + u32 res = rhs - lhs;\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(rhs, lhs, res);\ + SUBOVERFLOW(rhs, lhs, res);\ + } +#endif +#ifndef CMP_RD_RS + #define CMP_RD_RS \ + {\ + u32 lhs = bus.reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef IMM5_INSN + #define IMM5_INSN(OP,N) \ + int dest = opcode & 0x07;\ + int source = (opcode >> 3) & 0x07;\ + u32 value;\ + OP(N);\ + bus.reg[dest].I = value;\ + N_FLAG = (value & 0x80000000 ? true : false);\ + Z_FLAG = (value ? false : true); + #define IMM5_INSN_0(OP) \ + int dest = opcode & 0x07;\ + int source = (opcode >> 3) & 0x07;\ + u32 value;\ + OP;\ + bus.reg[dest].I = value;\ + N_FLAG = (value & 0x80000000 ? true : false);\ + Z_FLAG = (value ? false : true); + #define IMM5_LSL(N) \ + int shift = N;\ + LSL_RD_RM_I5; + #define IMM5_LSL_0 \ + value = bus.reg[source].I; + #define IMM5_LSR(N) \ + int shift = N;\ + LSR_RD_RM_I5; + #define IMM5_LSR_0 \ + C_FLAG = bus.reg[source].I & 0x80000000 ? true : false;\ + value = 0; + #define IMM5_ASR(N) \ + int shift = N;\ + ASR_RD_RM_I5; + #define IMM5_ASR_0 \ + if(bus.reg[source].I & 0x80000000) {\ + value = 0xFFFFFFFF;\ + C_FLAG = true;\ + } else {\ + value = 0;\ + C_FLAG = false;\ + } +#endif +#ifndef THREEARG_INSN + #define THREEARG_INSN(OP,N) \ + int dest = opcode & 0x07; \ + int source = (opcode >> 3) & 0x07; \ + OP(N); +#endif + +// Shift instructions ///////////////////////////////////////////////////// + +#define DEFINE_IMM5_INSN(OP,BASE) \ + void thumb##BASE##_00(u32 opcode) { IMM5_INSN_0(OP##_0); } \ + void thumb##BASE##_01(u32 opcode) { IMM5_INSN(OP, 1); } \ + void thumb##BASE##_02(u32 opcode) { IMM5_INSN(OP, 2); } \ + void thumb##BASE##_03(u32 opcode) { IMM5_INSN(OP, 3); } \ + void thumb##BASE##_04(u32 opcode) { IMM5_INSN(OP, 4); } \ + void thumb##BASE##_05(u32 opcode) { IMM5_INSN(OP, 5); } \ + void thumb##BASE##_06(u32 opcode) { IMM5_INSN(OP, 6); } \ + void thumb##BASE##_07(u32 opcode) { IMM5_INSN(OP, 7); } \ + void thumb##BASE##_08(u32 opcode) { IMM5_INSN(OP, 8); } \ + void thumb##BASE##_09(u32 opcode) { IMM5_INSN(OP, 9); } \ + void thumb##BASE##_0A(u32 opcode) { IMM5_INSN(OP,10); } \ + void thumb##BASE##_0B(u32 opcode) { IMM5_INSN(OP,11); } \ + void thumb##BASE##_0C(u32 opcode) { IMM5_INSN(OP,12); } \ + void thumb##BASE##_0D(u32 opcode) { IMM5_INSN(OP,13); } \ + void thumb##BASE##_0E(u32 opcode) { IMM5_INSN(OP,14); } \ + void thumb##BASE##_0F(u32 opcode) { IMM5_INSN(OP,15); } \ + void thumb##BASE##_10(u32 opcode) { IMM5_INSN(OP,16); } \ + void thumb##BASE##_11(u32 opcode) { IMM5_INSN(OP,17); } \ + void thumb##BASE##_12(u32 opcode) { IMM5_INSN(OP,18); } \ + void thumb##BASE##_13(u32 opcode) { IMM5_INSN(OP,19); } \ + void thumb##BASE##_14(u32 opcode) { IMM5_INSN(OP,20); } \ + void thumb##BASE##_15(u32 opcode) { IMM5_INSN(OP,21); } \ + void thumb##BASE##_16(u32 opcode) { IMM5_INSN(OP,22); } \ + void thumb##BASE##_17(u32 opcode) { IMM5_INSN(OP,23); } \ + void thumb##BASE##_18(u32 opcode) { IMM5_INSN(OP,24); } \ + void thumb##BASE##_19(u32 opcode) { IMM5_INSN(OP,25); } \ + void thumb##BASE##_1A(u32 opcode) { IMM5_INSN(OP,26); } \ + void thumb##BASE##_1B(u32 opcode) { IMM5_INSN(OP,27); } \ + void thumb##BASE##_1C(u32 opcode) { IMM5_INSN(OP,28); } \ + void thumb##BASE##_1D(u32 opcode) { IMM5_INSN(OP,29); } \ + void thumb##BASE##_1E(u32 opcode) { IMM5_INSN(OP,30); } \ + void thumb##BASE##_1F(u32 opcode) { IMM5_INSN(OP,31); } + +// LSL Rd, Rm, #Imm 5 +DEFINE_IMM5_INSN(IMM5_LSL,00) +// LSR Rd, Rm, #Imm 5 +DEFINE_IMM5_INSN(IMM5_LSR,08) +// ASR Rd, Rm, #Imm 5 +DEFINE_IMM5_INSN(IMM5_ASR,10) + +// 3-argument ADD/SUB ///////////////////////////////////////////////////// + +#define DEFINE_REG3_INSN(OP,BASE) \ + void thumb##BASE##_0(u32 opcode) { THREEARG_INSN(OP,0); } \ + void thumb##BASE##_1(u32 opcode) { THREEARG_INSN(OP,1); } \ + void thumb##BASE##_2(u32 opcode) { THREEARG_INSN(OP,2); } \ + void thumb##BASE##_3(u32 opcode) { THREEARG_INSN(OP,3); } \ + void thumb##BASE##_4(u32 opcode) { THREEARG_INSN(OP,4); } \ + void thumb##BASE##_5(u32 opcode) { THREEARG_INSN(OP,5); } \ + void thumb##BASE##_6(u32 opcode) { THREEARG_INSN(OP,6); } \ + void thumb##BASE##_7(u32 opcode) { THREEARG_INSN(OP,7); } + +#define DEFINE_IMM3_INSN(OP,BASE) \ + void thumb##BASE##_0(u32 opcode) { THREEARG_INSN(OP##_0,0); } \ + void thumb##BASE##_1(u32 opcode) { THREEARG_INSN(OP,1); } \ + void thumb##BASE##_2(u32 opcode) { THREEARG_INSN(OP,2); } \ + void thumb##BASE##_3(u32 opcode) { THREEARG_INSN(OP,3); } \ + void thumb##BASE##_4(u32 opcode) { THREEARG_INSN(OP,4); } \ + void thumb##BASE##_5(u32 opcode) { THREEARG_INSN(OP,5); } \ + void thumb##BASE##_6(u32 opcode) { THREEARG_INSN(OP,6); } \ + void thumb##BASE##_7(u32 opcode) { THREEARG_INSN(OP,7); } + +// ADD Rd, Rs, Rn +DEFINE_REG3_INSN(ADD_RD_RS_RN,18) +// SUB Rd, Rs, Rn +DEFINE_REG3_INSN(SUB_RD_RS_RN,1A) +// ADD Rd, Rs, #Offset3 +DEFINE_IMM3_INSN(ADD_RD_RS_O3,1C) +// SUB Rd, Rs, #Offset3 +DEFINE_IMM3_INSN(SUB_RD_RS_O3,1E) + +// MOV/CMP/ADD/SUB immediate ////////////////////////////////////////////// + +// MOV R0, #Offset8 + void thumb20(u32 opcode) { MOV_RN_O8(0); } +// MOV R1, #Offset8 + void thumb21(u32 opcode) { MOV_RN_O8(1); } +// MOV R2, #Offset8 + void thumb22(u32 opcode) { MOV_RN_O8(2); } +// MOV R3, #Offset8 + void thumb23(u32 opcode) { MOV_RN_O8(3); } +// MOV R4, #Offset8 + void thumb24(u32 opcode) { MOV_RN_O8(4); } +// MOV R5, #Offset8 + void thumb25(u32 opcode) { MOV_RN_O8(5); } +// MOV R6, #Offset8 + void thumb26(u32 opcode) { MOV_RN_O8(6); } +// MOV R7, #Offset8 + void thumb27(u32 opcode) { MOV_RN_O8(7); } + +// CMP R0, #Offset8 + void thumb28(u32 opcode) { CMP_RN_O8(0); } +// CMP R1, #Offset8 + void thumb29(u32 opcode) { CMP_RN_O8(1); } +// CMP R2, #Offset8 + void thumb2A(u32 opcode) { CMP_RN_O8(2); } +// CMP R3, #Offset8 + void thumb2B(u32 opcode) { CMP_RN_O8(3); } +// CMP R4, #Offset8 + void thumb2C(u32 opcode) { CMP_RN_O8(4); } +// CMP R5, #Offset8 + void thumb2D(u32 opcode) { CMP_RN_O8(5); } +// CMP R6, #Offset8 + void thumb2E(u32 opcode) { CMP_RN_O8(6); } +// CMP R7, #Offset8 + void thumb2F(u32 opcode) { CMP_RN_O8(7); } + +// ADD R0,#Offset8 + void thumb30(u32 opcode) { ADD_RN_O8(0); } +// ADD R1,#Offset8 + void thumb31(u32 opcode) { ADD_RN_O8(1); } +// ADD R2,#Offset8 + void thumb32(u32 opcode) { ADD_RN_O8(2); } +// ADD R3,#Offset8 + void thumb33(u32 opcode) { ADD_RN_O8(3); } +// ADD R4,#Offset8 + void thumb34(u32 opcode) { ADD_RN_O8(4); } +// ADD R5,#Offset8 + void thumb35(u32 opcode) { ADD_RN_O8(5); } +// ADD R6,#Offset8 + void thumb36(u32 opcode) { ADD_RN_O8(6); } +// ADD R7,#Offset8 + void thumb37(u32 opcode) { ADD_RN_O8(7); } + +// SUB R0,#Offset8 + void thumb38(u32 opcode) { SUB_RN_O8(0); } +// SUB R1,#Offset8 + void thumb39(u32 opcode) { SUB_RN_O8(1); } +// SUB R2,#Offset8 + void thumb3A(u32 opcode) { SUB_RN_O8(2); } +// SUB R3,#Offset8 + void thumb3B(u32 opcode) { SUB_RN_O8(3); } +// SUB R4,#Offset8 + void thumb3C(u32 opcode) { SUB_RN_O8(4); } +// SUB R5,#Offset8 + void thumb3D(u32 opcode) { SUB_RN_O8(5); } +// SUB R6,#Offset8 + void thumb3E(u32 opcode) { SUB_RN_O8(6); } +// SUB R7,#Offset8 + void thumb3F(u32 opcode) { SUB_RN_O8(7); } + +// ALU operations ///////////////////////////////////////////////////////// + +// AND Rd, Rs + void thumb40_0(u32 opcode) +{ + int dest = opcode & 7; + u32 val = (bus.reg[dest].I & bus.reg[(opcode >> 3)&7].I); + + //bus.reg[dest].I &= bus.reg[(opcode >> 3)&7].I; + N_FLAG = val & 0x80000000 ? true : false; + Z_FLAG = val ? false : true; + + bus.reg[dest].I = val; + +} + +// EOR Rd, Rs + void thumb40_1(u32 opcode) +{ + int dest = opcode & 7; + bus.reg[dest].I ^= bus.reg[(opcode >> 3)&7].I; + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = bus.reg[dest].I ? false : true; +} + +// LSL Rd, Rs + void thumb40_2(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[(opcode >> 3)&7].B.B0; + u32 val = value; + if(val) { + if(val == 32) { + value = 0; + C_FLAG = (bus.reg[dest].I & 1 ? true : false); + } else if(val < 32) { + LSL_RD_RS; + } else { + value = 0; + C_FLAG = false; + } + bus.reg[dest].I = value; + } + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = bus.reg[dest].I ? false : true; + clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2; +} + +// LSR Rd, Rs + void thumb40_3(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[(opcode >> 3)&7].B.B0; + u32 val = value; + if(val) { + if(val == 32) { + value = 0; + C_FLAG = (bus.reg[dest].I & 0x80000000 ? true : false); + } else if(val < 32) { + LSR_RD_RS; + } else { + value = 0; + C_FLAG = false; + } + bus.reg[dest].I = value; + } + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = bus.reg[dest].I ? false : true; + clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2; +} + +// ASR Rd, Rs + void thumb41_0(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[(opcode >> 3)&7].B.B0; + + if(value) { + if(value < 32) { + ASR_RD_RS; + bus.reg[dest].I = value; + } else { + if(bus.reg[dest].I & 0x80000000){ + bus.reg[dest].I = 0xFFFFFFFF; + C_FLAG = true; + } else { + bus.reg[dest].I = 0x00000000; + C_FLAG = false; + } + } + } + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = bus.reg[dest].I ? false : true; + clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2; +} + +// ADC Rd, Rs + void thumb41_1(u32 opcode) +{ + int dest = opcode & 0x07; + u32 value = bus.reg[(opcode >> 3)&7].I; + ADC_RD_RS; +} + +// SBC Rd, Rs + void thumb41_2(u32 opcode) +{ + int dest = opcode & 0x07; + u32 value = bus.reg[(opcode >> 3)&7].I; + SBC_RD_RS; +} + +// ROR Rd, Rs + void thumb41_3(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[(opcode >> 3)&7].B.B0; + u32 val = value; + if(val) { + value = value & 0x1f; + if(val == 0) { + C_FLAG = (bus.reg[dest].I & 0x80000000 ? true : false); + } else { + ROR_RD_RS; + bus.reg[dest].I = value; + } + } + clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2; + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = bus.reg[dest].I ? false : true; +} + +// TST Rd, Rs + void thumb42_0(u32 opcode) +{ + u32 value = bus.reg[opcode & 7].I & bus.reg[(opcode >> 3) & 7].I; + N_FLAG = value & 0x80000000 ? true : false; + Z_FLAG = value ? false : true; +} + +// NEG Rd, Rs + void thumb42_1(u32 opcode) +{ + int dest = opcode & 7; + int source = (opcode >> 3) & 7; + NEG_RD_RS; +} + +// CMP Rd, Rs + void thumb42_2(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[(opcode >> 3)&7].I; + CMP_RD_RS; +} + +// CMN Rd, Rs + void thumb42_3(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[(opcode >> 3)&7].I; + CMN_RD_RS; +} + +// ORR Rd, Rs + void thumb43_0(u32 opcode) +{ + int dest = opcode & 7; + bus.reg[dest].I |= bus.reg[(opcode >> 3) & 7].I; + Z_FLAG = bus.reg[dest].I ? false : true; + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; +} + +// MUL Rd, Rs + void thumb43_1(u32 opcode) +{ + clockTicks = 1; + int dest = opcode & 7; + u32 rm = bus.reg[dest].I; + bus.reg[dest].I = bus.reg[(opcode >> 3) & 7].I * rm; + if (((s32)rm) < 0) + rm = ~rm; + if ((rm & 0xFFFF0000) == 0) + clockTicks += 1; + else if ((rm & 0xFF000000) == 0) + clockTicks += 2; + else + clockTicks += 3; + bus.busPrefetchCount = (bus.busPrefetchCount<>(8-clockTicks)); + clockTicks += codeTicksAccess(bus.armNextPC, BITS_16) + 1; + Z_FLAG = bus.reg[dest].I ? false : true; + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; +} + +// BIC Rd, Rs + void thumb43_2(u32 opcode) +{ + int dest = opcode & 7; + bus.reg[dest].I &= (~bus.reg[(opcode >> 3) & 7].I); + Z_FLAG = bus.reg[dest].I ? false : true; + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; +} + +// MVN Rd, Rs + void thumb43_3(u32 opcode) +{ + int dest = opcode & 7; + bus.reg[dest].I = ~bus.reg[(opcode >> 3) & 7].I; + Z_FLAG = bus.reg[dest].I ? false : true; + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; +} + +// High-register instructions and BX ////////////////////////////////////// + +// ADD Rd, Hs + void thumb44_1(u32 opcode) +{ + bus.reg[opcode&7].I += bus.reg[((opcode>>3)&7)+8].I; +} + +// ADD Hd, Rs + void thumb44_2(u32 opcode) +{ + bus.reg[(opcode&7)+8].I += bus.reg[(opcode>>3)&7].I; + if((opcode&7) == 7) { + bus.reg[15].I &= 0xFFFFFFFE; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1 + + codeTicksAccess(bus.armNextPC, BITS_16) + 3; + } +} + +// ADD Hd, Hs + void thumb44_3(u32 opcode) +{ + bus.reg[(opcode&7)+8].I += bus.reg[((opcode>>3)&7)+8].I; + if((opcode&7) == 7) { + bus.reg[15].I &= 0xFFFFFFFE; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1 + + codeTicksAccess(bus.armNextPC, BITS_16) + 3; + } +} + +// CMP Rd, Hs + void thumb45_1(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[((opcode>>3)&7)+8].I; + CMP_RD_RS; +} + +// CMP Hd, Rs + void thumb45_2(u32 opcode) +{ + int dest = (opcode & 7) + 8; + u32 value = bus.reg[(opcode>>3)&7].I; + CMP_RD_RS; +} + +// CMP Hd, Hs + void thumb45_3(u32 opcode) +{ + int dest = (opcode & 7) + 8; + u32 value = bus.reg[((opcode>>3)&7)+8].I; + CMP_RD_RS; +} + +// MOV Rd, Hs + void thumb46_1(u32 opcode) +{ + bus.reg[opcode&7].I = bus.reg[((opcode>>3)&7)+8].I; +} + +// MOV Hd, Rs + void thumb46_2(u32 opcode) +{ + bus.reg[(opcode&7)+8].I = bus.reg[(opcode>>3)&7].I; + if((opcode&7) == 7) { + bus.reg[15].I &= 0xFFFFFFFE; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1 + + codeTicksAccess(bus.armNextPC, BITS_16) + 3; + } +} + +// MOV Hd, Hs + void thumb46_3(u32 opcode) +{ + bus.reg[(opcode&7)+8].I = bus.reg[((opcode>>3)&7)+8].I; + if((opcode&7) == 7) { + bus.reg[15].I &= 0xFFFFFFFE; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1 + + codeTicksAccess(bus.armNextPC, BITS_16) + 3; + } +} + + +// BX Rs + void thumb47(u32 opcode) +{ + int base = (opcode >> 3) & 15; + bus.busPrefetchCount=0; + bus.reg[15].I = bus.reg[base].I; + if(bus.reg[base].I & 1) { + armState = false; + bus.reg[15].I &= 0xFFFFFFFE; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = ((codeTicksAccessSeq16(bus.armNextPC)) << 1) + + codeTicksAccess(bus.armNextPC, BITS_16) + 3; + } else { + armState = true; + bus.reg[15].I &= 0xFFFFFFFC; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + ARM_PREFETCH; + clockTicks = ((codeTicksAccessSeq32(bus.armNextPC)) << 1) + + codeTicksAccess(bus.armNextPC, BITS_32) + 3; + } +} + +// Load/store instructions //////////////////////////////////////////////// + +// LDR R0~R7,[PC, #Imm] + void thumb48(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = (bus.reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + bus.reg[regist].I = CPUReadMemoryQuick(address); + bus.busPrefetchCount=0; + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// STR Rd, [Rs, Rn] + void thumb50(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + CPUWriteMemory(address, bus.reg[opcode & 7].I); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// STRH Rd, [Rs, Rn] + void thumb52(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + CPUWriteHalfWord(address, bus.reg[opcode&7].W.W0); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// STRB Rd, [Rs, Rn] + void thumb54(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode >>6)&7].I; + CPUWriteByte(address, bus.reg[opcode & 7].B.B0); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// LDSB Rd, [Rs, Rn] + void thumb56(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + bus.reg[opcode&7].I = (s8)CPUReadByte(address); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// LDR Rd, [Rs, Rn] + void thumb58(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + bus.reg[opcode&7].I = CPUReadMemory(address); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// LDRH Rd, [Rs, Rn] + void thumb5A(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + bus.reg[opcode&7].I = CPUReadHalfWord(address); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// LDRB Rd, [Rs, Rn] + void thumb5C(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + bus.reg[opcode&7].I = CPUReadByte(address); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// LDSH Rd, [Rs, Rn] + void thumb5E(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + bus.reg[opcode&7].I = (s16)CPUReadHalfWordSigned(address); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// STR Rd, [Rs, #Imm] + void thumb60(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2); + CPUWriteMemory(address, bus.reg[opcode&7].I); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// LDR Rd, [Rs, #Imm] + void thumb68(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2); + bus.reg[opcode&7].I = CPUReadMemory(address); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// STRB Rd, [Rs, #Imm] + void thumb70(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)); + CPUWriteByte(address, bus.reg[opcode&7].B.B0); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// LDRB Rd, [Rs, #Imm] + void thumb78(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)); + bus.reg[opcode&7].I = CPUReadByte(address); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// STRH Rd, [Rs, #Imm] + void thumb80(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1); + CPUWriteHalfWord(address, bus.reg[opcode&7].W.W0); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// LDRH Rd, [Rs, #Imm] + void thumb88(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1); + bus.reg[opcode&7].I = CPUReadHalfWord(address); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// STR R0~R7, [SP, #Imm] + void thumb90(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[13].I + ((opcode&255)<<2); + CPUWriteMemory(address, bus.reg[regist].I); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// LDR R0~R7, [SP, #Imm] + void thumb98(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[13].I + ((opcode&255)<<2); + bus.reg[regist].I = CPUReadMemoryQuick(address); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// PC/stack-related /////////////////////////////////////////////////////// + +// ADD R0~R7, PC, Imm + void thumbA0(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + bus.reg[regist].I = (bus.reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); +} + +// ADD R0~R7, SP, Imm + void thumbA8(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + bus.reg[regist].I = bus.reg[13].I + ((opcode&255)<<2); +} + +// ADD SP, Imm + void thumbB0(u32 opcode) +{ + int offset = (opcode & 127) << 2; + if(opcode & 0x80) + offset = -offset; + bus.reg[13].I += offset; +} + +// Push and pop /////////////////////////////////////////////////////////// + +#define PUSH_REG(val, r) \ + if (opcode & (val)) { \ + CPUWriteMemory(address, bus.reg[(r)].I); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + address += 4; \ + } + +#define POP_REG(val, r) \ + if (opcode & (val)) { \ + bus.reg[(r)].I = CPUReadMemory(address); \ +int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + address += 4; \ + } + +// PUSH {Rlist} + void thumbB4(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int count = 0; + u32 temp = bus.reg[13].I - 4 * cpuBitsSet[opcode & 0xff]; + u32 address = temp & 0xFFFFFFFC; + PUSH_REG(1, 0); + PUSH_REG(2, 1); + PUSH_REG(4, 2); + PUSH_REG(8, 3); + PUSH_REG(16, 4); + PUSH_REG(32, 5); + PUSH_REG(64, 6); + PUSH_REG(128, 7); + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_16); + bus.reg[13].I = temp; +} + +// PUSH {Rlist, LR} + void thumbB5(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int count = 0; + u32 temp = bus.reg[13].I - 4 - 4 * cpuBitsSet[opcode & 0xff]; + u32 address = temp & 0xFFFFFFFC; + PUSH_REG(1, 0); + PUSH_REG(2, 1); + PUSH_REG(4, 2); + PUSH_REG(8, 3); + PUSH_REG(16, 4); + PUSH_REG(32, 5); + PUSH_REG(64, 6); + PUSH_REG(128, 7); + PUSH_REG(256, 14); + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_16); + bus.reg[13].I = temp; +} + +// POP {Rlist} + void thumbBC(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int count = 0; + u32 address = bus.reg[13].I & 0xFFFFFFFC; + u32 temp = bus.reg[13].I + 4*cpuBitsSet[opcode & 0xFF]; + POP_REG(1, 0); + POP_REG(2, 1); + POP_REG(4, 2); + POP_REG(8, 3); + POP_REG(16, 4); + POP_REG(32, 5); + POP_REG(64, 6); + POP_REG(128, 7); + bus.reg[13].I = temp; + clockTicks = 2 + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// POP {Rlist, PC} + void thumbBD(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int count = 0; + u32 address = bus.reg[13].I & 0xFFFFFFFC; + u32 temp = bus.reg[13].I + 4 + 4*cpuBitsSet[opcode & 0xFF]; + POP_REG(1, 0); + POP_REG(2, 1); + POP_REG(4, 2); + POP_REG(8, 3); + POP_REG(16, 4); + POP_REG(32, 5); + POP_REG(64, 6); + POP_REG(128, 7); + bus.reg[15].I = (CPUReadMemory(address) & 0xFFFFFFFE); + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks += 1 + dataticks_value; + count++; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + bus.reg[13].I = temp; + THUMB_PREFETCH; + bus.busPrefetchCount = 0; + clockTicks += 3 + ((codeTicksAccess(bus.armNextPC, BITS_16)) << 1); +} + +// Load/store multiple //////////////////////////////////////////////////// + +#define THUMB_STM_REG(val,r,b) \ + if(opcode & (val)) { \ + CPUWriteMemory(address, bus.reg[(r)].I); \ + bus.reg[(b)].I = temp; \ + int dataticks_val = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_val); \ + clockTicks += 1 + dataticks_val; \ + count++; \ + address += 4; \ + } + +#define THUMB_LDM_REG(val,r) \ + if(opcode & (val)) { \ + bus.reg[(r)].I = CPUReadMemory(address); \ + int dataticks_val = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_val); \ + clockTicks += 1 + dataticks_val; \ + count++; \ + address += 4; \ + } + +// STM R0~7!, {Rlist} + void thumbC0(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[regist].I & 0xFFFFFFFC; + u32 temp = bus.reg[regist].I + 4*cpuBitsSet[opcode & 0xff]; + int count = 0; + // store + THUMB_STM_REG(1, 0, regist); + THUMB_STM_REG(2, 1, regist); + THUMB_STM_REG(4, 2, regist); + THUMB_STM_REG(8, 3, regist); + THUMB_STM_REG(16, 4, regist); + THUMB_STM_REG(32, 5, regist); + THUMB_STM_REG(64, 6, regist); + THUMB_STM_REG(128, 7, regist); + clockTicks = 1 + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// LDM R0~R7!, {Rlist} + void thumbC8(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[regist].I & 0xFFFFFFFC; + u32 temp = bus.reg[regist].I + 4*cpuBitsSet[opcode & 0xFF]; + int count = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + clockTicks = 2 + codeTicksAccess(bus.armNextPC, BITS_16); + if(!(opcode & (1<*thumbInsnTable[opcode>>6])(opcode); + + ct = clockTicks; + + if (ct < 0) + return 0; + + /// better pipelining + if (ct==0) + clockTicks = codeTicksAccessSeq16(oldArmNextPC) + 1; + + cpuTotalTicks += clockTicks; + + +#ifdef USE_SWITICKS + } while (cpuTotalTicks < cpuNextEvent && !armState && !holdState && !SWITicks); +#else +} while ((cpuTotalTicks < cpuNextEvent) & ~armState & ~holdState); +#endif + return 1; +} + + +/*============================================================ + GBA GFX +============================================================ */ + +#ifdef TILED_RENDERING +#ifdef _MSC_VER +union u8h +{ + __pragma( pack(push, 1)); + struct +#ifdef LSB_FIRST + { + /* 0*/ unsigned char lo:4; + /* 4*/ unsigned char hi:4; +#else + { + /* 4*/ unsigned char hi:4; + /* 0*/ unsigned char lo:4; +#endif + } + __pragma(pack(pop)); + u8 val; +}; +#else +union u8h +{ + struct +#ifdef LSB_FIRST + { + /* 0*/ unsigned char lo:4; + /* 4*/ unsigned char hi:4; +#else + { + /* 4*/ unsigned char hi:4; + /* 0*/ unsigned char lo:4; +#endif + } __attribute__ ((packed)); + u8 val; +}; +#endif + +union TileEntry +{ +#ifdef LSB_FIRST + struct + { + /* 0*/ unsigned tileNum:10; + /*12*/ unsigned hFlip:1; + /*13*/ unsigned vFlip:1; + /*14*/ unsigned palette:4; + }; +#else + struct + { + /*14*/ unsigned palette:4; + /*13*/ unsigned vFlip:1; + /*12*/ unsigned hFlip:1; + /* 0*/ unsigned tileNum:10; + }; +#endif + u16 val; +}; + +struct TileLine +{ + u32 pixels[8]; +}; + +typedef const TileLine (*TileReader) (const u16 *, const int, const u8 *, u16 *, const u32); + +static inline void gfxDrawPixel(u32 *dest, const u8 color, const u16 *palette, const u32 prio) +{ + *dest = color ? (READ16LE(&palette[color]) | prio): 0x80000000; +} + +static inline const TileLine gfxReadTile(const u16 *screenSource, const int yyy, const u8 *charBase, u16 *palette, const u32 prio) +{ + TileEntry tile; + tile.val = READ16LE(screenSource); + + int tileY = yyy & 7; + if (tile.vFlip) tileY = 7 - tileY; + TileLine tileLine; + + const u8 *tileBase = &charBase[tile.tileNum * 64 + tileY * 8]; + + if (!tile.hFlip) + { + gfxDrawPixel(&tileLine.pixels[0], tileBase[0], palette, prio); + gfxDrawPixel(&tileLine.pixels[1], tileBase[1], palette, prio); + gfxDrawPixel(&tileLine.pixels[2], tileBase[2], palette, prio); + gfxDrawPixel(&tileLine.pixels[3], tileBase[3], palette, prio); + gfxDrawPixel(&tileLine.pixels[4], tileBase[4], palette, prio); + gfxDrawPixel(&tileLine.pixels[5], tileBase[5], palette, prio); + gfxDrawPixel(&tileLine.pixels[6], tileBase[6], palette, prio); + gfxDrawPixel(&tileLine.pixels[7], tileBase[7], palette, prio); + } + else + { + gfxDrawPixel(&tileLine.pixels[0], tileBase[7], palette, prio); + gfxDrawPixel(&tileLine.pixels[1], tileBase[6], palette, prio); + gfxDrawPixel(&tileLine.pixels[2], tileBase[5], palette, prio); + gfxDrawPixel(&tileLine.pixels[3], tileBase[4], palette, prio); + gfxDrawPixel(&tileLine.pixels[4], tileBase[3], palette, prio); + gfxDrawPixel(&tileLine.pixels[5], tileBase[2], palette, prio); + gfxDrawPixel(&tileLine.pixels[6], tileBase[1], palette, prio); + gfxDrawPixel(&tileLine.pixels[7], tileBase[0], palette, prio); + } + + return tileLine; +} + +static inline const TileLine gfxReadTilePal(const u16 *screenSource, const int yyy, const u8 *charBase, u16 *palette, const u32 prio) +{ + TileEntry tile; + tile.val = READ16LE(screenSource); + + int tileY = yyy & 7; + if (tile.vFlip) tileY = 7 - tileY; + palette += tile.palette * 16; + TileLine tileLine; + + const u8h *tileBase = (u8h*) &charBase[tile.tileNum * 32 + tileY * 4]; + + if (!tile.hFlip) + { + gfxDrawPixel(&tileLine.pixels[0], tileBase[0].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[1], tileBase[0].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[2], tileBase[1].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[3], tileBase[1].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[4], tileBase[2].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[5], tileBase[2].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[6], tileBase[3].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[7], tileBase[3].hi, palette, prio); + } + else + { + gfxDrawPixel(&tileLine.pixels[0], tileBase[3].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[1], tileBase[3].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[2], tileBase[2].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[3], tileBase[2].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[4], tileBase[1].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[5], tileBase[1].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[6], tileBase[0].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[7], tileBase[0].lo, palette, prio); + } + + return tileLine; +} + +static inline void gfxDrawTile(const TileLine &tileLine, u32 *line) +{ + memcpy(line, tileLine.pixels, sizeof(tileLine.pixels)); +} + +static inline void gfxDrawTileClipped(const TileLine &tileLine, u32 *line, const int start, int w) +{ + memcpy(line, tileLine.pixels + start, w * sizeof(u32)); +} + +template +void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, + u32 *line) +{ + u16 *palette = (u16 *)graphics.paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u32 prio = ((control & 3)<<25) + 0x1000000; + int sizeX = 256; + int sizeY = 256; + switch ((control >> 14) & 3) + { + case 0: + break; + case 1: + sizeX = 512; + break; + case 2: + sizeY = 512; + break; + case 3: + sizeX = 512; + sizeY = 512; + break; + } + + int maskX = sizeX-1; + int maskY = sizeY-1; + + bool mosaicOn = (control & 0x40) ? true : false; + + int xxx = hofs & maskX; + int yyy = (vofs + io_registers[REG_VCOUNT]) & maskY; + int mosaicX = (MOSAIC & 0x000F)+1; + int mosaicY = ((MOSAIC & 0x00F0)>>4)+1; + + if (mosaicOn) + { + if ((io_registers[REG_VCOUNT] % mosaicY) != 0) + { + mosaicY = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY); + yyy = (vofs + mosaicY) & maskY; + } + } + + if (yyy > 255 && sizeY > 256) + { + yyy &= 255; + screenBase += 0x400; + if (sizeX > 256) + screenBase += 0x400; + } + + int yshift = ((yyy>>3)<<5); + + u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift; + int x = 0; + const int firstTileX = xxx & 7; + + // First tile, if clipped + if (firstTileX) + { + gfxDrawTileClipped(readTile(screenSource, yyy, charBase, palette, prio), &line[x], firstTileX, 8 - firstTileX); + screenSource++; + x += 8 - firstTileX; + xxx += 8 - firstTileX; + + if (xxx == 256 && sizeX > 256) + { + screenSource = screenBase + 0x400 + yshift; + } + else if (xxx >= sizeX) + { + xxx = 0; + screenSource = screenBase + yshift; + } + } + + // Middle tiles, full + while (x < 240 - firstTileX) + { + gfxDrawTile(readTile(screenSource, yyy, charBase, palette, prio), &line[x]); + screenSource++; + xxx += 8; + x += 8; + + if (xxx == 256 && sizeX > 256) + { + screenSource = screenBase + 0x400 + yshift; + } + else if (xxx >= sizeX) + { + xxx = 0; + screenSource = screenBase + yshift; + } + } + + // Last tile, if clipped + if (firstTileX) + { + gfxDrawTileClipped(readTile(screenSource, yyy, charBase, palette, prio), &line[x], 0, firstTileX); + } + + if (mosaicOn) + { + if (mosaicX > 1) + { + int m = 1; + for (int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if (m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, u32 *line) +{ + if (control & 0x80) // 1 pal / 256 col + gfxDrawTextScreen(control, hofs, vofs, line); + else // 16 pal / 16 col + gfxDrawTextScreen(control, hofs, vofs, line); +} +#else +inline void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, + u32 *line) +{ + u16 *palette = (u16 *)graphics.paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u32 prio = ((control & 3)<<25) + 0x1000000; + int sizeX = 256; + int sizeY = 256; + switch((control >> 14) & 3) { + case 0: + break; + case 1: + sizeX = 512; + break; + case 2: + sizeY = 512; + break; + case 3: + sizeX = 512; + sizeY = 512; + break; + } + + int maskX = sizeX-1; + int maskY = sizeY-1; + + bool mosaicOn = (control & 0x40) ? true : false; + + int xxx = hofs & maskX; + int yyy = (vofs + io_registers[REG_VCOUNT]) & maskY; + int mosaicX = (MOSAIC & 0x000F)+1; + int mosaicY = ((MOSAIC & 0x00F0)>>4)+1; + + if(mosaicOn) { + if((io_registers[REG_VCOUNT] % mosaicY) != 0) { + mosaicY = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY); + yyy = (vofs + mosaicY) & maskY; + } + } + + if(yyy > 255 && sizeY > 256) { + yyy &= 255; + screenBase += 0x400; + if(sizeX > 256) + screenBase += 0x400; + } + + int yshift = ((yyy>>3)<<5); + if((control) & 0x80) { + u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift; + for(int x = 0; x < 240; x++) { + u16 data = READ16LE(screenSource); + + int tile = data & 0x3FF; + int tileX = (xxx & 7); + int tileY = yyy & 7; + + if(tileX == 7) + screenSource++; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[tile * 64 + tileY * 8 + tileX]; + + line[x] = color ? (READ16LE(&palette[color]) | prio): 0x80000000; + + xxx++; + if(xxx == 256) { + if(sizeX > 256) + screenSource = screenBase + 0x400 + yshift; + else { + screenSource = screenBase + yshift; + xxx = 0; + } + } else if(xxx >= sizeX) { + xxx = 0; + screenSource = screenBase + yshift; + } + } + } else { + u16 *screenSource = screenBase + 0x400*(xxx>>8)+((xxx&255)>>3) + + yshift; + for(int x = 0; x < 240; x++) { + u16 data = READ16LE(screenSource); + + int tile = data & 0x3FF; + int tileX = (xxx & 7); + int tileY = yyy & 7; + + if(tileX == 7) + screenSource++; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[(tile<<5) + (tileY<<2) + (tileX>>1)]; + + if(tileX & 1) { + color = (color >> 4); + } else { + color &= 0x0F; + } + + int pal = (data>>8) & 0xF0; + line[x] = color ? (READ16LE(&palette[pal + color])|prio): 0x80000000; + + xxx++; + if(xxx == 256) { + if(sizeX > 256) + screenSource = screenBase + 0x400 + yshift; + else { + screenSource = screenBase + yshift; + xxx = 0; + } + } else if(xxx >= sizeX) { + xxx = 0; + screenSource = screenBase + yshift; + } + } + } + if(mosaicOn) { + if(mosaicX > 1) { + int m = 1; + for(int i = 0; i < 239; i++) { + line[i+1] = line[i]; + m++; + if(m == mosaicX) { + m = 1; + i++; + } + } + } + } +} +#endif + +INLINE void gfxDrawRotScreen(u16 control, u16 x_l, u16 x_h, u16 y_l, u16 y_h, +u16 pa, u16 pb, u16 pc, u16 pd, int& currentX, int& currentY, int changed, u32 *line) +{ + u16 *palette = (u16 *)graphics.paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) << 14]; + u8 *screenBase = (u8 *)&vram[((control >> 8) & 0x1f) << 11]; + int prio = ((control & 3) << 25) + 0x1000000; + + u32 map_size = (control >> 14) & 3; + u32 sizeX = map_sizes_rot[map_size]; + u32 sizeY = map_sizes_rot[map_size]; + + int maskX = sizeX-1; + int maskY = sizeY-1; + + int yshift = ((control >> 14) & 3)+4; + +#ifdef BRANCHLESS_GBA_GFX + int dx = pa & 0x7FFF; + int dmx = pb & 0x7FFF; + int dy = pc & 0x7FFF; + int dmy = pd & 0x7FFF; + + dx |= isel(-(pa & 0x8000), 0, 0xFFFF8000); + + dmx |= isel(-(pb & 0x8000), 0, 0xFFFF8000); + + dy |= isel(-(pc & 0x8000), 0, 0xFFFF8000); + + dmy |= isel(-(pd & 0x8000), 0, 0xFFFF8000); +#else + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; +#endif + + if(io_registers[REG_VCOUNT] == 0) + changed = 3; + + currentX += dmx; + currentY += dmy; + + if(changed & 1) + { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } + + if(changed & 2) + { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) + { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (io_registers[REG_VCOUNT] % mosaicY); + realX -= y*dmx; + realY -= y*dmy; + } + + memset(line, -1, 240 * sizeof(u32)); + if(control & 0x2000) + { + for(u32 x = 0; x < 240u; ++x) + { + int xxx = (realX >> 8) & maskX; + int yyy = (realY >> 8) & maskY; + + int tile = screenBase[(xxx>>3) + ((yyy>>3)<> 8); + unsigned yyy = (realY >> 8); + + if(xxx < sizeX && yyy < sizeY) + { + int tile = screenBase[(xxx>>3) + ((yyy>>3)< 1) + { + int m = 1; + for(u32 i = 0; i < 239u; ++i) + { + line[i+1] = line[i]; + if(++m == mosaicX) + { + m = 1; + ++i; + } + } + } + } +} + +INLINE void gfxDrawRotScreen16Bit( int& currentX, int& currentY, int changed) +{ + u16 *screenBase = (u16 *)&vram[0]; + int prio = ((io_registers[REG_BG2CNT] & 3) << 25) + 0x1000000; + + u32 sizeX = 240; + u32 sizeY = 160; + + int startX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); + if(BG2X_H & 0x0800) + startX |= 0xF8000000; + int startY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); + if(BG2Y_H & 0x0800) + startY |= 0xF8000000; + +#ifdef BRANCHLESS_GBA_GFX + int dx = io_registers[REG_BG2PA] & 0x7FFF; + dx |= isel(-(io_registers[REG_BG2PA] & 0x8000), 0, 0xFFFF8000); + + int dmx = io_registers[REG_BG2PB] & 0x7FFF; + dmx |= isel(-(io_registers[REG_BG2PB] & 0x8000), 0, 0xFFFF8000); + + int dy = io_registers[REG_BG2PC] & 0x7FFF; + dy |= isel(-(io_registers[REG_BG2PC] & 0x8000), 0, 0xFFFF8000); + + int dmy = io_registers[REG_BG2PD] & 0x7FFF; + dmy |= isel(-(io_registers[REG_BG2PD] & 0x8000), 0, 0xFFFF8000); +#else + int dx = io_registers[REG_BG2PA] & 0x7FFF; + if(io_registers[REG_BG2PA] & 0x8000) + dx |= 0xFFFF8000; + int dmx = io_registers[REG_BG2PB] & 0x7FFF; + if(io_registers[REG_BG2PB] & 0x8000) + dmx |= 0xFFFF8000; + int dy = io_registers[REG_BG2PC] & 0x7FFF; + if(io_registers[REG_BG2PC] & 0x8000) + dy |= 0xFFFF8000; + int dmy = io_registers[REG_BG2PD] & 0x7FFF; + if(io_registers[REG_BG2PD] & 0x8000) + dmy |= 0xFFFF8000; +#endif + + if(io_registers[REG_VCOUNT] == 0) + changed = 3; + + currentX += dmx; + currentY += dmy; + + if(changed & 1) + { + currentX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); + if(BG2X_H & 0x0800) + currentX |= 0xF8000000; + } + + if(changed & 2) + { + currentY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); + if(BG2Y_H & 0x0800) + currentY |= 0xF8000000; + } + + int realX = currentX; + int realY = currentY; + + if(io_registers[REG_BG2CNT] & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (io_registers[REG_VCOUNT] % mosaicY); + realX -= y*dmx; + realY -= y*dmy; + } + + unsigned xxx = (realX >> 8); + unsigned yyy = (realY >> 8); + + memset(line[2], -1, 240 * sizeof(u32)); + for(u32 x = 0; x < 240u; ++x) + { + if(xxx < sizeX && yyy < sizeY) + line[2][x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); + + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(io_registers[REG_BG2CNT] & 0x40) { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) { + int m = 1; + for(u32 i = 0; i < 239u; ++i) + { + line[2][i+1] = line[2][i]; + if(++m == mosaicX) + { + m = 1; + ++i; + } + } + } + } +} + +INLINE void gfxDrawRotScreen256(int ¤tX, int& currentY, int changed) +{ + u16 *palette = (u16 *)graphics.paletteRAM; + u8 *screenBase = (io_registers[REG_DISPCNT] & 0x0010) ? &vram[0xA000] : &vram[0x0000]; + int prio = ((io_registers[REG_BG2CNT] & 3) << 25) + 0x1000000; + u32 sizeX = 240; + u32 sizeY = 160; + + int startX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); + if(BG2X_H & 0x0800) + startX |= 0xF8000000; + int startY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); + if(BG2Y_H & 0x0800) + startY |= 0xF8000000; + +#ifdef BRANCHLESS_GBA_GFX + int dx = io_registers[REG_BG2PA] & 0x7FFF; + dx |= isel(-(io_registers[REG_BG2PA] & 0x8000), 0, 0xFFFF8000); + + int dmx = io_registers[REG_BG2PB] & 0x7FFF; + dmx |= isel(-(io_registers[REG_BG2PB] & 0x8000), 0, 0xFFFF8000); + + int dy = io_registers[REG_BG2PC] & 0x7FFF; + dy |= isel(-(io_registers[REG_BG2PC] & 0x8000), 0, 0xFFFF8000); + + int dmy = io_registers[REG_BG2PD] & 0x7FFF; + dmy |= isel(-(io_registers[REG_BG2PD] & 0x8000), 0, 0xFFFF8000); +#else + int dx = io_registers[REG_BG2PA] & 0x7FFF; + if(io_registers[REG_BG2PA] & 0x8000) + dx |= 0xFFFF8000; + int dmx = io_registers[REG_BG2PB] & 0x7FFF; + if(io_registers[REG_BG2PB] & 0x8000) + dmx |= 0xFFFF8000; + int dy = io_registers[REG_BG2PC] & 0x7FFF; + if(io_registers[REG_BG2PC] & 0x8000) + dy |= 0xFFFF8000; + int dmy = io_registers[REG_BG2PD] & 0x7FFF; + if(io_registers[REG_BG2PD] & 0x8000) + dmy |= 0xFFFF8000; +#endif + + if(io_registers[REG_VCOUNT] == 0) + changed = 3; + + currentX += dmx; + currentY += dmy; + + if(changed & 1) + { + currentX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); + if(BG2X_H & 0x0800) + currentX |= 0xF8000000; + } + + if(changed & 2) + { + currentY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); + if(BG2Y_H & 0x0800) + currentY |= 0xF8000000; + } + + int realX = currentX; + int realY = currentY; + + if(io_registers[REG_BG2CNT] & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY); + realX = startX + y*dmx; + realY = startY + y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + memset(line[2], -1, 240 * sizeof(u32)); + for(u32 x = 0; x < 240; ++x) + { + u8 color = screenBase[yyy * 240 + xxx]; + if(unsigned(xxx) < sizeX && unsigned(yyy) < sizeY && color) + line[2][x] = (READ16LE(&palette[color])|prio); + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(io_registers[REG_BG2CNT] & 0x40) + { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) + { + int m = 1; + for(u32 i = 0; i < 239u; ++i) + { + line[2][i+1] = line[2][i]; + if(++m == mosaicX) + { + m = 1; + ++i; + } + } + } + } +} + +INLINE void gfxDrawRotScreen16Bit160(int& currentX, int& currentY, int changed) +{ + u16 *screenBase = (io_registers[REG_DISPCNT] & 0x0010) ? (u16 *)&vram[0xa000] : + (u16 *)&vram[0]; + int prio = ((io_registers[REG_BG2CNT] & 3) << 25) + 0x1000000; + u32 sizeX = 160; + u32 sizeY = 128; + + int startX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); + if(BG2X_H & 0x0800) + startX |= 0xF8000000; + int startY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); + if(BG2Y_H & 0x0800) + startY |= 0xF8000000; + +#ifdef BRANCHLESS_GBA_GFX + int dx = io_registers[REG_BG2PA] & 0x7FFF; + dx |= isel(-(io_registers[REG_BG2PA] & 0x8000), 0, 0xFFFF8000); + + int dmx = io_registers[REG_BG2PB] & 0x7FFF; + dmx |= isel(-(io_registers[REG_BG2PB] & 0x8000), 0, 0xFFFF8000); + + int dy = io_registers[REG_BG2PC] & 0x7FFF; + dy |= isel(-(io_registers[REG_BG2PC] & 0x8000), 0, 0xFFFF8000); + + int dmy = io_registers[REG_BG2PD] & 0x7FFF; + dmy |= isel(-(io_registers[REG_BG2PD] & 0x8000), 0, 0xFFFF8000); +#else + int dx = io_registers[REG_BG2PA] & 0x7FFF; + if(io_registers[REG_BG2PA] & 0x8000) + dx |= 0xFFFF8000; + int dmx = io_registers[REG_BG2PB] & 0x7FFF; + if(io_registers[REG_BG2PB] & 0x8000) + dmx |= 0xFFFF8000; + int dy = io_registers[REG_BG2PC] & 0x7FFF; + if(io_registers[REG_BG2PC] & 0x8000) + dy |= 0xFFFF8000; + int dmy = io_registers[REG_BG2PD] & 0x7FFF; + if(io_registers[REG_BG2PD] & 0x8000) + dmy |= 0xFFFF8000; +#endif + + if(io_registers[REG_VCOUNT] == 0) + changed = 3; + + currentX += dmx; + currentY += dmy; + + if(changed & 1) + { + currentX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); + if(BG2X_H & 0x0800) + currentX |= 0xF8000000; + } + + if(changed & 2) + { + currentY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); + if(BG2Y_H & 0x0800) + currentY |= 0xF8000000; + } + + int realX = currentX; + int realY = currentY; + + if(io_registers[REG_BG2CNT] & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY); + realX = startX + y*dmx; + realY = startY + y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + memset(line[2], -1, 240 * sizeof(u32)); + for(u32 x = 0; x < 240u; ++x) + { + if(unsigned(xxx) < sizeX && unsigned(yyy) < sizeY) + line[2][x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); + + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + + int mosaicX = (MOSAIC & 0xF) + 1; + if(io_registers[REG_BG2CNT] & 0x40 && (mosaicX > 1)) + { + int m = 1; + for(u32 i = 0; i < 239u; ++i) + { + line[2][i+1] = line[2][i]; + if(++m == mosaicX) + { + m = 1; + ++i; + } + } + } +} + +/* lineOBJpix is used to keep track of the drawn OBJs + and to stop drawing them if the 'maximum number of OBJ per line' + has been reached. */ + +INLINE void gfxDrawSprites (void) +{ + unsigned lineOBJpix, m; + + lineOBJpix = (io_registers[REG_DISPCNT] & 0x20) ? 954 : 1226; + m = 0; + + u16 *sprites = (u16 *)oam; + u16 *spritePalette = &((u16 *)graphics.paletteRAM)[256]; + int mosaicY = ((MOSAIC & 0xF000)>>12) + 1; + int mosaicX = ((MOSAIC & 0xF00)>>8) + 1; + for(u32 x = 0; x < 128; x++) + { + u16 a0 = READ16LE(sprites++); + u16 a1 = READ16LE(sprites++); + u16 a2 = READ16LE(sprites++); + ++sprites; + + lineOBJpixleft[x]=lineOBJpix; + + lineOBJpix-=2; + if (lineOBJpix<=0) + return; + + if ((a0 & 0x0c00) == 0x0c00) + a0 &=0xF3FF; + + u16 a0val = a0>>14; + + if (a0val == 3) + { + a0 &= 0x3FFF; + a1 &= 0x3FFF; + } + + u32 sizeX = 8<<(a1>>14); + u32 sizeY = sizeX; + + + if (a0val & 1) + { +#ifdef BRANCHLESS_GBA_GFX + sizeX <<= isel(-(sizeX & (~31u)), 1, 0); + sizeY >>= isel(-(sizeY>8), 0, 1); +#else + if (sizeX<32) + sizeX<<=1; + if (sizeY>8) + sizeY>>=1; +#endif + } + else if (a0val & 2) + { +#ifdef BRANCHLESS_GBA_GFX + sizeX >>= isel(-(sizeX>8), 0, 1); + sizeY <<= isel(-(sizeY & (~31u)), 1, 0); +#else + if (sizeX>8) + sizeX>>=1; + if (sizeY<32) + sizeY<<=1; +#endif + + } + + + int sy = (a0 & 255); + int sx = (a1 & 0x1FF); + + // computes ticks used by OBJ-WIN if OBJWIN is enabled + if (((a0 & 0x0c00) == 0x0800) && (graphics.layerEnable & 0x8000)) + { + if ((a0 & 0x0300) == 0x0300) + { + sizeX<<=1; + sizeY<<=1; + } + +#ifdef BRANCHLESS_GBA_GFX + sy -= isel(256 - sy - sizeY, 0, 256); + sx -= isel(512 - sx - sizeX, 0, 512); +#else + if((sy+sizeY) > 256) + sy -= 256; + if ((sx+sizeX)> 512) + sx -= 512; +#endif + + if (sx < 0) + { + sizeX+=sx; + sx = 0; + } + else if ((sx+sizeX)>240) + sizeX=240-sx; + + if ((io_registers[REG_VCOUNT]>=sy) && (io_registers[REG_VCOUNT] 256) + sy -= 256; + int t = io_registers[REG_VCOUNT] - sy; + if(unsigned(t) < fieldY) + { + u32 startpix = 0; + if ((sx+fieldX)> 512) + startpix=512-sx; + + if (lineOBJpix && ((sx < 240) || startpix)) + { + lineOBJpix-=8; + int rot = (((a1 >> 9) & 0x1F) << 4); + u16 *OAM = (u16 *)oam; + int dx = READ16LE(&OAM[3 + rot]); + if(dx & 0x8000) + dx |= 0xFFFF8000; + int dmx = READ16LE(&OAM[7 + rot]); + if(dmx & 0x8000) + dmx |= 0xFFFF8000; + int dy = READ16LE(&OAM[11 + rot]); + if(dy & 0x8000) + dy |= 0xFFFF8000; + int dmy = READ16LE(&OAM[15 + rot]); + if(dmy & 0x8000) + dmy |= 0xFFFF8000; + + if(a0 & 0x1000) + t -= (t % mosaicY); + + int realX = ((sizeX) << 7) - (fieldX >> 1)*dx + ((t - (fieldY>>1))* dmx); + int realY = ((sizeY) << 7) - (fieldX >> 1)*dy + ((t - (fieldY>>1))* dmy); + + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + int c = (a2 & 0x3FF); + if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512)) + continue; + + if(a0 & 0x2000) + { + int inc = 32; + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + for(u32 x = 0; x < fieldX; x++) + { + if (x >= startpix) + lineOBJpix-=2; + unsigned xxx = realX >> 8; + unsigned yyy = realY >> 8; + if(xxx < sizeX && yyy < sizeY && sx < 240) + { + + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + (xxx & 7))&0x7FFF)]; + + if ((color==0) && (((prio >> 25)&3) < ((line[4][sx]>>25)&3))) + { + line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (line[4][sx]&0xFF000000))) + { + line[4][sx] = READ16LE(&spritePalette[color]) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + + if ((a0 & 0x1000) && ((m+1) == mosaicX)) + m = 0; + } + sx = (sx+1)&511; + realX += dx; + realY += dy; + } + } + else + { + int inc = 32; + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 3; + int palette = (a2 >> 8) & 0xF0; + for(u32 x = 0; x < fieldX; ++x) + { + if (x >= startpix) + lineOBJpix-=2; + unsigned xxx = realX >> 8; + unsigned yyy = realY >> 8; + if(xxx < sizeX && yyy < sizeY && sx < 240) + { + + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + + ((xxx & 7)>>1))&0x7FFF)]; + if(xxx & 1) + color >>= 4; + else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((line[4][sx]>>25)&3))) + { + line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (line[4][sx]&0xFF000000))) + { + line[4][sx] = READ16LE(&spritePalette[palette+color]) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + } + if((a0 & 0x1000) && m) + { + if (++m==mosaicX) + m=0; + } + + sx = (sx+1)&511; + realX += dx; + realY += dy; + + } + } + } + } + } + else + { + if(sy+sizeY > 256) + sy -= 256; + int t = io_registers[REG_VCOUNT] - sy; + if(unsigned(t) < sizeY) + { + u32 startpix = 0; + if ((sx+sizeX)> 512) + startpix=512-sx; + + if((sx < 240) || startpix) + { + lineOBJpix+=2; + + if(a1 & 0x2000) + t = sizeY - t - 1; + + int c = (a2 & 0x3FF); + if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX-1; + + if(a0 & 0x1000) + t -= (t % mosaicY); + + if(a0 & 0x2000) + { + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + + int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) + + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7)) & 0x7FFF); + + if(a1 & 0x1000) + xxx = 7; + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + for(u32 xx = 0; xx < sizeX; xx++) + { + if (xx >= startpix) + --lineOBJpix; + if(sx < 240) + { + u8 color = vram[address]; + if ((color==0) && (((prio >> 25)&3) < + ((line[4][sx]>>25)&3))) + { + line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (line[4][sx]&0xFF000000))) + { + line[4][sx] = READ16LE(&spritePalette[color]) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + + if ((a0 & 0x1000) && ((m+1) == mosaicX)) + m = 0; + } + + sx = (sx+1) & 511; + if(a1 & 0x1000) + { + --address; + if(--xxx == -1) + { + address -= 56; + xxx = 7; + } + if(address < 0x10000) + address += 0x8000; + } + else + { + ++address; + if(++xxx == 8) + { + address += 56; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + else + { + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 3; + + int address = 0x10000 + ((((c + (t>>3) * inc)<<5) + + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7FFF); + + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + int palette = (a2 >> 8) & 0xF0; + if(a1 & 0x1000) + { + xxx = 7; + int xx = sizeX - 1; + do + { + if (xx >= (int)(startpix)) + --lineOBJpix; + //if (lineOBJpix<0) + // continue; + if(sx < 240) + { + u8 color = vram[address]; + if(xx & 1) + color >>= 4; + else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((line[4][sx]>>25)&3))) + { + line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (line[4][sx]&0xFF000000))) + { + line[4][sx] = READ16LE(&spritePalette[palette + color]) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + } + + if ((a0 & 0x1000) && ((m+1) == mosaicX)) + m=0; + + sx = (sx+1) & 511; + if(!(xx & 1)) + --address; + if(--xxx == -1) + { + xxx = 7; + address -= 28; + } + if(address < 0x10000) + address += 0x8000; + }while(--xx >= 0); + } + else + { + for(u32 xx = 0; xx < sizeX; ++xx) + { + if (xx >= startpix) + --lineOBJpix; + //if (lineOBJpix<0) + // continue; + if(sx < 240) + { + u8 color = vram[address]; + if(xx & 1) + color >>= 4; + else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((line[4][sx]>>25)&3))) + { + line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (line[4][sx]&0xFF000000))) + { + line[4][sx] = READ16LE(&spritePalette[palette + color]) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + + } + } + if ((a0 & 0x1000) && ((m+1) == mosaicX)) + m=0; + + sx = (sx+1) & 511; + if(xx & 1) + ++address; + if(++xxx == 8) + { + address += 28; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + } + } + } + } +} + +INLINE void gfxDrawOBJWin (void) +{ + u16 *sprites = (u16 *)oam; + for(int x = 0; x < 128 ; x++) + { + int lineOBJpix = lineOBJpixleft[x]; + u16 a0 = READ16LE(sprites++); + u16 a1 = READ16LE(sprites++); + u16 a2 = READ16LE(sprites++); + sprites++; + + if (lineOBJpix<=0) + return; + + // ignores non OBJ-WIN and disabled OBJ-WIN + if(((a0 & 0x0c00) != 0x0800) || ((a0 & 0x0300) == 0x0200)) + continue; + + u16 a0val = a0>>14; + + if ((a0 & 0x0c00) == 0x0c00) + a0 &=0xF3FF; + + if (a0val == 3) + { + a0 &= 0x3FFF; + a1 &= 0x3FFF; + } + + int sizeX = 8<<(a1>>14); + int sizeY = sizeX; + + if (a0val & 1) + { +#ifdef BRANCHLESS_GBA_GFX + sizeX <<= isel(-(sizeX & (~31u)), 1, 0); + sizeY >>= isel(-(sizeY>8), 0, 1); +#else + if (sizeX<32) + sizeX<<=1; + if (sizeY>8) + sizeY>>=1; +#endif + } + else if (a0val & 2) + { +#ifdef BRANCHLESS_GBA_GFX + sizeX >>= isel(-(sizeX>8), 0, 1); + sizeY <<= isel(-(sizeY & (~31u)), 1, 0); +#else + if (sizeX>8) + sizeX>>=1; + if (sizeY<32) + sizeY<<=1; +#endif + + } + + int sy = (a0 & 255); + + if(a0 & 0x0100) + { + int fieldX = sizeX; + int fieldY = sizeY; + if(a0 & 0x0200) + { + fieldX <<= 1; + fieldY <<= 1; + } + if((sy+fieldY) > 256) + sy -= 256; + int t = io_registers[REG_VCOUNT] - sy; + if((t >= 0) && (t < fieldY)) + { + int sx = (a1 & 0x1FF); + int startpix = 0; + if ((sx+fieldX)> 512) + startpix=512-sx; + + if((sx < 240) || startpix) + { + lineOBJpix-=8; + // int t2 = t - (fieldY >> 1); + int rot = (a1 >> 9) & 0x1F; + u16 *OAM = (u16 *)oam; + int dx = READ16LE(&OAM[3 + (rot << 4)]); + if(dx & 0x8000) + dx |= 0xFFFF8000; + int dmx = READ16LE(&OAM[7 + (rot << 4)]); + if(dmx & 0x8000) + dmx |= 0xFFFF8000; + int dy = READ16LE(&OAM[11 + (rot << 4)]); + if(dy & 0x8000) + dy |= 0xFFFF8000; + int dmy = READ16LE(&OAM[15 + (rot << 4)]); + if(dmy & 0x8000) + dmy |= 0xFFFF8000; + + int realX = ((sizeX) << 7) - (fieldX >> 1)*dx - (fieldY>>1)*dmx + + t * dmx; + int realY = ((sizeY) << 7) - (fieldX >> 1)*dy - (fieldY>>1)*dmy + + t * dmy; + + int c = (a2 & 0x3FF); + if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + bool condition1 = a0 & 0x2000; + + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 3; + + for(int x = 0; x < fieldX; x++) + { + bool cont = true; + if (x >= startpix) + lineOBJpix-=2; + if (lineOBJpix<0) + continue; + int xxx = realX >> 8; + int yyy = realY >> 8; + + if(xxx < 0 || xxx >= sizeX || yyy < 0 || yyy >= sizeY || sx >= 240) + cont = false; + + if(cont) + { + u32 color; + if(condition1) + color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + + (xxx & 7))&0x7fff)]; + else + { + color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + + ((xxx & 7)>>1))&0x7fff)]; + if(xxx & 1) + color >>= 4; + else + color &= 0x0F; + } + + if(color) + line[5][sx] = 1; + } + sx = (sx+1)&511; + realX += dx; + realY += dy; + } + } + } + } + else + { + if((sy+sizeY) > 256) + sy -= 256; + int t = io_registers[REG_VCOUNT] - sy; + if((t >= 0) && (t < sizeY)) + { + int sx = (a1 & 0x1FF); + int startpix = 0; + if ((sx+sizeX)> 512) + startpix=512-sx; + + if((sx < 240) || startpix) + { + lineOBJpix+=2; + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512)) + continue; + if(a0 & 0x2000) + { + + int inc = 32; + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX-1; + int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) + + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7))&0x7fff); + if(a1 & 0x1000) + xxx = 7; + for(int xx = 0; xx < sizeX; xx++) + { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) + { + u8 color = vram[address]; + if(color) + line[5][sx] = 1; + } + + sx = (sx+1) & 511; + if(a1 & 0x1000) { + xxx--; + address--; + if(xxx == -1) { + address -= 56; + xxx = 7; + } + if(address < 0x10000) + address += 0x8000; + } else { + xxx++; + address++; + if(xxx == 8) { + address += 56; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + else + { + int inc = 32; + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 3; + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX - 1; + int address = 0x10000 + ((((c + (t>>3) * inc)<<5) + + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7fff); + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + // int palette = (a2 >> 8) & 0xF0; + if(a1 & 0x1000) + { + xxx = 7; + for(int xx = sizeX - 1; xx >= 0; xx--) + { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) + { + u8 color = vram[address]; + if(xx & 1) + color = (color >> 4); + else + color &= 0x0F; + + if(color) + line[5][sx] = 1; + } + sx = (sx+1) & 511; + xxx--; + if(!(xx & 1)) + address--; + if(xxx == -1) { + xxx = 7; + address -= 28; + } + if(address < 0x10000) + address += 0x8000; + } + } + else + { + for(int xx = 0; xx < sizeX; xx++) + { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) + { + u8 color = vram[address]; + if(xx & 1) + color = (color >> 4); + else + color &= 0x0F; + + if(color) + line[5][sx] = 1; + } + sx = (sx+1) & 511; + xxx++; + if(xx & 1) + address++; + if(xxx == 8) { + address += 28; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + } + } + } + } +} + +INLINE u32 gfxIncreaseBrightness(u32 color, int coeff) +{ + color = (((color & 0xffff) << 16) | (color & 0xffff)) & 0x3E07C1F; + + color += ((((0x3E07C1F - color) * coeff) >> 4) & 0x3E07C1F); + + return (color >> 16) | color; +} + +INLINE u32 gfxDecreaseBrightness(u32 color, int coeff) +{ + color = (((color & 0xffff) << 16) | (color & 0xffff)) & 0x3E07C1F; + + color -= (((color * coeff) >> 4) & 0x3E07C1F); + + return (color >> 16) | color; +} + +#define GFX_ALPHA_BLEND(color, color2, ca, cb) \ + int r = AlphaClampLUT[(((color & 0x1F) * ca) >> 4) + (((color2 & 0x1F) * cb) >> 4)]; \ + int g = AlphaClampLUT[((((color >> 5) & 0x1F) * ca) >> 4) + ((((color2 >> 5) & 0x1F) * cb) >> 4)]; \ + int b = AlphaClampLUT[((((color >> 10) & 0x1F) * ca) >> 4) + ((((color2 >> 10) & 0x1F) * cb) >> 4)]; \ + color = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + +/*============================================================ + GBA.CPP +============================================================ */ +int saveType; +static const bool useBios = true; +bool skipBios; +bool cpuIsMultiBoot; +int cpuSaveType; +bool enableRtc; +bool mirroringEnable; +bool skipSaveGameBattery; + +int cpuDmaCount; + +uint8_t *bios; +uint8_t *rom; +uint8_t *internalRAM; +uint8_t *workRAM; +uint8_t *vram; +u16 *pix; +uint8_t *oam; +uint8_t *ioMem; + +#ifdef USE_SWITICKS +int SWITicks = 0; +#endif + +bool cpuSramEnabled; // = true; +bool cpuFlashEnabled; // = true; +bool cpuEEPROMEnabled; // = true; +bool cpuEEPROMSensorEnabled; // = false; + +#ifndef LSB_FIRST +bool cpuBiosSwapped = false; +#endif + +INLINE int CPUUpdateTicks (void) +{ + int cpuLoopTicks = graphics.lcdTicks; + + if(soundTicks < cpuLoopTicks) + cpuLoopTicks = soundTicks; + + if(timer0On && (timer0Ticks < cpuLoopTicks)) + cpuLoopTicks = timer0Ticks; + + if(timer1On && !(io_registers[REG_TM1CNT] & 4) && (timer1Ticks < cpuLoopTicks)) + cpuLoopTicks = timer1Ticks; + + if(timer2On && !(io_registers[REG_TM2CNT] & 4) && (timer2Ticks < cpuLoopTicks)) + cpuLoopTicks = timer2Ticks; + + if(timer3On && !(io_registers[REG_TM3CNT] & 4) && (timer3Ticks < cpuLoopTicks)) + cpuLoopTicks = timer3Ticks; + +#ifdef USE_SWITICKS + if (SWITicks) + { + if (SWITicks < cpuLoopTicks) + cpuLoopTicks = SWITicks; + } +#endif + + if (IRQTicks) + { + if (IRQTicks < cpuLoopTicks) + cpuLoopTicks = IRQTicks; + } + + return cpuLoopTicks; +} + +#define CPUUpdateWindow0() \ +{ \ + int x00_window0 = io_registers[REG_WIN0H] >>8; \ + int x01_window0 = io_registers[REG_WIN0H] & 255; \ + int x00_lte_x01 = x00_window0 <= x01_window0; \ + for(int i = 0; i < 240; i++) \ + gfxInWin[0][i] = ((i >= x00_window0 && i < x01_window0) & x00_lte_x01) | ((i >= x00_window0 || i < x01_window0) & ~x00_lte_x01); \ +} + +#define CPUUpdateWindow1() \ +{ \ + int x00_window1 = io_registers[REG_WIN1H]>>8; \ + int x01_window1 = io_registers[REG_WIN1H] & 255; \ + int x00_lte_x01 = x00_window1 <= x01_window1; \ + for(int i = 0; i < 240; i++) \ + gfxInWin[1][i] = ((i >= x00_window1 && i < x01_window1) & x00_lte_x01) | ((i >= x00_window1 || i < x01_window1) & ~x00_lte_x01); \ +} + +#define CPUCompareVCOUNT() \ + if(io_registers[REG_VCOUNT] == (io_registers[REG_DISPSTAT] >> 8)) \ + { \ + io_registers[REG_DISPSTAT] |= 4; \ + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); \ + if(io_registers[REG_DISPSTAT] & 0x20) \ + { \ + io_registers[REG_IF] |= 4; \ + UPDATE_REG(0x202, io_registers[REG_IF]); \ + } \ + } \ + else \ + { \ + io_registers[REG_DISPSTAT] &= 0xFFFB; \ + UPDATE_REG(0x4, io_registers[REG_DISPSTAT]); \ + } \ + if (graphics.layerEnableDelay > 0) \ + { \ + graphics.layerEnableDelay--; \ + if (graphics.layerEnableDelay == 1) \ + graphics.layerEnable = io_registers[REG_DISPCNT]; \ + } + +// TODO: Batteryram stuffs +/* +bool CPUWriteBatteryFile(const char *fileName) +{ + if(gbaSaveType == 0) + { + if(eepromInUse) + gbaSaveType = 3; + else + switch(saveType) + { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + + if((gbaSaveType) && (gbaSaveType!=5)) + { + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage("Error creating file %s", fileName); + return false; + } + + // only save if Flash/Sram in use or EEprom in use + if(gbaSaveType != 3) { + if(gbaSaveType == 2) { + if(fwrite(flashSaveMemory, 1, flashSize, file) != (size_t)flashSize) { + fclose(file); + return false; + } + } else { + if(fwrite(flashSaveMemory, 1, 0x10000, file) != 0x10000) { + fclose(file); + return false; + } + } + } else { + if(fwrite(eepromData, 1, eepromSize, file) != (size_t)eepromSize) { + fclose(file); + return false; + } + } + fclose(file); + } + return true; +} + +bool CPUReadBatteryFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) + return false; + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + long size = ftell(file); + fseek(file, 0, SEEK_SET); + + if(size == 512 || size == 0x2000) { + if(fread(eepromData, 1, size, file) != (size_t)size) { + fclose(file); + return false; + } + } else { + if(size == 0x20000) { + if(fread(flashSaveMemory, 1, 0x20000, file) != 0x20000) { + fclose(file); + return false; + } + flashSetSize(0x20000); + } else { + if(fread(flashSaveMemory, 1, 0x10000, file) != 0x10000) { + fclose(file); + return false; + } + flashSetSize(0x10000); + } + } + fclose(file); + return true; +} +*/ +void CPUCleanUp (void) +{ + if(rom != NULL) { + free(rom); + rom = NULL; + } + + if(vram != NULL) { + free(vram); + vram = NULL; + } + + if(graphics.paletteRAM != NULL) { + free(graphics.paletteRAM); + graphics.paletteRAM = NULL; + } + + if(internalRAM != NULL) { + free(internalRAM); + internalRAM = NULL; + } + + if(workRAM != NULL) { + free(workRAM); + workRAM = NULL; + } + + if(bios != NULL) { + free(bios); + bios = NULL; + } + + if(pix != NULL) { + free(pix); + pix = NULL; + } + + if(oam != NULL) { + free(oam); + oam = NULL; + } + + if(ioMem != NULL) { + free(ioMem); + ioMem = NULL; + } +} + +int CPULoadRom(const u8 *romfile, const u32 romfilelen) +{ + if (cpuIsMultiBoot) + { + if (romfilelen > 0x40000) + return 0; + } + else + { + if (romfilelen > 0x2000000) + return 0; + } + + romSize = 0x2000000; + if(rom != NULL) + CPUCleanUp(); + + rom = (uint8_t *)malloc(0x2000000); + + if(rom == NULL) + return 0; + + workRAM = (uint8_t *)calloc(1, 0x40000); + + if(workRAM == NULL) + return 0; + + uint8_t *whereToLoad = cpuIsMultiBoot ? workRAM : rom; + + memcpy(whereToLoad, romfile, romfilelen); + romSize = romfilelen; + + uint16_t *temp = (uint16_t *)(rom+((romSize+1)&~1)); + int i; + for(i = (romSize+1)&~1; i < 0x2000000; i+=2) { + WRITE16LE(temp, (i >> 1) & 0xFFFF); + temp++; + } + + bios = (uint8_t *)calloc(1,0x4000); + if(bios == NULL) { + CPUCleanUp(); + return 0; + } + internalRAM = (uint8_t *)calloc(1,0x8000); + if(internalRAM == NULL) { + CPUCleanUp(); + return 0; + } + graphics.paletteRAM = (uint8_t *)calloc(1,0x400); + if(graphics.paletteRAM == NULL) { + CPUCleanUp(); + return 0; + } + vram = (uint8_t *)calloc(1, 0x20000); + if(vram == NULL) { + CPUCleanUp(); + return 0; + } + oam = (uint8_t *)calloc(1, 0x400); + if(oam == NULL) { + CPUCleanUp(); + return 0; + } + pix = (u16 *)calloc(1, 4 * PIX_BUFFER_SCREEN_WIDTH * 160); + if(pix == NULL) { + CPUCleanUp(); + return 0; + } + ioMem = (uint8_t *)calloc(1, 0x400); + if(ioMem == NULL) { + CPUCleanUp(); + return 0; + } + + flashInit(); + eepromInit(); + + memset(line[0], -1, 240 * sizeof(u32)); + memset(line[1], -1, 240 * sizeof(u32)); + memset(line[2], -1, 240 * sizeof(u32)); + memset(line[3], -1, 240 * sizeof(u32)); + + return romSize; +} + +void doMirroring (bool b) +{ + uint32_t mirroredRomSize = (((romSize)>>20) & 0x3F)<<20; + uint32_t mirroredRomAddress = romSize; + if ((mirroredRomSize <=0x800000) && (b)) + { + mirroredRomAddress = mirroredRomSize; + if (mirroredRomSize==0) + mirroredRomSize=0x100000; + while (mirroredRomAddress<0x01000000) + { + memcpy((uint16_t *)(rom+mirroredRomAddress), (uint16_t *)(rom), mirroredRomSize); + mirroredRomAddress+=mirroredRomSize; + } + } +} + +#define brightness_switch() \ + switch((BLDMOD >> 6) & 3) \ + { \ + case 2: \ + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); \ + break; \ + case 3: \ + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); \ + break; \ + } + +#define alpha_blend_brightness_switch() \ + if(top2 & (BLDMOD>>8)) \ + if(color < 0x80000000) \ + { \ + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); \ + } \ + else if(BLDMOD & top) \ + { \ + brightness_switch(); \ + } + +/* we only use 16bit color depth */ +#define INIT_COLOR_DEPTH_LINE_MIX() uint16_t * lineMix = (pix + PIX_BUFFER_SCREEN_WIDTH * io_registers[REG_VCOUNT]) + +void mode0RenderLine (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 0: Render Line\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0100) { + gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); + } + + if(graphics.layerEnable & 0x0200) { + gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); + } + + if(graphics.layerEnable & 0x0400) { + gfxDrawTextScreen(io_registers[REG_BG2CNT], io_registers[REG_BG2HOFS], io_registers[REG_BG2VOFS], line[2]); + } + + if(graphics.layerEnable & 0x0800) { + gfxDrawTextScreen(io_registers[REG_BG3CNT], io_registers[REG_BG3HOFS], io_registers[REG_BG3VOFS], line[3]); + } + + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + uint32_t color = backdrop; + uint8_t top = 0x20; + + if(line[0][x] < color) { + color = line[0][x]; + top = 0x01; + } + + if((uint8_t)(line[1][x]>>24) < (uint8_t)(color >> 24)) { + color = line[1][x]; + top = 0x02; + } + + if((uint8_t)(line[2][x]>>24) < (uint8_t)(color >> 24)) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[3][x]>>24) < (uint8_t)(color >> 24)) { + color = line[3][x]; + top = 0x08; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { + color = line[4][x]; + top = 0x10; + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24)) { + back = line[0][x]; + top2 = 0x01; + } + + if((uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) { + back = line[1][x]; + top2 = 0x02; + } + + if((uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { + back = line[2][x]; + top2 = 0x04; + } + + if((uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) { + back = line[3][x]; + top2 = 0x08; + } + + alpha_blend_brightness_switch(); + } + } + + + lineMix[x] = CONVERT_COLOR(color); + } +} + +void mode0RenderLineNoWindow (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 0: Render Line No Window\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + if(graphics.layerEnable & 0x0100) { + gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); + } + + if(graphics.layerEnable & 0x0200) { + gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); + } + + if(graphics.layerEnable & 0x0400) { + gfxDrawTextScreen(io_registers[REG_BG2CNT], io_registers[REG_BG2HOFS], io_registers[REG_BG2VOFS], line[2]); + } + + if(graphics.layerEnable & 0x0800) { + gfxDrawTextScreen(io_registers[REG_BG3CNT], io_registers[REG_BG3HOFS], io_registers[REG_BG3VOFS], line[3]); + } + + int effect = (BLDMOD >> 6) & 3; + + for(int x = 0; x < 240; x++) { + uint32_t color = backdrop; + uint8_t top = 0x20; + + if(line[0][x] < color) { + color = line[0][x]; + top = 0x01; + } + + if(line[1][x] < (color & 0xFF000000)) { + color = line[1][x]; + top = 0x02; + } + + if(line[2][x] < (color & 0xFF000000)) { + color = line[2][x]; + top = 0x04; + } + + if(line[3][x] < (color & 0xFF000000)) { + color = line[3][x]; + top = 0x08; + } + + if(line[4][x] < (color & 0xFF000000)) { + color = line[4][x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch(effect) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + if((line[0][x] < back) && (top != 0x01)) + { + back = line[0][x]; + top2 = 0x01; + } + + if((line[1][x] < (back & 0xFF000000)) && (top != 0x02)) + { + back = line[1][x]; + top2 = 0x02; + } + + if((line[2][x] < (back & 0xFF000000)) && (top != 0x04)) + { + back = line[2][x]; + top2 = 0x04; + } + + if((line[3][x] < (back & 0xFF000000)) && (top != 0x08)) + { + back = line[3][x]; + top2 = 0x08; + } + + if((line[4][x] < (back & 0xFF000000)) && (top != 0x10)) + { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if(line[0][x] < back) { + back = line[0][x]; + top2 = 0x01; + } + + if(line[1][x] < (back & 0xFF000000)) { + back = line[1][x]; + top2 = 0x02; + } + + if(line[2][x] < (back & 0xFF000000)) { + back = line[2][x]; + top2 = 0x04; + } + + if(line[3][x] < (back & 0xFF000000)) { + back = line[3][x]; + top2 = 0x08; + } + + alpha_blend_brightness_switch(); + } + + lineMix[x] = CONVERT_COLOR(color); + } +} + +void mode0RenderLineAll (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 0: Render Line All\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + bool inWindow0 = false; + bool inWindow1 = false; + + if(graphics.layerEnable & 0x2000) { + uint8_t v0 = io_registers[REG_WIN0V] >> 8; + uint8_t v1 = io_registers[REG_WIN0V] & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); + } + if(graphics.layerEnable & 0x4000) { + uint8_t v0 = io_registers[REG_WIN1V] >> 8; + uint8_t v1 = io_registers[REG_WIN1V] & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); + } + + if((graphics.layerEnable & 0x0100)) { + gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); + } + + if((graphics.layerEnable & 0x0200)) { + gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); + } + + if((graphics.layerEnable & 0x0400)) { + gfxDrawTextScreen(io_registers[REG_BG2CNT], io_registers[REG_BG2HOFS], io_registers[REG_BG2VOFS], line[2]); + } + + if((graphics.layerEnable & 0x0800)) { + gfxDrawTextScreen(io_registers[REG_BG3CNT], io_registers[REG_BG3HOFS], io_registers[REG_BG3VOFS], line[3]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; + uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; + uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; + + for(int x = 0; x < 240; x++) { + uint32_t color = backdrop; + uint8_t top = 0x20; + uint8_t mask = outMask; + + if(!(line[5][x] & 0x80000000)) { + mask = io_registers[REG_WINOUT] >> 8; + } + + int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; + int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; + mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); + mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); + + if((mask & 1) && (line[0][x] < color)) { + color = line[0][x]; + top = 0x01; + } + + if((mask & 2) && ((uint8_t)(line[1][x]>>24) < (uint8_t)(color >> 24))) { + color = line[1][x]; + top = 0x02; + } + + if((mask & 4) && ((uint8_t)(line[2][x]>>24) < (uint8_t)(color >> 24))) { + color = line[2][x]; + top = 0x04; + } + + if((mask & 8) && ((uint8_t)(line[3][x]>>24) < (uint8_t)(color >> 24))) { + color = line[3][x]; + top = 0x08; + } + + if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24))) { + color = line[4][x]; + top = 0x10; + } + + if(color & 0x00010000) + { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 1) && ((uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24))) { + back = line[0][x]; + top2 = 0x01; + } + + if((mask & 2) && ((uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24))) { + back = line[1][x]; + top2 = 0x02; + } + + if((mask & 4) && ((uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24))) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 8) && ((uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24))) { + back = line[3][x]; + top2 = 0x08; + } + + alpha_blend_brightness_switch(); + } + else if((mask & 32) && (top & BLDMOD)) + { + // special FX on in the window + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + if(((mask & 1) && (uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24)) && top != 0x01) + { + back = line[0][x]; + top2 = 0x01; + } + + if(((mask & 2) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) && top != 0x02) + { + back = line[1][x]; + top2 = 0x02; + } + + if(((mask & 4) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) && top != 0x04) + { + back = line[2][x]; + top2 = 0x04; + } + + if(((mask & 8) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) && top != 0x08) + { + back = line[3][x]; + top2 = 0x08; + } + + if(((mask & 16) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) && top != 0x10) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = CONVERT_COLOR(color); + } +} + +/* +Mode 1 is a tiled graphics mode, but with background layer 2 supporting scaling and rotation. +There is no layer 3 in this mode. +Layers 0 and 1 can be either 16 colours (with 16 different palettes) or 256 colours. +There are 1024 tiles available. +Layer 2 is 256 colours and allows only 256 tiles. + +These routines only render a single line at a time, because of the way the GBA does events. +*/ + +void mode1RenderLine (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 1: Render Line\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0100) { + gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); + } + + if(graphics.layerEnable & 0x0200) { + gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); + } + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], + gfxBG2X, gfxBG2Y, changed, line[2]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(uint32_t x = 0; x < 240u; ++x) { + uint32_t color = backdrop; + uint8_t top = 0x20; + + uint8_t li1 = (uint8_t)(line[1][x]>>24); + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t li4 = (uint8_t)(line[4][x]>>24); + + uint8_t r = (li2 < li1) ? (li2) : (li1); + + if(li4 < r){ + r = (li4); + } + + if(line[0][x] < backdrop) { + color = line[0][x]; + top = 0x01; + } + + if(r < (uint8_t)(color >> 24)) { + if(r == li1){ + color = line[1][x]; + top = 0x02; + }else if(r == li2){ + color = line[2][x]; + top = 0x04; + }else if(r == li4){ + color = line[4][x]; + top = 0x10; + if((color & 0x00010000)) + { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + uint8_t li0 = (uint8_t)(line[0][x]>>24); + uint8_t li1 = (uint8_t)(line[1][x]>>24); + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t r = (li1 < li0) ? (li1) : (li0); + + if(li2 < r) { + r = (li2); + } + + if(r < (uint8_t)(back >> 24)) { + if(r == li0){ + back = line[0][x]; + top2 = 0x01; + }else if(r == li1){ + back = line[1][x]; + top2 = 0x02; + }else if(r == li2){ + back = line[2][x]; + top2 = 0x04; + } + } + + alpha_blend_brightness_switch(); + } + } + } + + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode1RenderLineNoWindow (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 1: Render Line No Window\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0100) { + gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); + } + + + if(graphics.layerEnable & 0x0200) { + gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); + } + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], + gfxBG2X, gfxBG2Y, changed, line[2]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = backdrop; + uint8_t top = 0x20; + + uint8_t li1 = (uint8_t)(line[1][x]>>24); + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t li4 = (uint8_t)(line[4][x]>>24); + + uint8_t r = (li2 < li1) ? (li2) : (li1); + + if(li4 < r){ + r = (li4); + } + + if(line[0][x] < backdrop) { + color = line[0][x]; + top = 0x01; + } + + if(r < (uint8_t)(color >> 24)) { + if(r == li1){ + color = line[1][x]; + top = 0x02; + }else if(r == li2){ + color = line[2][x]; + top = 0x04; + }else if(r == li4){ + color = line[4][x]; + top = 0x10; + } + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((top != 0x01) && (uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24)) { + back = line[0][x]; + top2 = 0x01; + } + + if((top != 0x02) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) { + back = line[1][x]; + top2 = 0x02; + } + + if((top != 0x04) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { + back = line[2][x]; + top2 = 0x04; + } + + if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + uint8_t li0 = (uint8_t)(line[0][x]>>24); + uint8_t li1 = (uint8_t)(line[1][x]>>24); + uint8_t li2 = (uint8_t)(line[2][x]>>24); + + uint8_t r = (li1 < li0) ? (li1) : (li0); + + if(li2 < r) { + r = (li2); + } + + if(r < (uint8_t)(back >> 24)) + { + if(r == li0) + { + back = line[0][x]; + top2 = 0x01; + } + else if(r == li1) + { + back = line[1][x]; + top2 = 0x02; + } + else if(r == li2) + { + back = line[2][x]; + top2 = 0x04; + } + } + + alpha_blend_brightness_switch(); + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode1RenderLineAll (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 1: Render Line All\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + bool inWindow0 = false; + bool inWindow1 = false; + + if(graphics.layerEnable & 0x2000) + { + uint8_t v0 = io_registers[REG_WIN0V] >> 8; + uint8_t v1 = io_registers[REG_WIN0V] & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + if(graphics.layerEnable & 0x4000) + { + uint8_t v0 = io_registers[REG_WIN1V] >> 8; + uint8_t v1 = io_registers[REG_WIN1V] & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x0100) { + gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); + } + + if(graphics.layerEnable & 0x0200) { + gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); + } + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], + gfxBG2X, gfxBG2Y, changed, line[2]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; + uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; + uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; + + for(int x = 0; x < 240; ++x) { + uint32_t color = backdrop; + uint8_t top = 0x20; + uint8_t mask = outMask; + + if(!(line[5][x] & 0x80000000)) { + mask = io_registers[REG_WINOUT] >> 8; + } + + int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; + int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; + mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); + mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); + + // At the very least, move the inexpensive 'mask' operation up front + if((mask & 1) && line[0][x] < backdrop) { + color = line[0][x]; + top = 0x01; + } + + if((mask & 2) && (uint8_t)(line[1][x]>>24) < (uint8_t)(color >> 24)) { + color = line[1][x]; + top = 0x02; + } + + if((mask & 4) && (uint8_t)(line[2][x]>>24) < (uint8_t)(color >> 24)) { + color = line[2][x]; + top = 0x04; + } + + if((mask & 16) && (uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { + color = line[4][x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 1) && (uint8_t)(line[0][x]>>24) < (uint8_t)(backdrop >> 24)) { + back = line[0][x]; + top2 = 0x01; + } + + if((mask & 2) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) { + back = line[1][x]; + top2 = 0x02; + } + + if((mask & 4) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } else if(mask & 32) { + // special FX on the window + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 1) && (top != 0x01) && (uint8_t)(line[0][x]>>24) < (uint8_t)(backdrop >> 24)) { + back = line[0][x]; + top2 = 0x01; + } + + if((mask & 2) && (top != 0x02) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) { + back = line[1][x]; + top2 = 0x02; + } + + if((mask & 4) && (top != 0x04) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +/* +Mode 2 is a 256 colour tiled graphics mode which supports scaling and rotation. +There is no background layer 0 or 1 in this mode. Only background layers 2 and 3. +There are 256 tiles available. +It does not support flipping. + +These routines only render a single line at a time, because of the way the GBA does events. +*/ + +void mode2RenderLine (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 2: Render Line\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], gfxBG2X, gfxBG2Y, + changed, line[2]); + } + + if(graphics.layerEnable & 0x0800) { + int changed = gfxBG3Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen(io_registers[REG_BG3CNT], BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + io_registers[REG_BG3PA], io_registers[REG_BG3PB], io_registers[REG_BG3PC], io_registers[REG_BG3PD], gfxBG3X, gfxBG3Y, + changed, line[3]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = backdrop; + uint8_t top = 0x20; + + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t li3 = (uint8_t)(line[3][x]>>24); + uint8_t li4 = (uint8_t)(line[4][x]>>24); + + uint8_t r = (li3 < li2) ? (li3) : (li2); + + if(li4 < r){ + r = (li4); + } + + if(r < (uint8_t)(color >> 24)) { + if(r == li2){ + color = line[2][x]; + top = 0x04; + }else if(r == li3){ + color = line[3][x]; + top = 0x08; + }else if(r == li4){ + color = line[4][x]; + top = 0x10; + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t li3 = (uint8_t)(line[3][x]>>24); + uint8_t r = (li3 < li2) ? (li3) : (li2); + + if(r < (uint8_t)(back >> 24)) { + if(r == li2){ + back = line[2][x]; + top2 = 0x04; + }else if(r == li3){ + back = line[3][x]; + top2 = 0x08; + } + } + + alpha_blend_brightness_switch(); + } + } + } + + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode2RenderLineNoWindow (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 2: Render Line No Window\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], gfxBG2X, gfxBG2Y, + changed, line[2]); + } + + if(graphics.layerEnable & 0x0800) { + int changed = gfxBG3Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen(io_registers[REG_BG3CNT], BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + io_registers[REG_BG3PA], io_registers[REG_BG3PB], io_registers[REG_BG3PC], io_registers[REG_BG3PD], gfxBG3X, gfxBG3Y, + changed, line[3]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = backdrop; + uint8_t top = 0x20; + + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t li3 = (uint8_t)(line[3][x]>>24); + uint8_t li4 = (uint8_t)(line[4][x]>>24); + + uint8_t r = (li3 < li2) ? (li3) : (li2); + + if(li4 < r){ + r = (li4); + } + + if(r < (uint8_t)(color >> 24)) { + if(r == li2){ + color = line[2][x]; + top = 0x04; + }else if(r == li3){ + color = line[3][x]; + top = 0x08; + }else if(r == li4){ + color = line[4][x]; + top = 0x10; + } + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((top != 0x04) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { + back = line[2][x]; + top2 = 0x04; + } + + if((top != 0x08) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) { + back = line[3][x]; + top2 = 0x08; + } + + if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t li3 = (uint8_t)(line[3][x]>>24); + uint8_t r = (li3 < li2) ? (li3) : (li2); + + if(r < (uint8_t)(back >> 24)) { + if(r == li2){ + back = line[2][x]; + top2 = 0x04; + }else if(r == li3){ + back = line[3][x]; + top2 = 0x08; + } + } + + alpha_blend_brightness_switch(); + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode2RenderLineAll (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 2: Render Line All\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + bool inWindow0 = false; + bool inWindow1 = false; + + if(graphics.layerEnable & 0x2000) + { + uint8_t v0 = io_registers[REG_WIN0V] >> 8; + uint8_t v1 = io_registers[REG_WIN0V] & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + if(graphics.layerEnable & 0x4000) + { + uint8_t v0 = io_registers[REG_WIN1V] >> 8; + uint8_t v1 = io_registers[REG_WIN1V] & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], gfxBG2X, gfxBG2Y, + changed, line[2]); + } + + if(graphics.layerEnable & 0x0800) { + int changed = gfxBG3Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen(io_registers[REG_BG3CNT], BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + io_registers[REG_BG3PA], io_registers[REG_BG3PB], io_registers[REG_BG3PC], io_registers[REG_BG3PD], gfxBG3X, gfxBG3Y, + changed, line[3]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; + uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; + uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; + + for(int x = 0; x < 240; x++) { + uint32_t color = backdrop; + uint8_t top = 0x20; + uint8_t mask = outMask; + + if(!(line[5][x] & 0x80000000)) { + mask = io_registers[REG_WINOUT] >> 8; + } + + int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; + int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; + mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); + mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); + + if((mask & 4) && line[2][x] < color) { + color = line[2][x]; + top = 0x04; + } + + if((mask & 8) && (uint8_t)(line[3][x]>>24) < (uint8_t)(color >> 24)) { + color = line[3][x]; + top = 0x08; + } + + if((mask & 16) && (uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { + color = line[4][x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 4) && line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 8) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) { + back = line[3][x]; + top2 = 0x08; + } + + alpha_blend_brightness_switch(); + } else if(mask & 32) { + // special FX on the window + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 4) && (top != 0x04) && line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 8) && (top != 0x08) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) { + back = line[3][x]; + top2 = 0x08; + } + + if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +/* +Mode 3 is a 15-bit (32768) colour bitmap graphics mode. +It has a single layer, background layer 2, the same size as the screen. +It doesn't support paging, scrolling, flipping, rotation or tiles. + +These routines only render a single line at a time, because of the way the GBA does events. +*/ + +void mode3RenderLine (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 3: Render Line\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen16Bit(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = background; + uint8_t top = 0x20; + + if(line[2][x] < color) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) { + color = line[4][x]; + top = 0x10; + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = background; + uint8_t top2 = 0x20; + + if(line[2][x] < background) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } + } + + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode3RenderLineNoWindow (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 3: Render Line No Window\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen16Bit(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = background; + uint8_t top = 0x20; + + if(line[2][x] < background) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) { + color = line[4][x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = background; + uint8_t top2 = 0x20; + + if(top != 0x04 && (line[2][x] < background) ) { + back = line[2][x]; + top2 = 0x04; + } + + if(top != 0x10 && ((uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24))) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + uint32_t back = background; + uint8_t top2 = 0x20; + + if(line[2][x] < background) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode3RenderLineAll (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 3: Render Line All\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + bool inWindow0 = false; + bool inWindow1 = false; + + if(graphics.layerEnable & 0x2000) + { + uint8_t v0 = io_registers[REG_WIN0V] >> 8; + uint8_t v1 = io_registers[REG_WIN0V] & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x4000) + { + uint8_t v0 = io_registers[REG_WIN1V] >> 8; + uint8_t v1 = io_registers[REG_WIN1V] & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen16Bit(gfxBG2X, gfxBG2Y, changed); + } + + uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; + uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; + uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; + + uint32_t background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = background; + uint8_t top = 0x20; + uint8_t mask = outMask; + + if(!(line[5][x] & 0x80000000)) { + mask = io_registers[REG_WINOUT] >> 8; + } + + int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; + int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; + mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); + mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); + + if((mask & 4) && line[2][x] < background) { + color = line[2][x]; + top = 0x04; + } + + if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24))) { + color = line[4][x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = background; + uint8_t top2 = 0x20; + + if((mask & 4) && line[2][x] < background) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } else if(mask & 32) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = background; + uint8_t top2 = 0x20; + + if((mask & 4) && (top != 0x04) && line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +/* +Mode 4 is a 256 colour bitmap graphics mode with 2 swappable pages. +It has a single layer, background layer 2, the same size as the screen. +It doesn't support scrolling, flipping, rotation or tiles. + +These routines only render a single line at a time, because of the way the GBA does events. +*/ + +void mode4RenderLine (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 4: Render Line\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x400) + { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen256(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) + { + uint32_t color = backdrop; + uint8_t top = 0x20; + + if(line[2][x] < backdrop) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { + color = line[4][x]; + top = 0x10; + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if(line[2][x] < backdrop) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } + } + + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode4RenderLineNoWindow (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 4: Render Line No Window\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x400) + { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen256(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) + { + uint32_t color = backdrop; + uint8_t top = 0x20; + + if(line[2][x] < backdrop) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { + color = line[4][x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((top != 0x04) && line[2][x] < backdrop) { + back = line[2][x]; + top2 = 0x04; + } + + if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if(line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode4RenderLineAll (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 4: Render Line All\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + bool inWindow0 = false; + bool inWindow1 = false; + + if(graphics.layerEnable & 0x2000) + { + uint8_t v0 = io_registers[REG_WIN0V] >> 8; + uint8_t v1 = io_registers[REG_WIN0V] & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x4000) + { + uint8_t v0 = io_registers[REG_WIN1V] >> 8; + uint8_t v1 = io_registers[REG_WIN1V] & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x400) + { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen256(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; + uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; + uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; + + for(int x = 0; x < 240; ++x) { + uint32_t color = backdrop; + uint8_t top = 0x20; + uint8_t mask = outMask; + + if(!(line[5][x] & 0x80000000)) + mask = io_registers[REG_WINOUT] >> 8; + + int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; + int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; + mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); + mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); + + if((mask & 4) && (line[2][x] < backdrop)) + { + color = line[2][x]; + top = 0x04; + } + + if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24))) + { + color = line[4][x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 4) && line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } else if(mask & 32) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 4) && (top != 0x04) && (line[2][x] < backdrop)) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +/* +Mode 5 is a low resolution (160x128) 15-bit colour bitmap graphics mode +with 2 swappable pages! +It has a single layer, background layer 2, lower resolution than the screen. +It doesn't support scrolling, flipping, rotation or tiles. + +These routines only render a single line at a time, because of the way the GBA does events. +*/ + +void mode5RenderLine (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 5: Render Line\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen16Bit160(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t background; + background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = background; + uint8_t top = 0x20; + + if(line[2][x] < background) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) { + color = line[4][x]; + top = 0x10; + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = background; + uint8_t top2 = 0x20; + + if(line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } + } + + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode5RenderLineNoWindow (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 5: Render Line No Window\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen16Bit160(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t background; + background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = background; + uint8_t top = 0x20; + + if(line[2][x] < background) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) { + color = line[4][x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = background; + uint8_t top2 = 0x20; + + if((top != 0x04) && line[2][x] < background) { + back = line[2][x]; + top2 = 0x04; + } + + if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + uint32_t back = background; + uint8_t top2 = 0x20; + + if(line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode5RenderLineAll (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 5: Render Line All\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen16Bit160(gfxBG2X, gfxBG2Y, changed); + } + + + + bool inWindow0 = false; + bool inWindow1 = false; + + if(graphics.layerEnable & 0x2000) + { + uint8_t v0 = io_registers[REG_WIN0V] >> 8; + uint8_t v1 = io_registers[REG_WIN0V] & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x4000) + { + uint8_t v0 = io_registers[REG_WIN1V] >> 8; + uint8_t v1 = io_registers[REG_WIN1V] & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; + uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; + uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; + + uint32_t background; + background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = background; + uint8_t top = 0x20; + uint8_t mask = outMask; + + if(!(line[5][x] & 0x80000000)) { + mask = io_registers[REG_WINOUT] >> 8; + } + + int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; + int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; + mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); + mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); + + if((mask & 4) && (line[2][x] < background)) { + color = line[2][x]; + top = 0x04; + } + + if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24))) { + color = line[4][x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = background; + uint8_t top2 = 0x20; + + if((mask & 4) && line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } else if(mask & 32) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = background; + uint8_t top2 = 0x20; + + if((mask & 4) && (top != 0x04) && (line[2][x] < background)) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void (Gigazoid::*renderLine)(void); +bool render_line_all_enabled; + +#define CPUUpdateRender() \ + render_line_all_enabled = false; \ + switch(io_registers[REG_DISPCNT] & 7) { \ + case 0: \ + if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ + renderLine = &Gigazoid::mode0RenderLine; \ + else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ + renderLine = &Gigazoid::mode0RenderLineNoWindow; \ + else { \ + renderLine = &Gigazoid::mode0RenderLineAll; \ + render_line_all_enabled = true; \ + } \ + break; \ + case 1: \ + if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ + renderLine = &Gigazoid::mode1RenderLine; \ + else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ + renderLine = &Gigazoid::mode1RenderLineNoWindow; \ + else { \ + renderLine = &Gigazoid::mode1RenderLineAll; \ + render_line_all_enabled = true; \ + } \ + break; \ + case 2: \ + if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ + renderLine = &Gigazoid::mode2RenderLine; \ + else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ + renderLine = &Gigazoid::mode2RenderLineNoWindow; \ + else { \ + renderLine = &Gigazoid::mode2RenderLineAll; \ + render_line_all_enabled = true; \ + } \ + break; \ + case 3: \ + if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ + renderLine = &Gigazoid::mode3RenderLine; \ + else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ + renderLine = &Gigazoid::mode3RenderLineNoWindow; \ + else { \ + renderLine = &Gigazoid::mode3RenderLineAll; \ + render_line_all_enabled = true; \ + } \ + break; \ + case 4: \ + if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ + renderLine = &Gigazoid::mode4RenderLine; \ + else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ + renderLine = &Gigazoid::mode4RenderLineNoWindow; \ + else { \ + renderLine = &Gigazoid::mode4RenderLineAll; \ + render_line_all_enabled = true; \ + } \ + break; \ + case 5: \ + if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ + renderLine = &Gigazoid::mode5RenderLine; \ + else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ + renderLine = &Gigazoid::mode5RenderLineNoWindow; \ + else { \ + renderLine = &Gigazoid::mode5RenderLineAll; \ + render_line_all_enabled = true; \ + } \ + } + +#define CPUSwap(a, b) \ +a ^= b; \ +b ^= a; \ +a ^= b; + +void CPUSwitchMode(int mode, bool saveState, bool breakLoop) +{ + CPU_UPDATE_CPSR(); + + switch(armMode) { + case 0x10: + case 0x1F: + bus.reg[R13_USR].I = bus.reg[13].I; + bus.reg[R14_USR].I = bus.reg[14].I; + bus.reg[17].I = bus.reg[16].I; + break; + case 0x11: + CPUSwap(bus.reg[R8_FIQ].I, bus.reg[8].I); + CPUSwap(bus.reg[R9_FIQ].I, bus.reg[9].I); + CPUSwap(bus.reg[R10_FIQ].I, bus.reg[10].I); + CPUSwap(bus.reg[R11_FIQ].I, bus.reg[11].I); + CPUSwap(bus.reg[R12_FIQ].I, bus.reg[12].I); + bus.reg[R13_FIQ].I = bus.reg[13].I; + bus.reg[R14_FIQ].I = bus.reg[14].I; + bus.reg[SPSR_FIQ].I = bus.reg[17].I; + break; + case 0x12: + bus.reg[R13_IRQ].I = bus.reg[13].I; + bus.reg[R14_IRQ].I = bus.reg[14].I; + bus.reg[SPSR_IRQ].I = bus.reg[17].I; + break; + case 0x13: + bus.reg[R13_SVC].I = bus.reg[13].I; + bus.reg[R14_SVC].I = bus.reg[14].I; + bus.reg[SPSR_SVC].I = bus.reg[17].I; + break; + case 0x17: + bus.reg[R13_ABT].I = bus.reg[13].I; + bus.reg[R14_ABT].I = bus.reg[14].I; + bus.reg[SPSR_ABT].I = bus.reg[17].I; + break; + case 0x1b: + bus.reg[R13_UND].I = bus.reg[13].I; + bus.reg[R14_UND].I = bus.reg[14].I; + bus.reg[SPSR_UND].I = bus.reg[17].I; + break; + } + + uint32_t CPSR = bus.reg[16].I; + uint32_t SPSR = bus.reg[17].I; + + switch(mode) { + case 0x10: + case 0x1F: + bus.reg[13].I = bus.reg[R13_USR].I; + bus.reg[14].I = bus.reg[R14_USR].I; + bus.reg[16].I = SPSR; + break; + case 0x11: + CPUSwap(bus.reg[8].I, bus.reg[R8_FIQ].I); + CPUSwap(bus.reg[9].I, bus.reg[R9_FIQ].I); + CPUSwap(bus.reg[10].I, bus.reg[R10_FIQ].I); + CPUSwap(bus.reg[11].I, bus.reg[R11_FIQ].I); + CPUSwap(bus.reg[12].I, bus.reg[R12_FIQ].I); + bus.reg[13].I = bus.reg[R13_FIQ].I; + bus.reg[14].I = bus.reg[R14_FIQ].I; + if(saveState) + bus.reg[17].I = CPSR; else + bus.reg[17].I = bus.reg[SPSR_FIQ].I; + break; + case 0x12: + bus.reg[13].I = bus.reg[R13_IRQ].I; + bus.reg[14].I = bus.reg[R14_IRQ].I; + bus.reg[16].I = SPSR; + if(saveState) + bus.reg[17].I = CPSR; + else + bus.reg[17].I = bus.reg[SPSR_IRQ].I; + break; + case 0x13: + bus.reg[13].I = bus.reg[R13_SVC].I; + bus.reg[14].I = bus.reg[R14_SVC].I; + bus.reg[16].I = SPSR; + if(saveState) + bus.reg[17].I = CPSR; + else + bus.reg[17].I = bus.reg[SPSR_SVC].I; + break; + case 0x17: + bus.reg[13].I = bus.reg[R13_ABT].I; + bus.reg[14].I = bus.reg[R14_ABT].I; + bus.reg[16].I = SPSR; + if(saveState) + bus.reg[17].I = CPSR; + else + bus.reg[17].I = bus.reg[SPSR_ABT].I; + break; + case 0x1b: + bus.reg[13].I = bus.reg[R13_UND].I; + bus.reg[14].I = bus.reg[R14_UND].I; + bus.reg[16].I = SPSR; + if(saveState) + bus.reg[17].I = CPSR; + else + bus.reg[17].I = bus.reg[SPSR_UND].I; + break; + default: + break; + } + armMode = mode; + CPUUpdateFlags(breakLoop); + CPU_UPDATE_CPSR(); +} + + + +void doDMA(uint32_t &s, uint32_t &d, uint32_t si, uint32_t di, uint32_t c, int transfer32) +{ + int sm = s >> 24; + int dm = d >> 24; + int sw = 0; + int dw = 0; + int sc = c; + + cpuDmaCount = c; + // This is done to get the correct waitstates. + int32_t sm_gt_15_mask = ((sm>15) | -(sm>15)) >> 31; + int32_t dm_gt_15_mask = ((dm>15) | -(dm>15)) >> 31; + sm = ((((15) & sm_gt_15_mask) | ((((sm) & ~(sm_gt_15_mask)))))); + dm = ((((15) & dm_gt_15_mask) | ((((dm) & ~(dm_gt_15_mask)))))); + + //if ((sm>=0x05) && (sm<=0x07) || (dm>=0x05) && (dm <=0x07)) + // blank = (((io_registers[REG_DISPSTAT] | ((io_registers[REG_DISPSTAT] >> 1)&1))==1) ? true : false); + + if(transfer32) + { + s &= 0xFFFFFFFC; + if(s < 0x02000000 && (bus.reg[15].I >> 24)) + { + do + { + CPUWriteMemory(d, 0); + d += di; + c--; + }while(c != 0); + } + else + { + do { + CPUWriteMemory(d, CPUReadMemory(s)); + d += di; + s += si; + c--; + }while(c != 0); + } + } + else + { + s &= 0xFFFFFFFE; + si = (int)si >> 1; + di = (int)di >> 1; + if(s < 0x02000000 && (bus.reg[15].I >> 24)) + { + do { + CPUWriteHalfWord(d, 0); + d += di; + c--; + }while(c != 0); + } + else + { + do{ + CPUWriteHalfWord(d, CPUReadHalfWord(s)); + d += di; + s += si; + c--; + }while(c != 0); + } + } + + cpuDmaCount = 0; + + if(transfer32) + { + sw = 1+memoryWaitSeq32[sm & 15]; + dw = 1+memoryWaitSeq32[dm & 15]; + cpuDmaTicksToUpdate += (sw+dw)*(sc-1) + 6 + memoryWait32[sm & 15] + memoryWaitSeq32[dm & 15]; + } + else + { + sw = 1+memoryWaitSeq[sm & 15]; + dw = 1+memoryWaitSeq[dm & 15]; + cpuDmaTicksToUpdate += (sw+dw)*(sc-1) + 6 + memoryWait[sm & 15] + memoryWaitSeq[dm & 15]; + } +} + + +void CPUCheckDMA(int reason, int dmamask) +{ + uint32_t arrayval[] = {4, (uint32_t)-4, 0, 4}; + // DMA 0 + if((DM0CNT_H & 0x8000) && (dmamask & 1)) + { + if(((DM0CNT_H >> 12) & 3) == reason) + { + uint32_t sourceIncrement, destIncrement; + uint32_t condition1 = ((DM0CNT_H >> 7) & 3); + uint32_t condition2 = ((DM0CNT_H >> 5) & 3); + sourceIncrement = arrayval[condition1]; + destIncrement = arrayval[condition2]; + doDMA(dma0Source, dma0Dest, sourceIncrement, destIncrement, + DM0CNT_L ? DM0CNT_L : 0x4000, + DM0CNT_H & 0x0400); + + if(DM0CNT_H & 0x4000) + { + io_registers[REG_IF] |= 0x0100; + UPDATE_REG(0x202, io_registers[REG_IF]); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM0CNT_H >> 5) & 3) == 3) { + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + } + + if(!(DM0CNT_H & 0x0200) || (reason == 0)) { + DM0CNT_H &= 0x7FFF; + UPDATE_REG(0xBA, DM0CNT_H); + } + } + } + + // DMA 1 + if((DM1CNT_H & 0x8000) && (dmamask & 2)) { + if(((DM1CNT_H >> 12) & 3) == reason) { + uint32_t sourceIncrement, destIncrement; + uint32_t condition1 = ((DM1CNT_H >> 7) & 3); + uint32_t condition2 = ((DM1CNT_H >> 5) & 3); + sourceIncrement = arrayval[condition1]; + destIncrement = arrayval[condition2]; + uint32_t di_value, c_value, transfer_value; + if(reason == 3) + { + di_value = 0; + c_value = 4; + transfer_value = 0x0400; + } + else + { + di_value = destIncrement; + c_value = DM1CNT_L ? DM1CNT_L : 0x4000; + transfer_value = DM1CNT_H & 0x0400; + } + doDMA(dma1Source, dma1Dest, sourceIncrement, di_value, c_value, transfer_value); + + if(DM1CNT_H & 0x4000) { + io_registers[REG_IF] |= 0x0200; + UPDATE_REG(0x202, io_registers[REG_IF]); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM1CNT_H >> 5) & 3) == 3) { + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + } + + if(!(DM1CNT_H & 0x0200) || (reason == 0)) { + DM1CNT_H &= 0x7FFF; + UPDATE_REG(0xC6, DM1CNT_H); + } + } + } + + // DMA 2 + if((DM2CNT_H & 0x8000) && (dmamask & 4)) { + if(((DM2CNT_H >> 12) & 3) == reason) { + uint32_t sourceIncrement, destIncrement; + uint32_t condition1 = ((DM2CNT_H >> 7) & 3); + uint32_t condition2 = ((DM2CNT_H >> 5) & 3); + sourceIncrement = arrayval[condition1]; + destIncrement = arrayval[condition2]; + uint32_t di_value, c_value, transfer_value; + if(reason == 3) + { + di_value = 0; + c_value = 4; + transfer_value = 0x0400; + } + else + { + di_value = destIncrement; + c_value = DM2CNT_L ? DM2CNT_L : 0x4000; + transfer_value = DM2CNT_H & 0x0400; + } + doDMA(dma2Source, dma2Dest, sourceIncrement, di_value, c_value, transfer_value); + + if(DM2CNT_H & 0x4000) { + io_registers[REG_IF] |= 0x0400; + UPDATE_REG(0x202, io_registers[REG_IF]); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM2CNT_H >> 5) & 3) == 3) { + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + } + + if(!(DM2CNT_H & 0x0200) || (reason == 0)) { + DM2CNT_H &= 0x7FFF; + UPDATE_REG(0xD2, DM2CNT_H); + } + } + } + + // DMA 3 + if((DM3CNT_H & 0x8000) && (dmamask & 8)) + { + if(((DM3CNT_H >> 12) & 3) == reason) + { + uint32_t sourceIncrement, destIncrement; + uint32_t condition1 = ((DM3CNT_H >> 7) & 3); + uint32_t condition2 = ((DM3CNT_H >> 5) & 3); + sourceIncrement = arrayval[condition1]; + destIncrement = arrayval[condition2]; + doDMA(dma3Source, dma3Dest, sourceIncrement, destIncrement, + DM3CNT_L ? DM3CNT_L : 0x10000, + DM3CNT_H & 0x0400); + if(DM3CNT_H & 0x4000) { + io_registers[REG_IF] |= 0x0800; + UPDATE_REG(0x202, io_registers[REG_IF]); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM3CNT_H >> 5) & 3) == 3) { + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + } + + if(!(DM3CNT_H & 0x0200) || (reason == 0)) { + DM3CNT_H &= 0x7FFF; + UPDATE_REG(0xDE, DM3CNT_H); + } + } + } +} + +uint16_t *address_lut[0x300]; + +void CPUUpdateRegister(uint32_t address, uint16_t value) +{ + switch(address) + { + case 0x00: + { + if((value & 7) > 5) // display modes above 0-5 are prohibited + io_registers[REG_DISPCNT] = (value & 7); + + bool change = (0 != ((io_registers[REG_DISPCNT] ^ value) & 0x80)); + bool changeBG = (0 != ((io_registers[REG_DISPCNT] ^ value) & 0x0F00)); + uint16_t changeBGon = ((~io_registers[REG_DISPCNT]) & value) & 0x0F00; // these layers are being activated + + io_registers[REG_DISPCNT] = (value & 0xFFF7); // bit 3 can only be accessed by the BIOS to enable GBC mode + UPDATE_REG(0x00, io_registers[REG_DISPCNT]); + + graphics.layerEnable = value; + + if(changeBGon) + { + graphics.layerEnableDelay = 4; + graphics.layerEnable &= ~changeBGon; + } + + windowOn = (graphics.layerEnable & 0x6000) ? true : false; + if(change && !((value & 0x80))) + { + if(!(io_registers[REG_DISPSTAT] & 1)) + { + graphics.lcdTicks = 1008; + io_registers[REG_DISPSTAT] &= 0xFFFC; + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + CPUCompareVCOUNT(); + } + } + CPUUpdateRender(); + // we only care about changes in BG0-BG3 + if(changeBG) + { + if(!(graphics.layerEnable & 0x0100)) + memset(line[0], -1, 240 * sizeof(u32)); + if(!(graphics.layerEnable & 0x0200)) + memset(line[1], -1, 240 * sizeof(u32)); + if(!(graphics.layerEnable & 0x0400)) + memset(line[2], -1, 240 * sizeof(u32)); + if(!(graphics.layerEnable & 0x0800)) + memset(line[3], -1, 240 * sizeof(u32)); + } + break; + } + case 0x04: + io_registers[REG_DISPSTAT] = (value & 0xFF38) | (io_registers[REG_DISPSTAT] & 7); + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + break; + case 0x06: + // not writable + break; + case 0x08: /* BG0CNT */ + case 0x0A: /* BG1CNT */ + *address_lut[address] = (value & 0xDFCF); + UPDATE_REG(address, *address_lut[address]); + break; + case 0x0C: /* BG2CNT */ + case 0x0E: /* BG3CNT */ + *address_lut[address] = (value & 0xFFCF); + UPDATE_REG(address, *address_lut[address]); + break; + case 0x10: /* BG0HOFS */ + case 0x12: /* BG0VOFS */ + case 0x14: /* BG1HOFS */ + case 0x16: /* BG1VOFS */ + case 0x18: /* BG2HOFS */ + case 0x1A: /* BG2VOFS */ + case 0x1C: /* BG3HOFS */ + case 0x1E: /* BG3VOFS */ + *address_lut[address] = value & 511; + UPDATE_REG(address, *address_lut[address]); + break; + case 0x20: /* BG2PA */ + case 0x22: /* BG2PB */ + case 0x24: /* BG2PC */ + case 0x26: /* BG2PD */ + *address_lut[address] = value; + UPDATE_REG(address, *address_lut[address]); + break; + case 0x28: + BG2X_L = value; + UPDATE_REG(0x28, BG2X_L); + gfxBG2Changed |= 1; + break; + case 0x2A: + BG2X_H = (value & 0xFFF); + UPDATE_REG(0x2A, BG2X_H); + gfxBG2Changed |= 1; + break; + case 0x2C: + BG2Y_L = value; + UPDATE_REG(0x2C, BG2Y_L); + gfxBG2Changed |= 2; + break; + case 0x2E: + BG2Y_H = value & 0xFFF; + UPDATE_REG(0x2E, BG2Y_H); + gfxBG2Changed |= 2; + break; + case 0x30: /* BG3PA */ + case 0x32: /* BG3PB */ + case 0x34: /* BG3PC */ + case 0x36: /* BG3PD */ + *address_lut[address] = value; + UPDATE_REG(address, *address_lut[address]); + break; + case 0x38: + BG3X_L = value; + UPDATE_REG(0x38, BG3X_L); + gfxBG3Changed |= 1; + break; + case 0x3A: + BG3X_H = value & 0xFFF; + UPDATE_REG(0x3A, BG3X_H); + gfxBG3Changed |= 1; + break; + case 0x3C: + BG3Y_L = value; + UPDATE_REG(0x3C, BG3Y_L); + gfxBG3Changed |= 2; + break; + case 0x3E: + BG3Y_H = value & 0xFFF; + UPDATE_REG(0x3E, BG3Y_H); + gfxBG3Changed |= 2; + break; + case 0x40: + io_registers[REG_WIN0H] = value; + UPDATE_REG(0x40, io_registers[REG_WIN0H]); + CPUUpdateWindow0(); + break; + case 0x42: + io_registers[REG_WIN1H] = value; + UPDATE_REG(0x42, io_registers[REG_WIN1H]); + CPUUpdateWindow1(); + break; + case 0x44: + case 0x46: + *address_lut[address] = value; + UPDATE_REG(address, *address_lut[address]); + break; + case 0x48: /* WININ */ + case 0x4A: /* WINOUT */ + *address_lut[address] = value & 0x3F3F; + UPDATE_REG(address, *address_lut[address]); + break; + case 0x4C: + MOSAIC = value; + UPDATE_REG(0x4C, MOSAIC); + break; + case 0x50: + BLDMOD = value & 0x3FFF; + UPDATE_REG(0x50, BLDMOD); + fxOn = ((BLDMOD>>6)&3) != 0; + CPUUpdateRender(); + break; + case 0x52: + COLEV = value & 0x1F1F; + UPDATE_REG(0x52, COLEV); + break; + case 0x54: + COLY = value & 0x1F; + UPDATE_REG(0x54, COLY); + break; + case 0x60: + case 0x62: + case 0x64: + case 0x68: + case 0x6c: + case 0x70: + case 0x72: + case 0x74: + case 0x78: + case 0x7c: + case 0x80: + case 0x84: + { + int gb_addr[2] = {address & 0xFF, (address & 0xFF) + 1}; + uint32_t address_array[2] = {address & 0xFF, (address&0xFF)+1}; + uint8_t data_array[2] = {(uint8_t)(value & 0xFF), (uint8_t)(value>>8)}; + gb_addr[0] = table[gb_addr[0] - 0x60]; + gb_addr[1] = table[gb_addr[1] - 0x60]; + soundEvent_u8_parallel(gb_addr, address_array, data_array); + break; + } + case 0x82: + case 0x88: + case 0xa0: + case 0xa2: + case 0xa4: + case 0xa6: + case 0x90: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + case 0x9a: + case 0x9c: + case 0x9e: + soundEvent_u16(address&0xFF, value); + break; + case 0xB0: + DM0SAD_L = value; + UPDATE_REG(0xB0, DM0SAD_L); + break; + case 0xB2: + DM0SAD_H = value & 0x07FF; + UPDATE_REG(0xB2, DM0SAD_H); + break; + case 0xB4: + DM0DAD_L = value; + UPDATE_REG(0xB4, DM0DAD_L); + break; + case 0xB6: + DM0DAD_H = value & 0x07FF; + UPDATE_REG(0xB6, DM0DAD_H); + break; + case 0xB8: + DM0CNT_L = value & 0x3FFF; + UPDATE_REG(0xB8, 0); + break; + case 0xBA: + { + bool start = ((DM0CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM0CNT_H = value; + UPDATE_REG(0xBA, DM0CNT_H); + + if(start && (value & 0x8000)) + { + dma0Source = DM0SAD_L | (DM0SAD_H << 16); + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + CPUCheckDMA(0, 1); + } + } + break; + case 0xBC: + DM1SAD_L = value; + UPDATE_REG(0xBC, DM1SAD_L); + break; + case 0xBE: + DM1SAD_H = value & 0x0FFF; + UPDATE_REG(0xBE, DM1SAD_H); + break; + case 0xC0: + DM1DAD_L = value; + UPDATE_REG(0xC0, DM1DAD_L); + break; + case 0xC2: + DM1DAD_H = value & 0x07FF; + UPDATE_REG(0xC2, DM1DAD_H); + break; + case 0xC4: + DM1CNT_L = value & 0x3FFF; + UPDATE_REG(0xC4, 0); + break; + case 0xC6: + { + bool start = ((DM1CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM1CNT_H = value; + UPDATE_REG(0xC6, DM1CNT_H); + + if(start && (value & 0x8000)) + { + dma1Source = DM1SAD_L | (DM1SAD_H << 16); + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + CPUCheckDMA(0, 2); + } + } + break; + case 0xC8: + DM2SAD_L = value; + UPDATE_REG(0xC8, DM2SAD_L); + break; + case 0xCA: + DM2SAD_H = value & 0x0FFF; + UPDATE_REG(0xCA, DM2SAD_H); + break; + case 0xCC: + DM2DAD_L = value; + UPDATE_REG(0xCC, DM2DAD_L); + break; + case 0xCE: + DM2DAD_H = value & 0x07FF; + UPDATE_REG(0xCE, DM2DAD_H); + break; + case 0xD0: + DM2CNT_L = value & 0x3FFF; + UPDATE_REG(0xD0, 0); + break; + case 0xD2: + { + bool start = ((DM2CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xF7E0; + + DM2CNT_H = value; + UPDATE_REG(0xD2, DM2CNT_H); + + if(start && (value & 0x8000)) { + dma2Source = DM2SAD_L | (DM2SAD_H << 16); + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + + CPUCheckDMA(0, 4); + } + } + break; + case 0xD4: + DM3SAD_L = value; + UPDATE_REG(0xD4, DM3SAD_L); + break; + case 0xD6: + DM3SAD_H = value & 0x0FFF; + UPDATE_REG(0xD6, DM3SAD_H); + break; + case 0xD8: + DM3DAD_L = value; + UPDATE_REG(0xD8, DM3DAD_L); + break; + case 0xDA: + DM3DAD_H = value & 0x0FFF; + UPDATE_REG(0xDA, DM3DAD_H); + break; + case 0xDC: + DM3CNT_L = value; + UPDATE_REG(0xDC, 0); + break; + case 0xDE: + { + bool start = ((DM3CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xFFE0; + + DM3CNT_H = value; + UPDATE_REG(0xDE, DM3CNT_H); + + if(start && (value & 0x8000)) { + dma3Source = DM3SAD_L | (DM3SAD_H << 16); + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + CPUCheckDMA(0,8); + } + } + break; + case 0x100: + timer0Reload = value; + break; + case 0x102: + timer0Value = value; + timerOnOffDelay|=1; + cpuNextEvent = cpuTotalTicks; + break; + case 0x104: + timer1Reload = value; + break; + case 0x106: + timer1Value = value; + timerOnOffDelay|=2; + cpuNextEvent = cpuTotalTicks; + break; + case 0x108: + timer2Reload = value; + break; + case 0x10A: + timer2Value = value; + timerOnOffDelay|=4; + cpuNextEvent = cpuTotalTicks; + break; + case 0x10C: + timer3Reload = value; + break; + case 0x10E: + timer3Value = value; + timerOnOffDelay|=8; + cpuNextEvent = cpuTotalTicks; + break; + case 0x130: + io_registers[REG_P1] |= (value & 0x3FF); + UPDATE_REG(0x130, io_registers[REG_P1]); + break; + case 0x132: + UPDATE_REG(0x132, value & 0xC3FF); + break; + + + case 0x200: + io_registers[REG_IE] = value & 0x3FFF; + UPDATE_REG(0x200, io_registers[REG_IE]); + if ((io_registers[REG_IME] & 1) && (io_registers[REG_IF] & io_registers[REG_IE]) && armIrqEnable) + cpuNextEvent = cpuTotalTicks; + break; + case 0x202: + io_registers[REG_IF] ^= (value & io_registers[REG_IF]); + UPDATE_REG(0x202, io_registers[REG_IF]); + break; + case 0x204: + { + memoryWait[0x0e] = memoryWaitSeq[0x0e] = gamepakRamWaitState[value & 3]; + + memoryWait[0x08] = memoryWait[0x09] = 3; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = 1; + + memoryWait[0x0a] = memoryWait[0x0b] = 3; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = 1; + + memoryWait[0x0c] = memoryWait[0x0d] = 3; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = 1; + + memoryWait32[8] = memoryWait[8] + memoryWaitSeq[8] + 1; + memoryWaitSeq32[8] = memoryWaitSeq[8]*2 + 1; + + memoryWait32[9] = memoryWait[9] + memoryWaitSeq[9] + 1; + memoryWaitSeq32[9] = memoryWaitSeq[9]*2 + 1; + + memoryWait32[10] = memoryWait[10] + memoryWaitSeq[10] + 1; + memoryWaitSeq32[10] = memoryWaitSeq[10]*2 + 1; + + memoryWait32[11] = memoryWait[11] + memoryWaitSeq[11] + 1; + memoryWaitSeq32[11] = memoryWaitSeq[11]*2 + 1; + + memoryWait32[12] = memoryWait[12] + memoryWaitSeq[12] + 1; + memoryWaitSeq32[12] = memoryWaitSeq[12]*2 + 1; + + memoryWait32[13] = memoryWait[13] + memoryWaitSeq[13] + 1; + memoryWaitSeq32[13] = memoryWaitSeq[13]*2 + 1; + + memoryWait32[14] = memoryWait[14] + memoryWaitSeq[14] + 1; + memoryWaitSeq32[14] = memoryWaitSeq[14]*2 + 1; + + if((value & 0x4000) == 0x4000) + bus.busPrefetchEnable = true; + else + bus.busPrefetchEnable = false; + + bus.busPrefetch = false; + bus.busPrefetchCount = 0; + + UPDATE_REG(0x204, value & 0x7FFF); + + } + break; + case 0x208: + io_registers[REG_IME] = value & 1; + UPDATE_REG(0x208, io_registers[REG_IME]); + if ((io_registers[REG_IME] & 1) && (io_registers[REG_IF] & io_registers[REG_IE]) && armIrqEnable) + cpuNextEvent = cpuTotalTicks; + break; + case 0x300: + if(value != 0) + value &= 0xFFFE; + UPDATE_REG(0x300, value); + break; + default: + UPDATE_REG(address&0x3FE, value); + break; + } +} + + +void CPUInit(const u8 *biosfile, const u32 biosfilelen) +{ +#ifndef LSB_FIRST + if(!cpuBiosSwapped) { + for(unsigned int i = 0; i < sizeof(myROM)/4; i++) { + WRITE32LE(&myROM[i], myROM[i]); + } + cpuBiosSwapped = true; + } +#endif + gbaSaveType = 0; + eepromInUse = 0; + saveType = 0; + + memcpy(bios, biosfile, 16384); + + int i = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + for(i = 0; i < 256; i++) + { + int count = 0; + int j; + for(j = 0; j < 8; j++) + if(i & (1 << j)) + count++; + cpuBitsSet[i] = count; + + for(j = 0; j < 8; j++) + if(i & (1 << j)) + break; + } + + for(i = 0; i < 0x400; i++) + ioReadable[i] = true; + for(i = 0x10; i < 0x48; i++) + ioReadable[i] = false; + for(i = 0x4c; i < 0x50; i++) + ioReadable[i] = false; + for(i = 0x54; i < 0x60; i++) + ioReadable[i] = false; + for(i = 0x8c; i < 0x90; i++) + ioReadable[i] = false; + for(i = 0xa0; i < 0xb8; i++) + ioReadable[i] = false; + for(i = 0xbc; i < 0xc4; i++) + ioReadable[i] = false; + for(i = 0xc8; i < 0xd0; i++) + ioReadable[i] = false; + for(i = 0xd4; i < 0xdc; i++) + ioReadable[i] = false; + for(i = 0xe0; i < 0x100; i++) + ioReadable[i] = false; + for(i = 0x110; i < 0x120; i++) + ioReadable[i] = false; + for(i = 0x12c; i < 0x130; i++) + ioReadable[i] = false; + for(i = 0x138; i < 0x140; i++) + ioReadable[i] = false; + for(i = 0x144; i < 0x150; i++) + ioReadable[i] = false; + for(i = 0x15c; i < 0x200; i++) + ioReadable[i] = false; + for(i = 0x20c; i < 0x300; i++) + ioReadable[i] = false; + for(i = 0x304; i < 0x400; i++) + ioReadable[i] = false; + + if(romSize < 0x1fe2000) { + *((uint16_t *)&rom[0x1fe209c]) = 0xdffa; // SWI 0xFA + *((uint16_t *)&rom[0x1fe209e]) = 0x4770; // BX LR + } + + graphics.layerEnable = 0xff00; + graphics.layerEnableDelay = 1; + io_registers[REG_DISPCNT] = 0x0080; + io_registers[REG_DISPSTAT] = 0; + graphics.lcdTicks = (useBios && !skipBios) ? 1008 : 208; + + /* address lut for use in CPUUpdateRegister */ + address_lut[0x08] = &io_registers[REG_BG0CNT]; + address_lut[0x0A] = &io_registers[REG_BG1CNT]; + address_lut[0x0C] = &io_registers[REG_BG2CNT]; + address_lut[0x0E] = &io_registers[REG_BG3CNT]; + address_lut[0x10] = &io_registers[REG_BG0HOFS]; + address_lut[0x12] = &io_registers[REG_BG0VOFS]; + address_lut[0x14] = &io_registers[REG_BG1HOFS]; + address_lut[0x16] = &io_registers[REG_BG1VOFS]; + address_lut[0x18] = &io_registers[REG_BG2HOFS]; + address_lut[0x1A] = &io_registers[REG_BG2VOFS]; + address_lut[0x1C] = &io_registers[REG_BG3HOFS]; + address_lut[0x1E] = &io_registers[REG_BG3VOFS]; + address_lut[0x20] = &io_registers[REG_BG2PA]; + address_lut[0x22] = &io_registers[REG_BG2PB]; + address_lut[0x24] = &io_registers[REG_BG2PC]; + address_lut[0x26] = &io_registers[REG_BG2PD]; + address_lut[0x48] = &io_registers[REG_WININ]; + address_lut[0x4A] = &io_registers[REG_WINOUT]; + address_lut[0x30] = &io_registers[REG_BG3PA]; + address_lut[0x32] = &io_registers[REG_BG3PB]; + address_lut[0x34] = &io_registers[REG_BG3PC]; + address_lut[0x36] = &io_registers[REG_BG3PD]; + address_lut[0x40] = &io_registers[REG_WIN0H]; + address_lut[0x42] = &io_registers[REG_WIN1H]; + address_lut[0x44] = &io_registers[REG_WIN0V]; + address_lut[0x46] = &io_registers[REG_WIN1V]; +} + +void CPUReset (void) +{ + if(gbaSaveType == 0) + { + if(eepromInUse) + gbaSaveType = 3; + else + switch(saveType) + { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + rtcReset(); + memset(&bus.reg[0], 0, sizeof(bus.reg)); // clean registers + memset(oam, 0, 0x400); // clean OAM + memset(graphics.paletteRAM, 0, 0x400); // clean palette + memset(pix, 0, 4 * 160 * 240); // clean picture + memset(vram, 0, 0x20000); // clean vram + memset(ioMem, 0, 0x400); // clean io memory + + io_registers[REG_DISPCNT] = 0x0080; + io_registers[REG_DISPSTAT] = 0x0000; + io_registers[REG_VCOUNT] = (useBios && !skipBios) ? 0 :0x007E; + io_registers[REG_BG0CNT] = 0x0000; + io_registers[REG_BG1CNT] = 0x0000; + io_registers[REG_BG2CNT] = 0x0000; + io_registers[REG_BG3CNT] = 0x0000; + io_registers[REG_BG0HOFS] = 0x0000; + io_registers[REG_BG0VOFS] = 0x0000; + io_registers[REG_BG1HOFS] = 0x0000; + io_registers[REG_BG1VOFS] = 0x0000; + io_registers[REG_BG2HOFS] = 0x0000; + io_registers[REG_BG2VOFS] = 0x0000; + io_registers[REG_BG3HOFS] = 0x0000; + io_registers[REG_BG3VOFS] = 0x0000; + io_registers[REG_BG2PA] = 0x0100; + io_registers[REG_BG2PB] = 0x0000; + io_registers[REG_BG2PC] = 0x0000; + io_registers[REG_BG2PD] = 0x0100; + BG2X_L = 0x0000; + BG2X_H = 0x0000; + BG2Y_L = 0x0000; + BG2Y_H = 0x0000; + io_registers[REG_BG3PA] = 0x0100; + io_registers[REG_BG3PB] = 0x0000; + io_registers[REG_BG3PC] = 0x0000; + io_registers[REG_BG3PD] = 0x0100; + BG3X_L = 0x0000; + BG3X_H = 0x0000; + BG3Y_L = 0x0000; + BG3Y_H = 0x0000; + io_registers[REG_WIN0H] = 0x0000; + io_registers[REG_WIN1H] = 0x0000; + io_registers[REG_WIN0V] = 0x0000; + io_registers[REG_WIN1V] = 0x0000; + io_registers[REG_WININ] = 0x0000; + io_registers[REG_WINOUT] = 0x0000; + MOSAIC = 0x0000; + BLDMOD = 0x0000; + COLEV = 0x0000; + COLY = 0x0000; + DM0SAD_L = 0x0000; + DM0SAD_H = 0x0000; + DM0DAD_L = 0x0000; + DM0DAD_H = 0x0000; + DM0CNT_L = 0x0000; + DM0CNT_H = 0x0000; + DM1SAD_L = 0x0000; + DM1SAD_H = 0x0000; + DM1DAD_L = 0x0000; + DM1DAD_H = 0x0000; + DM1CNT_L = 0x0000; + DM1CNT_H = 0x0000; + DM2SAD_L = 0x0000; + DM2SAD_H = 0x0000; + DM2DAD_L = 0x0000; + DM2DAD_H = 0x0000; + DM2CNT_L = 0x0000; + DM2CNT_H = 0x0000; + DM3SAD_L = 0x0000; + DM3SAD_H = 0x0000; + DM3DAD_L = 0x0000; + DM3DAD_H = 0x0000; + DM3CNT_L = 0x0000; + DM3CNT_H = 0x0000; + io_registers[REG_TM0D] = 0x0000; + io_registers[REG_TM0CNT] = 0x0000; + io_registers[REG_TM1D] = 0x0000; + io_registers[REG_TM1CNT] = 0x0000; + io_registers[REG_TM2D] = 0x0000; + io_registers[REG_TM2CNT] = 0x0000; + io_registers[REG_TM3D] = 0x0000; + io_registers[REG_TM3CNT] = 0x0000; + io_registers[REG_P1] = 0x03FF; + io_registers[REG_IE] = 0x0000; + io_registers[REG_IF] = 0x0000; + io_registers[REG_IME] = 0x0000; + + armMode = 0x1F; + + if(cpuIsMultiBoot) { + bus.reg[13].I = 0x03007F00; + bus.reg[15].I = 0x02000000; + bus.reg[16].I = 0x00000000; + bus.reg[R13_IRQ].I = 0x03007FA0; + bus.reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } else { +#ifdef HAVE_HLE_BIOS + if(useBios && !skipBios) + { + bus.reg[15].I = 0x00000000; + armMode = 0x13; + armIrqEnable = false; + } + else + { +#endif + bus.reg[13].I = 0x03007F00; + bus.reg[15].I = 0x08000000; + bus.reg[16].I = 0x00000000; + bus.reg[R13_IRQ].I = 0x03007FA0; + bus.reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; +#ifdef HAVE_HLE_BIOS + } +#endif + } + armState = true; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + UPDATE_REG(0x00, io_registers[REG_DISPCNT]); + UPDATE_REG(0x06, io_registers[REG_VCOUNT]); + UPDATE_REG(0x20, io_registers[REG_BG2PA]); + UPDATE_REG(0x26, io_registers[REG_BG2PD]); + UPDATE_REG(0x30, io_registers[REG_BG3PA]); + UPDATE_REG(0x36, io_registers[REG_BG3PD]); + UPDATE_REG(0x130, io_registers[REG_P1]); + UPDATE_REG(0x88, 0x200); + + // disable FIQ + bus.reg[16].I |= 0x40; + + CPU_UPDATE_CPSR(); + + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + + // reset internal state + holdState = false; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + graphics.lcdTicks = (useBios && !skipBios) ? 1008 : 208; + timer0On = false; + timer0Ticks = 0; + timer0Reload = 0; + timer0ClockReload = 0; + timer1On = false; + timer1Ticks = 0; + timer1Reload = 0; + timer1ClockReload = 0; + timer2On = false; + timer2Ticks = 0; + timer2Reload = 0; + timer2ClockReload = 0; + timer3On = false; + timer3Ticks = 0; + timer3Reload = 0; + timer3ClockReload = 0; + dma0Source = 0; + dma0Dest = 0; + dma1Source = 0; + dma1Dest = 0; + dma2Source = 0; + dma2Dest = 0; + dma3Source = 0; + dma3Dest = 0; + cpuSaveGameFunc = &Gigazoid::flashSaveDecide; + renderLine = &Gigazoid::mode0RenderLine; + fxOn = false; + windowOn = false; + saveType = 0; + graphics.layerEnable = io_registers[REG_DISPCNT]; + + memset(line[0], -1, 240 * sizeof(u32)); + memset(line[1], -1, 240 * sizeof(u32)); + memset(line[2], -1, 240 * sizeof(u32)); + memset(line[3], -1, 240 * sizeof(u32)); + + for(int i = 0; i < 256; i++) { + map[i].address = 0; + map[i].mask = 0; + } + + map[0].address = bios; + map[0].mask = 0x3FFF; + map[2].address = workRAM; + map[2].mask = 0x3FFFF; + map[3].address = internalRAM; + map[3].mask = 0x7FFF; + map[4].address = ioMem; + map[4].mask = 0x3FF; + map[5].address = graphics.paletteRAM; + map[5].mask = 0x3FF; + map[6].address = vram; + map[6].mask = 0x1FFFF; + map[7].address = oam; + map[7].mask = 0x3FF; + map[8].address = rom; + map[8].mask = 0x1FFFFFF; + map[9].address = rom; + map[9].mask = 0x1FFFFFF; + map[10].address = rom; + map[10].mask = 0x1FFFFFF; + map[12].address = rom; + map[12].mask = 0x1FFFFFF; + map[14].address = flashSaveMemory; + map[14].mask = 0xFFFF; + + eepromReset(); + flashReset(); + + soundReset(); + + CPUUpdateWindow0(); + CPUUpdateWindow1(); + + // make sure registers are correctly initialized if not using BIOS + if(cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + else if(!useBios && !cpuIsMultiBoot) + BIOS_RegisterRamReset(0xff); + + switch(cpuSaveType) { + case 0: // automatic + cpuSramEnabled = true; + cpuFlashEnabled = true; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + saveType = gbaSaveType = 0; + break; + case 1: // EEPROM + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + saveType = gbaSaveType = 3; + // EEPROM usage is automatically detected + break; + case 2: // SRAM + cpuSramEnabled = true; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = &Gigazoid::sramDelayedWrite; // to insure we detect the write + saveType = gbaSaveType = 1; + break; + case 3: // FLASH + cpuSramEnabled = false; + cpuFlashEnabled = true; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = &Gigazoid::flashDelayedWrite; // to insure we detect the write + saveType = gbaSaveType = 2; + break; + case 4: // EEPROM+Sensor + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = true; + // EEPROM usage is automatically detected + saveType = gbaSaveType = 3; + break; + case 5: // NONE + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + // no save at all + saveType = gbaSaveType = 5; + break; + } + + ARM_PREFETCH; + +#ifdef USE_SWITICKS + SWITicks = 0; +#endif +} + +void CPUInterrupt(void) +{ + uint32_t PC = bus.reg[15].I; + bool savedState = armState; + + if(armMode != 0x12 ) + CPUSwitchMode(0x12, true, false); + + bus.reg[14].I = PC; + if(!savedState) + bus.reg[14].I += 2; + bus.reg[15].I = 0x18; + armState = true; + armIrqEnable = false; + + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + ARM_PREFETCH; + + // if(!holdState) + biosProtected[0] = 0x02; + biosProtected[1] = 0xc0; + biosProtected[2] = 0x5e; + biosProtected[3] = 0xe5; +} + +void CPULoop (void) +{ + bus.busPrefetchCount = 0; + int ticks = 250000; + int timerOverflow = 0; + // variable used by the CPU core + cpuTotalTicks = 0; + + cpuNextEvent = CPUUpdateTicks(); + if(cpuNextEvent > ticks) + cpuNextEvent = ticks; + + + do + { + if(!holdState) + { + if(armState) + { + if (!armExecute()) + return; + } + else + { + if (!thumbExecute()) + return; + } + clockTicks = 0; + } + else + clockTicks = CPUUpdateTicks(); + + cpuTotalTicks += clockTicks; + + + if(cpuTotalTicks >= cpuNextEvent) { + int remainingTicks = cpuTotalTicks - cpuNextEvent; + +#ifdef USE_SWITICKS + if (SWITicks) + { + SWITicks-=clockTicks; + if (SWITicks<0) + SWITicks = 0; + } +#endif + + clockTicks = cpuNextEvent; + cpuTotalTicks = 0; + +updateLoop: + + if (IRQTicks) + { + IRQTicks -= clockTicks; + if (IRQTicks<0) + IRQTicks = 0; + } + + graphics.lcdTicks -= clockTicks; + + if(graphics.lcdTicks <= 0) + { + if(io_registers[REG_DISPSTAT] & 1) + { // V-BLANK + // if in V-Blank mode, keep computing... + if(io_registers[REG_DISPSTAT] & 2) + { + graphics.lcdTicks += 1008; + io_registers[REG_VCOUNT] += 1; + UPDATE_REG(0x06, io_registers[REG_VCOUNT]); + io_registers[REG_DISPSTAT] &= 0xFFFD; + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + CPUCompareVCOUNT(); + } + else + { + graphics.lcdTicks += 224; + io_registers[REG_DISPSTAT] |= 2; + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + if(io_registers[REG_DISPSTAT] & 16) + { + io_registers[REG_IF] |= 2; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + + if(io_registers[REG_VCOUNT] >= 228) + { + //Reaching last line + io_registers[REG_DISPSTAT] &= 0xFFFC; + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + io_registers[REG_VCOUNT] = 0; + UPDATE_REG(0x06, io_registers[REG_VCOUNT]); + CPUCompareVCOUNT(); + } + } + else if(io_registers[REG_DISPSTAT] & 2) + { + // if in H-Blank, leave it and move to drawing mode + io_registers[REG_VCOUNT] += 1; + UPDATE_REG(0x06, io_registers[REG_VCOUNT]); + + graphics.lcdTicks += 1008; + io_registers[REG_DISPSTAT] &= 0xFFFD; + if(io_registers[REG_VCOUNT] == 160) + { + /* update joystick information */ + io_registers[REG_P1] = 0x03FF ^ (joy & 0x3FF); +#if 0 + if(cpuEEPROMSensorEnabled) + systemUpdateMotionSensor(); +#endif + UPDATE_REG(0x130, io_registers[REG_P1]); + io_registers[REG_P1CNT] = READ16LE(((uint16_t *)&ioMem[0x132])); + + // this seems wrong, but there are cases where the game + // can enter the stop state without requesting an IRQ from + // the joypad. + if((io_registers[REG_P1CNT] & 0x4000) || stopState) { + uint16_t p1 = (0x3FF ^ io_registers[REG_P1CNT]) & 0x3FF; + if(io_registers[REG_P1CNT] & 0x8000) { + if(p1 == (io_registers[REG_P1CNT] & 0x3FF)) { + io_registers[REG_IF] |= 0x1000; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } else { + if(p1 & io_registers[REG_P1CNT]) { + io_registers[REG_IF] |= 0x1000; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + } + + io_registers[REG_DISPSTAT] |= 1; + io_registers[REG_DISPSTAT] &= 0xFFFD; + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + if(io_registers[REG_DISPSTAT] & 0x0008) + { + io_registers[REG_IF] |= 1; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + CPUCheckDMA(1, 0x0f); + systemDrawScreen(); + } + + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + CPUCompareVCOUNT(); + } + else + { + bool draw_objwin = (graphics.layerEnable & 0x9000) == 0x9000; + bool draw_sprites = graphics.layerEnable & 0x1000; + memset(line[4], -1, 240 * sizeof(u32)); // erase all sprites + + if(draw_sprites) + gfxDrawSprites(); + + if(render_line_all_enabled) + { + memset(line[5], -1, 240 * sizeof(u32)); // erase all OBJ Win + if(draw_objwin) + gfxDrawOBJWin(); + } + + (this->*renderLine)(); + + // entering H-Blank + io_registers[REG_DISPSTAT] |= 2; + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + graphics.lcdTicks += 224; + CPUCheckDMA(2, 0x0f); + if(io_registers[REG_DISPSTAT] & 16) + { + io_registers[REG_IF] |= 2; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + } + + // we shouldn't be doing sound in stop state, but we lose synchronization + // if sound is disabled, so in stop state, soundTick will just produce + // mute sound + soundTicks -= clockTicks; + if(!soundTicks) + { + process_sound_tick_fn(); + soundTicks += SOUND_CLOCK_TICKS; + } + + if(!stopState) { + if(timer0On) { + timer0Ticks -= clockTicks; + if(timer0Ticks <= 0) { + timer0Ticks += (0x10000 - timer0Reload) << timer0ClockReload; + timerOverflow |= 1; + soundTimerOverflow(0); + if(io_registers[REG_TM0CNT] & 0x40) { + io_registers[REG_IF] |= 0x08; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + io_registers[REG_TM0D] = 0xFFFF - (timer0Ticks >> timer0ClockReload); + UPDATE_REG(0x100, io_registers[REG_TM0D]); + } + + if(timer1On) { + if(io_registers[REG_TM1CNT] & 4) { + if(timerOverflow & 1) { + io_registers[REG_TM1D]++; + if(io_registers[REG_TM1D] == 0) { + io_registers[REG_TM1D] += timer1Reload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(io_registers[REG_TM1CNT] & 0x40) { + io_registers[REG_IF] |= 0x10; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + UPDATE_REG(0x104, io_registers[REG_TM1D]); + } + } else { + timer1Ticks -= clockTicks; + if(timer1Ticks <= 0) { + timer1Ticks += (0x10000 - timer1Reload) << timer1ClockReload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(io_registers[REG_TM1CNT] & 0x40) { + io_registers[REG_IF] |= 0x10; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + io_registers[REG_TM1D] = 0xFFFF - (timer1Ticks >> timer1ClockReload); + UPDATE_REG(0x104, io_registers[REG_TM1D]); + } + } + + if(timer2On) { + if(io_registers[REG_TM2CNT] & 4) { + if(timerOverflow & 2) { + io_registers[REG_TM2D]++; + if(io_registers[REG_TM2D] == 0) { + io_registers[REG_TM2D] += timer2Reload; + timerOverflow |= 4; + if(io_registers[REG_TM2CNT] & 0x40) { + io_registers[REG_IF] |= 0x20; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + UPDATE_REG(0x108, io_registers[REG_TM2D]); + } + } else { + timer2Ticks -= clockTicks; + if(timer2Ticks <= 0) { + timer2Ticks += (0x10000 - timer2Reload) << timer2ClockReload; + timerOverflow |= 4; + if(io_registers[REG_TM2CNT] & 0x40) { + io_registers[REG_IF] |= 0x20; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + io_registers[REG_TM2D] = 0xFFFF - (timer2Ticks >> timer2ClockReload); + UPDATE_REG(0x108, io_registers[REG_TM2D]); + } + } + + if(timer3On) { + if(io_registers[REG_TM3CNT] & 4) { + if(timerOverflow & 4) { + io_registers[REG_TM3D]++; + if(io_registers[REG_TM3D] == 0) { + io_registers[REG_TM3D] += timer3Reload; + if(io_registers[REG_TM3CNT] & 0x40) { + io_registers[REG_IF] |= 0x40; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + UPDATE_REG(0x10C, io_registers[REG_TM3D]); + } + } else { + timer3Ticks -= clockTicks; + if(timer3Ticks <= 0) { + timer3Ticks += (0x10000 - timer3Reload) << timer3ClockReload; + if(io_registers[REG_TM3CNT] & 0x40) { + io_registers[REG_IF] |= 0x40; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + io_registers[REG_TM3D] = 0xFFFF - (timer3Ticks >> timer3ClockReload); + UPDATE_REG(0x10C, io_registers[REG_TM3D]); + } + } + } + + timerOverflow = 0; + ticks -= clockTicks; + cpuNextEvent = CPUUpdateTicks(); + + if(cpuDmaTicksToUpdate > 0) + { + if(cpuDmaTicksToUpdate > cpuNextEvent) + clockTicks = cpuNextEvent; + else + clockTicks = cpuDmaTicksToUpdate; + cpuDmaTicksToUpdate -= clockTicks; + if(cpuDmaTicksToUpdate < 0) + cpuDmaTicksToUpdate = 0; + goto updateLoop; + } + + if(io_registers[REG_IF] && (io_registers[REG_IME] & 1) && armIrqEnable) + { + int res = io_registers[REG_IF] & io_registers[REG_IE]; + if(stopState) + res &= 0x3080; + if(res) + { + if (intState) + { + if (!IRQTicks) + { + CPUInterrupt(); + intState = false; + holdState = false; + stopState = false; + } + } + else + { + if (!holdState) + { + intState = true; + IRQTicks=7; + if (cpuNextEvent> IRQTicks) + cpuNextEvent = IRQTicks; + } + else + { + CPUInterrupt(); + holdState = false; + stopState = false; + } + } + +#ifdef USE_SWITICKS + // Stops the SWI Ticks emulation if an IRQ is executed + //(to avoid problems with nested IRQ/SWI) + if (SWITicks) + SWITicks = 0; +#endif + } + } + + if(remainingTicks > 0) { + if(remainingTicks > cpuNextEvent) + clockTicks = cpuNextEvent; + else + clockTicks = remainingTicks; + remainingTicks -= clockTicks; + if(remainingTicks < 0) + remainingTicks = 0; + goto updateLoop; + } + + if (timerOnOffDelay) + { + // Apply Timer + if (timerOnOffDelay & 1) + { + timer0ClockReload = TIMER_TICKS[timer0Value & 3]; + if(!timer0On && (timer0Value & 0x80)) { + // reload the counter + io_registers[REG_TM0D] = timer0Reload; + timer0Ticks = (0x10000 - io_registers[REG_TM0D]) << timer0ClockReload; + UPDATE_REG(0x100, io_registers[REG_TM0D]); + } + timer0On = timer0Value & 0x80 ? true : false; + io_registers[REG_TM0CNT] = timer0Value & 0xC7; + UPDATE_REG(0x102, io_registers[REG_TM0CNT]); + } + if (timerOnOffDelay & 2) + { + timer1ClockReload = TIMER_TICKS[timer1Value & 3]; + if(!timer1On && (timer1Value & 0x80)) { + // reload the counter + io_registers[REG_TM1D] = timer1Reload; + timer1Ticks = (0x10000 - io_registers[REG_TM1D]) << timer1ClockReload; + UPDATE_REG(0x104, io_registers[REG_TM1D]); + } + timer1On = timer1Value & 0x80 ? true : false; + io_registers[REG_TM1CNT] = timer1Value & 0xC7; + UPDATE_REG(0x106, io_registers[REG_TM1CNT]); + } + if (timerOnOffDelay & 4) + { + timer2ClockReload = TIMER_TICKS[timer2Value & 3]; + if(!timer2On && (timer2Value & 0x80)) { + // reload the counter + io_registers[REG_TM2D] = timer2Reload; + timer2Ticks = (0x10000 - io_registers[REG_TM2D]) << timer2ClockReload; + UPDATE_REG(0x108, io_registers[REG_TM2D]); + } + timer2On = timer2Value & 0x80 ? true : false; + io_registers[REG_TM2CNT] = timer2Value & 0xC7; + UPDATE_REG(0x10A, io_registers[REG_TM2CNT]); + } + if (timerOnOffDelay & 8) + { + timer3ClockReload = TIMER_TICKS[timer3Value & 3]; + if(!timer3On && (timer3Value & 0x80)) { + // reload the counter + io_registers[REG_TM3D] = timer3Reload; + timer3Ticks = (0x10000 - io_registers[REG_TM3D]) << timer3ClockReload; + UPDATE_REG(0x10C, io_registers[REG_TM3D]); + } + timer3On = timer3Value & 0x80 ? true : false; + io_registers[REG_TM3CNT] = timer3Value & 0xC7; + UPDATE_REG(0x10E, io_registers[REG_TM3CNT]); + } + cpuNextEvent = CPUUpdateTicks(); + timerOnOffDelay = 0; + // End of Apply Timer + } + + if(cpuNextEvent > ticks) + cpuNextEvent = ticks; + + if(ticks <= 0) + break; + + } + }while(1); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// END GBA.CPP +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Gigazoid_Init() +{ + // one time constructor stuff + + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + flashSize = 0x10000; + flashDeviceID = 0x1b; + flashManufacturerID = 0x32; + flashBank = 0; + + eepromMode = EEPROM_IDLE; + + eepromInUse = false; + eepromSize = 512; + + rtcEnabled = false; + + // this is fixed now + // soundSampleRate = 22050; + + SOUND_CLOCK_TICKS = SOUND_CLOCK_TICKS_; + soundTicks = SOUND_CLOCK_TICKS_; + + soundEnableFlag = 0x3ff; /* emulator channels enabled*/ + + armState = true; + armIrqEnable = true; + armMode = 0x1f; + + romSize = 0x2000000; + + cpuSramEnabled = true; + cpuFlashEnabled = true; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + + cpuSaveGameFunc = &Gigazoid::flashSaveDecide; + renderLine = &Gigazoid::mode0RenderLine; + + #define ARRAYINIT(n) memcpy((n), (n##_init), sizeof(n)) + ARRAYINIT(memoryWait); + ARRAYINIT(memoryWaitSeq); + ARRAYINIT(memoryWait32); + ARRAYINIT(memoryWaitSeq32); + #undef ARRAYINIT +} + +bool systemVideoFrameDone; +u32 *systemVideoFrameDest; + +void systemDrawScreen (void) +{ + systemVideoFrameDone = true; + // upconvert 555->888 (TODO: BETTER) + for (int i = 0; i < 240 * 160; i++) + { + u32 input = pix[i]; + u32 output = 0xff000000 | + input << 9 & 0xf80000 | + input << 6 & 0xf800 | + input << 3 & 0xf8; + systemVideoFrameDest[i] = output; + } +} + +// TODO: fix up RTC so this is used +//uint32_t systemGetClock (void) { return 0; } + +// TODO joypad: set "joy" lower 0x3ff + +void systemMessage(const char *, ...) +{ +} + +// called at regular intervals on sound clock +void systemOnWriteDataToSoundBuffer(int16_t * finalWave, int length) { } + +public: + Gigazoid() + { + Gigazoid_Init(); + } + + ~Gigazoid() + { + CPUCleanUp(); // todo: check me! + } + + bool LoadRom(const u8 *romfile, const u32 romfilelen, const u8 *biosfile, const u32 biosfilelen) + { + if (biosfilelen != 16384) + return false; + + // todo: set cpuismultiboot + if (!CPULoadRom(romfile, romfilelen)) + return false; + + // todo: populate these 4 variables from a syncsetting or a gamedb (vba_over.ini) + cpuSaveType = 0; + flashSize = 0x10000; + enableRtc = false; + mirroringEnable = false; + + if(flashSize == 0x10000 || flashSize == 0x20000) + flashSetSize(flashSize); + if(enableRtc) + rtcEnable(enableRtc); + doMirroring(mirroringEnable); + + CPUInit(biosfile, biosfilelen); + CPUReset(); + soundReset(); + + return true; + } + + void Reset() + { + CPUReset(); + } + + void FrameAdvance(int input, u32 *videobuffer) + { + joy = input; + systemVideoFrameDone = false; + systemVideoFrameDest = videobuffer; + do + { + CPULoop(); + } while (!systemVideoFrameDone); + systemVideoFrameDest = nullptr; + } + +}; // class Gigazoid + +// zeroing mem operators: these are very important +void *operator new(std::size_t n) +{ + void *p = std::malloc(n); + std::memset(p, 0, n); + return p; +} +void operator delete(void *p) +{ + std::free(p); +} + +#define EXPORT extern "C" __declspec(dllexport) + +// public interface follows +EXPORT Gigazoid *Create() +{ + return new Gigazoid(); +} + +EXPORT void Destroy(Gigazoid *g) +{ + delete g; +} + +EXPORT int LoadRom(Gigazoid *g, const u8 *romfile, const u32 romfilelen, const u8 *biosfile, const u32 biosfilelen) +{ + return g->LoadRom(romfile, romfilelen, biosfile, biosfilelen); +} + +EXPORT void Reset(Gigazoid *g) +{ + g->Reset(); +} + +EXPORT void FrameAdvance(Gigazoid *g, int input, u32 *videobuffer) +{ + g->FrameAdvance(input, videobuffer); +} + +#include "optable.inc" diff --git a/vbanext/instance.h b/vbanext/instance.h new file mode 100644 index 0000000000..0e7a5499cc --- /dev/null +++ b/vbanext/instance.h @@ -0,0 +1,194 @@ +#ifndef INSTANCE_H +#define INSTANCE_H + +#define FLASH_128K_SZ 0x20000 + +#define EEPROM_IDLE 0 +#define EEPROM_READADDRESS 1 +#define EEPROM_READDATA 2 +#define EEPROM_READDATA2 3 +#define EEPROM_WRITEDATA 4 + +enum { + IMAGE_UNKNOWN, + IMAGE_GBA +}; + +#define SGCNT0_H 0x82 +#define FIFOA_L 0xa0 +#define FIFOA_H 0xa2 +#define FIFOB_L 0xa4 +#define FIFOB_H 0xa6 + +#define BLIP_BUFFER_ACCURACY 16 +#define BLIP_PHASE_BITS 8 +#define BLIP_WIDEST_IMPULSE_ 16 +#define BLIP_BUFFER_EXTRA_ 18 +#define BLIP_RES 256 +#define BLIP_RES_MIN_ONE 255 +#define BLIP_SAMPLE_BITS 30 +#define BLIP_READER_DEFAULT_BASS 9 +#define BLIP_DEFAULT_LENGTH 250 /* 1/4th of a second */ + +#define BUFS_SIZE 3 +#define STEREO 2 + +#define CLK_MUL GB_APU_OVERCLOCK +#define CLK_MUL_MUL_2 8 +#define CLK_MUL_MUL_4 16 +#define CLK_MUL_MUL_6 24 +#define CLK_MUL_MUL_8 32 +#define CLK_MUL_MUL_15 60 +#define CLK_MUL_MUL_32 128 +#define DAC_BIAS 7 + +#define PERIOD_MASK 0x70 +#define SHIFT_MASK 0x07 + +#define PERIOD2_MASK 0x1FFFF + +#define BANK40_MASK 0x40 +#define BANK_SIZE 32 +#define BANK_SIZE_MIN_ONE 31 +#define BANK_SIZE_DIV_TWO 16 + +/* 11-bit frequency in NRx3 and NRx4*/ +#define GB_OSC_FREQUENCY() (((regs[4] & 7) << 8) + regs[3]) + +#define WAVE_TYPE 0x100 +#define NOISE_TYPE 0x200 +#define MIXED_TYPE WAVE_TYPE | NOISE_TYPE +#define TYPE_INDEX_MASK 0xFF + +#define BITS_16 0 +#define BITS_32 1 + +#define R13_IRQ 18 +#define R14_IRQ 19 +#define SPSR_IRQ 20 +#define R13_USR 26 +#define R14_USR 27 +#define R13_SVC 28 +#define R14_SVC 29 +#define SPSR_SVC 30 +#define R13_ABT 31 +#define R14_ABT 32 +#define SPSR_ABT 33 +#define R13_UND 34 +#define R14_UND 35 +#define SPSR_UND 36 +#define R8_FIQ 37 +#define R9_FIQ 38 +#define R10_FIQ 39 +#define R11_FIQ 40 +#define R12_FIQ 41 +#define R13_FIQ 42 +#define R14_FIQ 43 +#define SPSR_FIQ 44 + +typedef struct { + uint8_t *address; + uint32_t mask; +} memoryMap; + +typedef union { + struct { +#ifdef LSB_FIRST + uint8_t B0; + uint8_t B1; + uint8_t B2; + uint8_t B3; +#else + uint8_t B3; + uint8_t B2; + uint8_t B1; + uint8_t B0; +#endif + } B; + struct { +#ifdef LSB_FIRST + uint16_t W0; + uint16_t W1; +#else + uint16_t W1; + uint16_t W0; +#endif + } W; +#ifdef LSB_FIRST + uint32_t I; +#else + volatile uint32_t I; +#endif +} reg_pair; + +typedef struct +{ + reg_pair reg[45]; + bool busPrefetch; + bool busPrefetchEnable; + uint32_t busPrefetchCount; + uint32_t armNextPC; +} bus_t; + +typedef struct +{ + uint8_t * paletteRAM; + int layerEnable; + int layerEnableDelay; + int lcdTicks; +} graphics_t; + +/* Begins reading from buffer. Name should be unique to the current block.*/ +#define BLIP_READER_BEGIN( name, blip_buffer ) \ + const int32_t * name##_reader_buf = (blip_buffer).buffer_;\ + int32_t name##_reader_accum = (blip_buffer).reader_accum_ + +/* Advances to next sample*/ +#define BLIP_READER_NEXT( name, bass ) \ + (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) + +/* Ends reading samples from buffer. The number of samples read must now be removed + using Blip_Buffer::remove_samples(). */ +#define BLIP_READER_END( name, blip_buffer ) \ + (void) ((blip_buffer).reader_accum_ = name##_reader_accum) + +#define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset) + +#define BLIP_READER_NEXT_IDX_( name, idx ) {\ + name##_reader_accum -= name##_reader_accum >> BLIP_READER_DEFAULT_BASS;\ + name##_reader_accum += name##_reader_buf [(idx)];\ +} + +#define BLIP_READER_NEXT_RAW_IDX_( name, idx ) {\ + name##_reader_accum -= name##_reader_accum >> BLIP_READER_DEFAULT_BASS; \ + name##_reader_accum += *(int32_t const*) ((char const*) name##_reader_buf + (idx)); \ +} + +#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + #define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in +#else + #define BLIP_CLAMP_( in ) (int16_t) in != in +#endif + +/* Clamp sample to int16_t range */ +#define BLIP_CLAMP( sample, out ) { if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 24) ^ 0x7FFF; } +#define GB_ENV_DAC_ENABLED() (regs[2] & 0xF8) /* Non-zero if DAC is enabled*/ +#define GB_NOISE_PERIOD2_INDEX() (regs[3] >> 4) +#define GB_NOISE_PERIOD2(base) (base << GB_NOISE_PERIOD2_INDEX()) +#define GB_NOISE_LFSR_MASK() ((regs[3] & 0x08) ? ~0x4040 : ~0x4000) +#define GB_WAVE_DAC_ENABLED() (regs[0] & 0x80) /* Non-zero if DAC is enabled*/ + +#define reload_sweep_timer() \ + sweep_delay = (regs [0] & PERIOD_MASK) >> 4; \ + if ( !sweep_delay ) \ + sweep_delay = 8; + + +#ifdef __LIBRETRO__ +#define PIX_BUFFER_SCREEN_WIDTH 256 +#else +#define PIX_BUFFER_SCREEN_WIDTH 240 +#endif + +#endif diff --git a/vbanext/msvs/libvbanext/libvbanext.sln b/vbanext/msvs/libvbanext/libvbanext.sln new file mode 100644 index 0000000000..3b47db7b92 --- /dev/null +++ b/vbanext/msvs/libvbanext/libvbanext.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libvbanext", "libvbanext.vcxproj", "{DE666222-17A4-4B1E-8A5A-0A69838AE23F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DE666222-17A4-4B1E-8A5A-0A69838AE23F}.Debug|Win32.ActiveCfg = Debug|Win32 + {DE666222-17A4-4B1E-8A5A-0A69838AE23F}.Debug|Win32.Build.0 = Debug|Win32 + {DE666222-17A4-4B1E-8A5A-0A69838AE23F}.Release|Win32.ActiveCfg = Release|Win32 + {DE666222-17A4-4B1E-8A5A-0A69838AE23F}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vbanext/msvs/libvbanext/libvbanext.vcxproj b/vbanext/msvs/libvbanext/libvbanext.vcxproj new file mode 100644 index 0000000000..56cc5d8cb6 --- /dev/null +++ b/vbanext/msvs/libvbanext/libvbanext.vcxproj @@ -0,0 +1,88 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {DE666222-17A4-4B1E-8A5A-0A69838AE23F} + libvbanext + + + + DynamicLibrary + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + 4146;4800 + + + true + + + copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\..\..\output\dll\$(TargetFileName) + + + + + Level3 + Full + true + true + 4146;4800 + AnySuitable + true + + + true + true + true + + + copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\..\..\output\dll\$(TargetFileName) + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vbanext/msvs/libvbanext/libvbanext.vcxproj.filters b/vbanext/msvs/libvbanext/libvbanext.vcxproj.filters new file mode 100644 index 0000000000..3deff3727c --- /dev/null +++ b/vbanext/msvs/libvbanext/libvbanext.vcxproj.filters @@ -0,0 +1,47 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {25bb448a-4359-4425-bc82-a36c2dcddcb8} + + + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Header Files + + + \ No newline at end of file diff --git a/vbanext/optable.inc b/vbanext/optable.inc new file mode 100644 index 0000000000..d0cbc9eb00 --- /dev/null +++ b/vbanext/optable.inc @@ -0,0 +1,327 @@ +#define F(fcn) &Gigazoid::fcn + +#define REP16(insn) \ + F(insn),F(insn),F(insn),F(insn),F(insn),F(insn),F(insn),F(insn),\ + F(insn),F(insn),F(insn),F(insn),F(insn),F(insn),F(insn),F(insn) +#define REP256(insn) \ + REP16(insn),REP16(insn),REP16(insn),REP16(insn),\ + REP16(insn),REP16(insn),REP16(insn),REP16(insn),\ + REP16(insn),REP16(insn),REP16(insn),REP16(insn),\ + REP16(insn),REP16(insn),REP16(insn),REP16(insn) + +#define arm_UI armUnknownInsn +#define arm_BP armUnknownInsn + +#define thumbUI thumbUnknownInsn +#define thumbBP thumbUnknownInsn + +void (Gigazoid::*const Gigazoid::armInsnTable[4096])(u32 opcode) = { + F(arm000),F(arm001),F(arm002),F(arm003),F(arm004),F(arm005),F(arm006),F(arm007), // F(000) + F(arm000),F(arm009),F(arm002),F(arm00B),F(arm004),F(arm_UI),F(arm006),F(arm_UI), // F(008) + F(arm010),F(arm011),F(arm012),F(arm013),F(arm014),F(arm015),F(arm016),F(arm017), // F(010) + F(arm010),F(arm019),F(arm012),F(arm01B),F(arm014),F(arm01D),F(arm016),F(arm01F), // F(018) + F(arm020),F(arm021),F(arm022),F(arm023),F(arm024),F(arm025),F(arm026),F(arm027), // F(020) + F(arm020),F(arm029),F(arm022),F(arm_UI),F(arm024),F(arm_UI),F(arm026),F(arm_UI), // F(028) + F(arm030),F(arm031),F(arm032),F(arm033),F(arm034),F(arm035),F(arm036),F(arm037), // F(030) + F(arm030),F(arm039),F(arm032),F(arm_UI),F(arm034),F(arm01D),F(arm036),F(arm01F), // F(038) + F(arm040),F(arm041),F(arm042),F(arm043),F(arm044),F(arm045),F(arm046),F(arm047), // F(040) + F(arm040),F(arm_UI),F(arm042),F(arm04B),F(arm044),F(arm_UI),F(arm046),F(arm_UI), // F(048) + F(arm050),F(arm051),F(arm052),F(arm053),F(arm054),F(arm055),F(arm056),F(arm057), // F(050) + F(arm050),F(arm_UI),F(arm052),F(arm05B),F(arm054),F(arm05D),F(arm056),F(arm05F), // F(058) + F(arm060),F(arm061),F(arm062),F(arm063),F(arm064),F(arm065),F(arm066),F(arm067), // F(060) + F(arm060),F(arm_UI),F(arm062),F(arm_UI),F(arm064),F(arm_UI),F(arm066),F(arm_UI), // F(068) + F(arm070),F(arm071),F(arm072),F(arm073),F(arm074),F(arm075),F(arm076),F(arm077), // F(070) + F(arm070),F(arm_UI),F(arm072),F(arm_UI),F(arm074),F(arm05D),F(arm076),F(arm05F), // F(078) + F(arm080),F(arm081),F(arm082),F(arm083),F(arm084),F(arm085),F(arm086),F(arm087), // F(080) + F(arm080),F(arm089),F(arm082),F(arm08B),F(arm084),F(arm_UI),F(arm086),F(arm_UI), // F(088) + F(arm090),F(arm091),F(arm092),F(arm093),F(arm094),F(arm095),F(arm096),F(arm097), // F(090) + F(arm090),F(arm099),F(arm092),F(arm09B),F(arm094),F(arm09D),F(arm096),F(arm09F), // F(098) + F(arm0A0),F(arm0A1),F(arm0A2),F(arm0A3),F(arm0A4),F(arm0A5),F(arm0A6),F(arm0A7), // F(0A0) + F(arm0A0),F(arm0A9),F(arm0A2),F(arm_UI),F(arm0A4),F(arm_UI),F(arm0A6),F(arm_UI), // F(0A8) + F(arm0B0),F(arm0B1),F(arm0B2),F(arm0B3),F(arm0B4),F(arm0B5),F(arm0B6),F(arm0B7), // F(0B0) + F(arm0B0),F(arm0B9),F(arm0B2),F(arm_UI),F(arm0B4),F(arm09D),F(arm0B6),F(arm09F), // F(0B8) + F(arm0C0),F(arm0C1),F(arm0C2),F(arm0C3),F(arm0C4),F(arm0C5),F(arm0C6),F(arm0C7), // F(0C0) + F(arm0C0),F(arm0C9),F(arm0C2),F(arm0CB),F(arm0C4),F(arm_UI),F(arm0C6),F(arm_UI), // F(0C8) + F(arm0D0),F(arm0D1),F(arm0D2),F(arm0D3),F(arm0D4),F(arm0D5),F(arm0D6),F(arm0D7), // F(0D0) + F(arm0D0),F(arm0D9),F(arm0D2),F(arm0DB),F(arm0D4),F(arm0DD),F(arm0D6),F(arm0DF), // F(0D8) + F(arm0E0),F(arm0E1),F(arm0E2),F(arm0E3),F(arm0E4),F(arm0E5),F(arm0E6),F(arm0E7), // F(0E0) + F(arm0E0),F(arm0E9),F(arm0E2),F(arm_UI),F(arm0E4),F(arm_UI),F(arm0E6),F(arm_UI), // F(0E8) + F(arm0F0),F(arm0F1),F(arm0F2),F(arm0F3),F(arm0F4),F(arm0F5),F(arm0F6),F(arm0F7), // F(0F0) + F(arm0F0),F(arm0F9),F(arm0F2),F(arm_UI),F(arm0F4),F(arm0DD),F(arm0F6),F(arm0DF), // F(0F8) + + F(arm100),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(100) + F(arm_UI),F(arm109),F(arm_UI),F(arm10B),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(108) + F(arm110),F(arm111),F(arm112),F(arm113),F(arm114),F(arm115),F(arm116),F(arm117), // F(110) + F(arm110),F(arm_UI),F(arm112),F(arm11B),F(arm114),F(arm11D),F(arm116),F(arm11F), // F(118) + F(arm120),F(arm121),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_BP), // F(120) + F(arm_UI),F(arm_UI),F(arm_UI),F(arm12B),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(128) + F(arm130),F(arm131),F(arm132),F(arm133),F(arm134),F(arm135),F(arm136),F(arm137), // F(130) + F(arm130),F(arm_UI),F(arm132),F(arm13B),F(arm134),F(arm13D),F(arm136),F(arm13F), // F(138) + F(arm140),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(140) + F(arm_UI),F(arm149),F(arm_UI),F(arm14B),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(148) + F(arm150),F(arm151),F(arm152),F(arm153),F(arm154),F(arm155),F(arm156),F(arm157), // F(150) + F(arm150),F(arm_UI),F(arm152),F(arm15B),F(arm154),F(arm15D),F(arm156),F(arm15F), // F(158) + F(arm160),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(160) + F(arm_UI),F(arm_UI),F(arm_UI),F(arm16B),F(arm_UI),F(arm_UI),F(arm_UI),F(arm_UI), // F(168) + F(arm170),F(arm171),F(arm172),F(arm173),F(arm174),F(arm175),F(arm176),F(arm177), // F(170) + F(arm170),F(arm_UI),F(arm172),F(arm17B),F(arm174),F(arm17D),F(arm176),F(arm17F), // F(178) + F(arm180),F(arm181),F(arm182),F(arm183),F(arm184),F(arm185),F(arm186),F(arm187), // F(180) + F(arm180),F(arm_UI),F(arm182),F(arm18B),F(arm184),F(arm_UI),F(arm186),F(arm_UI), // F(188) + F(arm190),F(arm191),F(arm192),F(arm193),F(arm194),F(arm195),F(arm196),F(arm197), // F(190) + F(arm190),F(arm_UI),F(arm192),F(arm19B),F(arm194),F(arm19D),F(arm196),F(arm19F), // F(198) + F(arm1A0),F(arm1A1),F(arm1A2),F(arm1A3),F(arm1A4),F(arm1A5),F(arm1A6),F(arm1A7), // F(1A0) + F(arm1A0),F(arm_UI),F(arm1A2),F(arm1AB),F(arm1A4),F(arm_UI),F(arm1A6),F(arm_UI), // F(1A8) + F(arm1B0),F(arm1B1),F(arm1B2),F(arm1B3),F(arm1B4),F(arm1B5),F(arm1B6),F(arm1B7), // F(1B0) + F(arm1B0),F(arm_UI),F(arm1B2),F(arm1BB),F(arm1B4),F(arm1BD),F(arm1B6),F(arm1BF), // F(1B8) + F(arm1C0),F(arm1C1),F(arm1C2),F(arm1C3),F(arm1C4),F(arm1C5),F(arm1C6),F(arm1C7), // F(1C0) + F(arm1C0),F(arm_UI),F(arm1C2),F(arm1CB),F(arm1C4),F(arm_UI),F(arm1C6),F(arm_UI), // F(1C8) + F(arm1D0),F(arm1D1),F(arm1D2),F(arm1D3),F(arm1D4),F(arm1D5),F(arm1D6),F(arm1D7), // F(1D0) + F(arm1D0),F(arm_UI),F(arm1D2),F(arm1DB),F(arm1D4),F(arm1DD),F(arm1D6),F(arm1DF), // F(1D8) + F(arm1E0),F(arm1E1),F(arm1E2),F(arm1E3),F(arm1E4),F(arm1E5),F(arm1E6),F(arm1E7), // F(1E0) + F(arm1E0),F(arm_UI),F(arm1E2),F(arm1EB),F(arm1E4),F(arm_UI),F(arm1E6),F(arm_UI), // F(1E8) + F(arm1F0),F(arm1F1),F(arm1F2),F(arm1F3),F(arm1F4),F(arm1F5),F(arm1F6),F(arm1F7), // F(1F0) + F(arm1F0),F(arm_UI),F(arm1F2),F(arm1FB),F(arm1F4),F(arm1FD),F(arm1F6),F(arm1FF), // F(1F8) + + REP16(arm200),REP16(arm210),REP16(arm220),REP16(arm230), // 200 + REP16(arm240),REP16(arm250),REP16(arm260),REP16(arm270), // 240 + REP16(arm280),REP16(arm290),REP16(arm2A0),REP16(arm2B0), // 280 + REP16(arm2C0),REP16(arm2D0),REP16(arm2E0),REP16(arm2F0), // 2C0 + REP16(arm_UI),REP16(arm310),REP16(arm320),REP16(arm330), // 300 + REP16(arm_UI),REP16(arm350),REP16(arm360),REP16(arm370), // 340 + REP16(arm380),REP16(arm390),REP16(arm3A0),REP16(arm3B0), // 380 + REP16(arm3C0),REP16(arm3D0),REP16(arm3E0),REP16(arm3F0), // 3C0 + + REP16(arm400),REP16(arm410),REP16(arm400),REP16(arm410), // 400 + REP16(arm440),REP16(arm450),REP16(arm440),REP16(arm450), // 440 + REP16(arm480),REP16(arm490),REP16(arm480),REP16(arm490), // 480 + REP16(arm4C0),REP16(arm4D0),REP16(arm4C0),REP16(arm4D0), // 4C0 + REP16(arm500),REP16(arm510),REP16(arm520),REP16(arm530), // 500 + REP16(arm540),REP16(arm550),REP16(arm560),REP16(arm570), // 540 + REP16(arm580),REP16(arm590),REP16(arm5A0),REP16(arm5B0), // 580 + REP16(arm5C0),REP16(arm5D0),REP16(arm5E0),REP16(arm5F0), // 5C0 + + F(arm600),F(arm_UI),F(arm602),F(arm_UI),F(arm604),F(arm_UI),F(arm606),F(arm_UI), // F(600) + F(arm600),F(arm_UI),F(arm602),F(arm_UI),F(arm604),F(arm_UI),F(arm606),F(arm_UI), // F(608) + F(arm610),F(arm_UI),F(arm612),F(arm_UI),F(arm614),F(arm_UI),F(arm616),F(arm_UI), // F(610) + F(arm610),F(arm_UI),F(arm612),F(arm_UI),F(arm614),F(arm_UI),F(arm616),F(arm_UI), // F(618) + F(arm600),F(arm_UI),F(arm602),F(arm_UI),F(arm604),F(arm_UI),F(arm606),F(arm_UI), // F(620) + F(arm600),F(arm_UI),F(arm602),F(arm_UI),F(arm604),F(arm_UI),F(arm606),F(arm_UI), // F(628) + F(arm610),F(arm_UI),F(arm612),F(arm_UI),F(arm614),F(arm_UI),F(arm616),F(arm_UI), // F(630) + F(arm610),F(arm_UI),F(arm612),F(arm_UI),F(arm614),F(arm_UI),F(arm616),F(arm_UI), // F(638) + F(arm640),F(arm_UI),F(arm642),F(arm_UI),F(arm644),F(arm_UI),F(arm646),F(arm_UI), // F(640) + F(arm640),F(arm_UI),F(arm642),F(arm_UI),F(arm644),F(arm_UI),F(arm646),F(arm_UI), // F(648) + F(arm650),F(arm_UI),F(arm652),F(arm_UI),F(arm654),F(arm_UI),F(arm656),F(arm_UI), // F(650) + F(arm650),F(arm_UI),F(arm652),F(arm_UI),F(arm654),F(arm_UI),F(arm656),F(arm_UI), // F(658) + F(arm640),F(arm_UI),F(arm642),F(arm_UI),F(arm644),F(arm_UI),F(arm646),F(arm_UI), // F(660) + F(arm640),F(arm_UI),F(arm642),F(arm_UI),F(arm644),F(arm_UI),F(arm646),F(arm_UI), // F(668) + F(arm650),F(arm_UI),F(arm652),F(arm_UI),F(arm654),F(arm_UI),F(arm656),F(arm_UI), // F(670) + F(arm650),F(arm_UI),F(arm652),F(arm_UI),F(arm654),F(arm_UI),F(arm656),F(arm_UI), // F(678) + F(arm680),F(arm_UI),F(arm682),F(arm_UI),F(arm684),F(arm_UI),F(arm686),F(arm_UI), // F(680) + F(arm680),F(arm_UI),F(arm682),F(arm_UI),F(arm684),F(arm_UI),F(arm686),F(arm_UI), // F(688) + F(arm690),F(arm_UI),F(arm692),F(arm_UI),F(arm694),F(arm_UI),F(arm696),F(arm_UI), // F(690) + F(arm690),F(arm_UI),F(arm692),F(arm_UI),F(arm694),F(arm_UI),F(arm696),F(arm_UI), // F(698) + F(arm680),F(arm_UI),F(arm682),F(arm_UI),F(arm684),F(arm_UI),F(arm686),F(arm_UI), // F(6A0) + F(arm680),F(arm_UI),F(arm682),F(arm_UI),F(arm684),F(arm_UI),F(arm686),F(arm_UI), // F(6A8) + F(arm690),F(arm_UI),F(arm692),F(arm_UI),F(arm694),F(arm_UI),F(arm696),F(arm_UI), // F(6B0) + F(arm690),F(arm_UI),F(arm692),F(arm_UI),F(arm694),F(arm_UI),F(arm696),F(arm_UI), // F(6B8) + F(arm6C0),F(arm_UI),F(arm6C2),F(arm_UI),F(arm6C4),F(arm_UI),F(arm6C6),F(arm_UI), // F(6C0) + F(arm6C0),F(arm_UI),F(arm6C2),F(arm_UI),F(arm6C4),F(arm_UI),F(arm6C6),F(arm_UI), // F(6C8) + F(arm6D0),F(arm_UI),F(arm6D2),F(arm_UI),F(arm6D4),F(arm_UI),F(arm6D6),F(arm_UI), // F(6D0) + F(arm6D0),F(arm_UI),F(arm6D2),F(arm_UI),F(arm6D4),F(arm_UI),F(arm6D6),F(arm_UI), // F(6D8) + F(arm6C0),F(arm_UI),F(arm6C2),F(arm_UI),F(arm6C4),F(arm_UI),F(arm6C6),F(arm_UI), // F(6E0) + F(arm6C0),F(arm_UI),F(arm6C2),F(arm_UI),F(arm6C4),F(arm_UI),F(arm6C6),F(arm_UI), // F(6E8) + F(arm6D0),F(arm_UI),F(arm6D2),F(arm_UI),F(arm6D4),F(arm_UI),F(arm6D6),F(arm_UI), // F(6F0) + F(arm6D0),F(arm_UI),F(arm6D2),F(arm_UI),F(arm6D4),F(arm_UI),F(arm6D6),F(arm_UI), // F(6F8) + + F(arm700),F(arm_UI),F(arm702),F(arm_UI),F(arm704),F(arm_UI),F(arm706),F(arm_UI), // F(700) + F(arm700),F(arm_UI),F(arm702),F(arm_UI),F(arm704),F(arm_UI),F(arm706),F(arm_UI), // F(708) + F(arm710),F(arm_UI),F(arm712),F(arm_UI),F(arm714),F(arm_UI),F(arm716),F(arm_UI), // F(710) + F(arm710),F(arm_UI),F(arm712),F(arm_UI),F(arm714),F(arm_UI),F(arm716),F(arm_UI), // F(718) + F(arm720),F(arm_UI),F(arm722),F(arm_UI),F(arm724),F(arm_UI),F(arm726),F(arm_UI), // F(720) + F(arm720),F(arm_UI),F(arm722),F(arm_UI),F(arm724),F(arm_UI),F(arm726),F(arm_UI), // F(728) + F(arm730),F(arm_UI),F(arm732),F(arm_UI),F(arm734),F(arm_UI),F(arm736),F(arm_UI), // F(730) + F(arm730),F(arm_UI),F(arm732),F(arm_UI),F(arm734),F(arm_UI),F(arm736),F(arm_UI), // F(738) + F(arm740),F(arm_UI),F(arm742),F(arm_UI),F(arm744),F(arm_UI),F(arm746),F(arm_UI), // F(740) + F(arm740),F(arm_UI),F(arm742),F(arm_UI),F(arm744),F(arm_UI),F(arm746),F(arm_UI), // F(748) + F(arm750),F(arm_UI),F(arm752),F(arm_UI),F(arm754),F(arm_UI),F(arm756),F(arm_UI), // F(750) + F(arm750),F(arm_UI),F(arm752),F(arm_UI),F(arm754),F(arm_UI),F(arm756),F(arm_UI), // F(758) + F(arm760),F(arm_UI),F(arm762),F(arm_UI),F(arm764),F(arm_UI),F(arm766),F(arm_UI), // F(760) + F(arm760),F(arm_UI),F(arm762),F(arm_UI),F(arm764),F(arm_UI),F(arm766),F(arm_UI), // F(768) + F(arm770),F(arm_UI),F(arm772),F(arm_UI),F(arm774),F(arm_UI),F(arm776),F(arm_UI), // F(770) + F(arm770),F(arm_UI),F(arm772),F(arm_UI),F(arm774),F(arm_UI),F(arm776),F(arm_UI), // F(778) + F(arm780),F(arm_UI),F(arm782),F(arm_UI),F(arm784),F(arm_UI),F(arm786),F(arm_UI), // F(780) + F(arm780),F(arm_UI),F(arm782),F(arm_UI),F(arm784),F(arm_UI),F(arm786),F(arm_UI), // F(788) + F(arm790),F(arm_UI),F(arm792),F(arm_UI),F(arm794),F(arm_UI),F(arm796),F(arm_UI), // F(790) + F(arm790),F(arm_UI),F(arm792),F(arm_UI),F(arm794),F(arm_UI),F(arm796),F(arm_UI), // F(798) + F(arm7A0),F(arm_UI),F(arm7A2),F(arm_UI),F(arm7A4),F(arm_UI),F(arm7A6),F(arm_UI), // F(7A0) + F(arm7A0),F(arm_UI),F(arm7A2),F(arm_UI),F(arm7A4),F(arm_UI),F(arm7A6),F(arm_UI), // F(7A8) + F(arm7B0),F(arm_UI),F(arm7B2),F(arm_UI),F(arm7B4),F(arm_UI),F(arm7B6),F(arm_UI), // F(7B0) + F(arm7B0),F(arm_UI),F(arm7B2),F(arm_UI),F(arm7B4),F(arm_UI),F(arm7B6),F(arm_UI), // F(7B8) + F(arm7C0),F(arm_UI),F(arm7C2),F(arm_UI),F(arm7C4),F(arm_UI),F(arm7C6),F(arm_UI), // F(7C0) + F(arm7C0),F(arm_UI),F(arm7C2),F(arm_UI),F(arm7C4),F(arm_UI),F(arm7C6),F(arm_UI), // F(7C8) + F(arm7D0),F(arm_UI),F(arm7D2),F(arm_UI),F(arm7D4),F(arm_UI),F(arm7D6),F(arm_UI), // F(7D0) + F(arm7D0),F(arm_UI),F(arm7D2),F(arm_UI),F(arm7D4),F(arm_UI),F(arm7D6),F(arm_UI), // F(7D8) + F(arm7E0),F(arm_UI),F(arm7E2),F(arm_UI),F(arm7E4),F(arm_UI),F(arm7E6),F(arm_UI), // F(7E0) + F(arm7E0),F(arm_UI),F(arm7E2),F(arm_UI),F(arm7E4),F(arm_UI),F(arm7E6),F(arm_UI), // F(7E8) + F(arm7F0),F(arm_UI),F(arm7F2),F(arm_UI),F(arm7F4),F(arm_UI),F(arm7F6),F(arm_UI), // F(7F0) + F(arm7F0),F(arm_UI),F(arm7F2),F(arm_UI),F(arm7F4),F(arm_UI),F(arm7F6),F(arm_BP), // F(7F8) + + REP16(arm800),REP16(arm810),REP16(arm820),REP16(arm830), // 800 + REP16(arm840),REP16(arm850),REP16(arm860),REP16(arm870), // 840 + REP16(arm880),REP16(arm890),REP16(arm8A0),REP16(arm8B0), // 880 + REP16(arm8C0),REP16(arm8D0),REP16(arm8E0),REP16(arm8F0), // 8C0 + REP16(arm900),REP16(arm910),REP16(arm920),REP16(arm930), // 900 + REP16(arm940),REP16(arm950),REP16(arm960),REP16(arm970), // 940 + REP16(arm980),REP16(arm990),REP16(arm9A0),REP16(arm9B0), // 980 + REP16(arm9C0),REP16(arm9D0),REP16(arm9E0),REP16(arm9F0), // 9C0 + + REP256(armA00), // A00 + REP256(armB00), // B00 + REP256(arm_UI), // C00 + REP256(arm_UI), // D00 + + F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01), // F(E00) + F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01), // F(E08) + F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01), // F(E10) + F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01),F(arm_UI),F(armE01), // F(E18) + REP16(arm_UI), // E20 + REP16(arm_UI), // E30 + REP16(arm_UI),REP16(arm_UI),REP16(arm_UI),REP16(arm_UI), // E40 + REP16(arm_UI),REP16(arm_UI),REP16(arm_UI),REP16(arm_UI), // E80 + REP16(arm_UI),REP16(arm_UI),REP16(arm_UI),REP16(arm_UI), // EC0 + + REP256(armF00), // F00 +}; + +void (Gigazoid::*const Gigazoid::thumbInsnTable[1024])(u32 opcode) = { + F(thumb00_00),F(thumb00_01),F(thumb00_02),F(thumb00_03),F(thumb00_04),F(thumb00_05),F(thumb00_06),F(thumb00_07), // F(00) + F(thumb00_08),F(thumb00_09),F(thumb00_0A),F(thumb00_0B),F(thumb00_0C),F(thumb00_0D),F(thumb00_0E),F(thumb00_0F), + F(thumb00_10),F(thumb00_11),F(thumb00_12),F(thumb00_13),F(thumb00_14),F(thumb00_15),F(thumb00_16),F(thumb00_17), + F(thumb00_18),F(thumb00_19),F(thumb00_1A),F(thumb00_1B),F(thumb00_1C),F(thumb00_1D),F(thumb00_1E),F(thumb00_1F), + F(thumb08_00),F(thumb08_01),F(thumb08_02),F(thumb08_03),F(thumb08_04),F(thumb08_05),F(thumb08_06),F(thumb08_07), // F(08) + F(thumb08_08),F(thumb08_09),F(thumb08_0A),F(thumb08_0B),F(thumb08_0C),F(thumb08_0D),F(thumb08_0E),F(thumb08_0F), + F(thumb08_10),F(thumb08_11),F(thumb08_12),F(thumb08_13),F(thumb08_14),F(thumb08_15),F(thumb08_16),F(thumb08_17), + F(thumb08_18),F(thumb08_19),F(thumb08_1A),F(thumb08_1B),F(thumb08_1C),F(thumb08_1D),F(thumb08_1E),F(thumb08_1F), + F(thumb10_00),F(thumb10_01),F(thumb10_02),F(thumb10_03),F(thumb10_04),F(thumb10_05),F(thumb10_06),F(thumb10_07), // F(10) + F(thumb10_08),F(thumb10_09),F(thumb10_0A),F(thumb10_0B),F(thumb10_0C),F(thumb10_0D),F(thumb10_0E),F(thumb10_0F), + F(thumb10_10),F(thumb10_11),F(thumb10_12),F(thumb10_13),F(thumb10_14),F(thumb10_15),F(thumb10_16),F(thumb10_17), + F(thumb10_18),F(thumb10_19),F(thumb10_1A),F(thumb10_1B),F(thumb10_1C),F(thumb10_1D),F(thumb10_1E),F(thumb10_1F), + F(thumb18_0),F(thumb18_1),F(thumb18_2),F(thumb18_3),F(thumb18_4),F(thumb18_5),F(thumb18_6),F(thumb18_7), // F(18) + F(thumb1A_0),F(thumb1A_1),F(thumb1A_2),F(thumb1A_3),F(thumb1A_4),F(thumb1A_5),F(thumb1A_6),F(thumb1A_7), + F(thumb1C_0),F(thumb1C_1),F(thumb1C_2),F(thumb1C_3),F(thumb1C_4),F(thumb1C_5),F(thumb1C_6),F(thumb1C_7), + F(thumb1E_0),F(thumb1E_1),F(thumb1E_2),F(thumb1E_3),F(thumb1E_4),F(thumb1E_5),F(thumb1E_6),F(thumb1E_7), + F(thumb20),F(thumb20),F(thumb20),F(thumb20),F(thumb21),F(thumb21),F(thumb21),F(thumb21), // F(20) + F(thumb22),F(thumb22),F(thumb22),F(thumb22),F(thumb23),F(thumb23),F(thumb23),F(thumb23), + F(thumb24),F(thumb24),F(thumb24),F(thumb24),F(thumb25),F(thumb25),F(thumb25),F(thumb25), + F(thumb26),F(thumb26),F(thumb26),F(thumb26),F(thumb27),F(thumb27),F(thumb27),F(thumb27), + F(thumb28),F(thumb28),F(thumb28),F(thumb28),F(thumb29),F(thumb29),F(thumb29),F(thumb29), // F(28) + F(thumb2A),F(thumb2A),F(thumb2A),F(thumb2A),F(thumb2B),F(thumb2B),F(thumb2B),F(thumb2B), + F(thumb2C),F(thumb2C),F(thumb2C),F(thumb2C),F(thumb2D),F(thumb2D),F(thumb2D),F(thumb2D), + F(thumb2E),F(thumb2E),F(thumb2E),F(thumb2E),F(thumb2F),F(thumb2F),F(thumb2F),F(thumb2F), + F(thumb30),F(thumb30),F(thumb30),F(thumb30),F(thumb31),F(thumb31),F(thumb31),F(thumb31), // F(30) + F(thumb32),F(thumb32),F(thumb32),F(thumb32),F(thumb33),F(thumb33),F(thumb33),F(thumb33), + F(thumb34),F(thumb34),F(thumb34),F(thumb34),F(thumb35),F(thumb35),F(thumb35),F(thumb35), + F(thumb36),F(thumb36),F(thumb36),F(thumb36),F(thumb37),F(thumb37),F(thumb37),F(thumb37), + F(thumb38),F(thumb38),F(thumb38),F(thumb38),F(thumb39),F(thumb39),F(thumb39),F(thumb39), // F(38) + F(thumb3A),F(thumb3A),F(thumb3A),F(thumb3A),F(thumb3B),F(thumb3B),F(thumb3B),F(thumb3B), + F(thumb3C),F(thumb3C),F(thumb3C),F(thumb3C),F(thumb3D),F(thumb3D),F(thumb3D),F(thumb3D), + F(thumb3E),F(thumb3E),F(thumb3E),F(thumb3E),F(thumb3F),F(thumb3F),F(thumb3F),F(thumb3F), + F(thumb40_0),F(thumb40_1),F(thumb40_2),F(thumb40_3),F(thumb41_0),F(thumb41_1),F(thumb41_2),F(thumb41_3), // F(40) + F(thumb42_0),F(thumb42_1),F(thumb42_2),F(thumb42_3),F(thumb43_0),F(thumb43_1),F(thumb43_2),F(thumb43_3), + F(thumbUI),F(thumb44_1),F(thumb44_2),F(thumb44_3),F(thumbUI),F(thumb45_1),F(thumb45_2),F(thumb45_3), + F(thumbUI),F(thumb46_1),F(thumb46_2),F(thumb46_3),F(thumb47),F(thumb47),F(thumbUI),F(thumbUI), + F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48), // F(48) + F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48), + F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48), + F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48),F(thumb48), + F(thumb50),F(thumb50),F(thumb50),F(thumb50),F(thumb50),F(thumb50),F(thumb50),F(thumb50), // F(50) + F(thumb52),F(thumb52),F(thumb52),F(thumb52),F(thumb52),F(thumb52),F(thumb52),F(thumb52), + F(thumb54),F(thumb54),F(thumb54),F(thumb54),F(thumb54),F(thumb54),F(thumb54),F(thumb54), + F(thumb56),F(thumb56),F(thumb56),F(thumb56),F(thumb56),F(thumb56),F(thumb56),F(thumb56), + F(thumb58),F(thumb58),F(thumb58),F(thumb58),F(thumb58),F(thumb58),F(thumb58),F(thumb58), // F(58) + F(thumb5A),F(thumb5A),F(thumb5A),F(thumb5A),F(thumb5A),F(thumb5A),F(thumb5A),F(thumb5A), + F(thumb5C),F(thumb5C),F(thumb5C),F(thumb5C),F(thumb5C),F(thumb5C),F(thumb5C),F(thumb5C), + F(thumb5E),F(thumb5E),F(thumb5E),F(thumb5E),F(thumb5E),F(thumb5E),F(thumb5E),F(thumb5E), + F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60), // F(60) + F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60), + F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60), + F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60),F(thumb60), + F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68), // F(68) + F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68), + F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68), + F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68),F(thumb68), + F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70), // F(70) + F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70), + F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70), + F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70),F(thumb70), + F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78), // F(78) + F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78), + F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78), + F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78),F(thumb78), + F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80), // F(80) + F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80), + F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80), + F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80),F(thumb80), + F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88), // F(88) + F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88), + F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88), + F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88),F(thumb88), + F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90), // F(90) + F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90), + F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90), + F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90),F(thumb90), + F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98), // F(98) + F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98), + F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98), + F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98),F(thumb98), + F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0), // F(A0) + F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0), + F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0), + F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0),F(thumbA0), + F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8), // F(A8) + F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8), + F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8), + F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8),F(thumbA8), + F(thumbB0),F(thumbB0),F(thumbB0),F(thumbB0),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), // F(B0) + F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), + F(thumbB4),F(thumbB4),F(thumbB4),F(thumbB4),F(thumbB5),F(thumbB5),F(thumbB5),F(thumbB5), + F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), + F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), // F(B8) + F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), + F(thumbBC),F(thumbBC),F(thumbBC),F(thumbBC),F(thumbBD),F(thumbBD),F(thumbBD),F(thumbBD), + F(thumbBP),F(thumbBP),F(thumbBP),F(thumbBP),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), + F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0), // F(C0) + F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0), + F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0), + F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0),F(thumbC0), + F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8), // F(C8) + F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8), + F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8), + F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8),F(thumbC8), + F(thumbD0),F(thumbD0),F(thumbD0),F(thumbD0),F(thumbD1),F(thumbD1),F(thumbD1),F(thumbD1), // F(D0) + F(thumbD2),F(thumbD2),F(thumbD2),F(thumbD2),F(thumbD3),F(thumbD3),F(thumbD3),F(thumbD3), + F(thumbD4),F(thumbD4),F(thumbD4),F(thumbD4),F(thumbD5),F(thumbD5),F(thumbD5),F(thumbD5), + F(thumbD6),F(thumbD6),F(thumbD6),F(thumbD6),F(thumbD7),F(thumbD7),F(thumbD7),F(thumbD7), + F(thumbD8),F(thumbD8),F(thumbD8),F(thumbD8),F(thumbD9),F(thumbD9),F(thumbD9),F(thumbD9), // F(D8) + F(thumbDA),F(thumbDA),F(thumbDA),F(thumbDA),F(thumbDB),F(thumbDB),F(thumbDB),F(thumbDB), + F(thumbDC),F(thumbDC),F(thumbDC),F(thumbDC),F(thumbDD),F(thumbDD),F(thumbDD),F(thumbDD), + F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbDF),F(thumbDF),F(thumbDF),F(thumbDF), + F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0), // F(E0) + F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0), + F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0), + F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0),F(thumbE0), + F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), // F(E8) + F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), + F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), + F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI),F(thumbUI), + F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0), // F(F0) + F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0),F(thumbF0), + F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4), + F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4),F(thumbF4), + F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8), // F(F8) + F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8), + F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8), + F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8),F(thumbF8), +}; + +#undef F diff --git a/vbanext/port.h b/vbanext/port.h new file mode 100644 index 0000000000..3b2457042e --- /dev/null +++ b/vbanext/port.h @@ -0,0 +1,57 @@ +#ifndef PORT_H +#define PORT_H + +#include "types.h" + +/* if a >= 0 return x else y*/ +#define isel(a, x, y) ((x & (~(a >> 31))) + (y & (a >> 31))) + +#ifdef FRONTEND_SUPPORTS_RGB565 +/* 16bit color - RGB565 */ +#define RED_MASK 0xf800 +#define GREEN_MASK 0x7e0 +#define BLUE_MASK 0x1f +#define RED_EXPAND 3 +#define GREEN_EXPAND 2 +#define BLUE_EXPAND 3 +#define RED_SHIFT 11 +#define GREEN_SHIFT 5 +#define BLUE_SHIFT 0 +#define CONVERT_COLOR(color) (((color & 0x001f) << 11) | ((color & 0x03e0) << 1) | ((color & 0x0200) >> 4) | ((color & 0x7c00) >> 10)) +#else +/* 16bit color - RGB555 */ +#define RED_MASK 0x7c00 +#define GREEN_MASK 0x3e0 +#define BLUE_MASK 0x1f +#define RED_EXPAND 3 +#define GREEN_EXPAND 3 +#define BLUE_EXPAND 3 +#define RED_SHIFT 10 +#define GREEN_SHIFT 5 +#define BLUE_SHIFT 0 +#define CONVERT_COLOR(color) ((((color & 0x1f) << 10) | (((color & 0x3e0) >> 5) << 5) | (((color & 0x7c00) >> 10))) & 0x7fff) +#endif + +#ifdef _MSC_VER +#include +#define strcasecmp _stricmp +#endif + +#ifdef USE_CACHE_PREFETCH +#if defined(__ANDROID__) +#define CACHE_PREFETCH(prefetch) prefetch(&prefetch); +#elif defined(_XBOX) +#define CACHE_PREFETCH(prefetch) __dcbt(0, &prefetch); +#else +#define CACHE_PREFETCH(prefetch) __dcbt(&prefetch); +#endif +#else +#define CACHE_PREFETCH(prefetch) +#endif + +#define READ16LE(x) *((u16 *)x) +#define READ32LE(x) *((u32 *)x) +#define WRITE16LE(x,v) *((u16 *)x) = (v) +#define WRITE32LE(x,v) *((u32 *)x) = (v) + +#endif diff --git a/vbanext/sound_blargg.h b/vbanext/sound_blargg.h new file mode 100644 index 0000000000..c80d61e18e --- /dev/null +++ b/vbanext/sound_blargg.h @@ -0,0 +1,57 @@ +#ifndef SOUND_BLARGG_H +#define SOUND_BLARGG_H + +/* Uncomment to have Gb_Apu run at 4x normal clock rate (16777216 Hz), useful in +a Game Boy Advance emulator. */ +#define GB_APU_OVERCLOCK 4 + +#ifndef STATIC_CAST + #if __GNUC__ >= 4 + #define STATIC_CAST(T,expr) static_cast (expr) + #define CONST_CAST( T,expr) const_cast (expr) + #else + #define STATIC_CAST(T,expr) ((T) (expr)) + #define CONST_CAST( T,expr) ((T) (expr)) + #endif +#endif + +// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, +// compiler is assumed to support bool. If undefined, availability is determined. +#ifndef BLARGG_COMPILER_HAS_BOOL + #if defined (__MWERKS__) + #if !__option(bool) + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (_MSC_VER) + #if _MSC_VER < 1100 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (__GNUC__) + // supports bool + #elif __cplusplus < 199711 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif +#endif +#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL + typedef int bool; + const bool true = 1; + const bool false = 0; +#endif + +/* HAVE_STDINT_H: If defined, use for int8_t etc.*/ +#if defined (HAVE_STDINT_H) + #include +/* HAVE_INTTYPES_H: If defined, use for int8_t etc.*/ +#elif defined (HAVE_INTTYPES_H) + #include +#endif + +// If expr yields non-NULL error string, returns it from current function, +// otherwise continues normally. +#undef RETURN_ERR +#define RETURN_ERR( expr ) do { \ + const char * blargg_return_err_ = (expr); \ + if ( blargg_return_err_ ) return blargg_return_err_; \ + } while ( 0 ) + +#endif // #ifndef SOUND_BLARGG_H diff --git a/vbanext/types.h b/vbanext/types.h new file mode 100644 index 0000000000..8433576dcc --- /dev/null +++ b/vbanext/types.h @@ -0,0 +1,33 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef __VBA_TYPES_H__ +#define __VBA_TYPES_H__ + +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +#endif // __VBA_TYPES_H__