--- /dev/null
+#include "Kernel.h"
+
+#include <stdio.h>
+
+#include "Bootloader.h"
+
+//they are in mem in that order fs loader pm sm pix
+
+
+
+//tools
+static u32 Read32revers(uint8_t p[4])
+{
+ u32 temp = p[3] | p[2] << 8 | p[1] << 16 | p[0] << 24;
+ return temp;
+}
+
+static u32 Read32(uint8_t p[4])
+{
+ u32 temp = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+ return temp;
+}
+static u64 Read64(uint8_t p[8])
+{
+ u64 temp = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24 | (u64)(p[4]) << 32 | (u64)(p[5]) << 40 | (u64)(p[6]) << 48 | (u64)(p[7]) << 56;
+ return temp;
+}
+static u16 Read16(uint8_t p[2])
+{
+ u16 temp = p[0] | p[1] << 8;
+ return temp;
+}
+static u32 AlignPage(u32 in)
+{
+ return ((in + 0xFFF) / 0x1000) * 0x1000;
+}
+static u32 GetDecompressedSize(u8* compressed, u32 compressedsize)
+{
+ u8* footer = compressed + compressedsize - 8;
+
+ u32 originalbottom = Read32(footer + 4);
+ return originalbottom + compressedsize;
+}
+
+static int Decompress(u8* compressed, u32 compressedsize, u8* decompressed, u32 decompressedsize)
+{
+
+ u8* footer = compressed + compressedsize - 8;
+ u32 buffertopandbottom = Read32(footer + 0);
+ u32 i, j;
+ u32 out = decompressedsize;
+ u32 index = compressedsize - ((buffertopandbottom >> 24) & 0xFF);
+ u32 segmentoffset;
+ u32 segmentsize;
+ u8 control;
+ u32 stopindex = compressedsize - (buffertopandbottom & 0xFFFFFF);
+
+ memset(decompressed, 0, decompressedsize);
+ memcpy(decompressed, compressed, compressedsize);
+
+ while (index > stopindex) {
+ control = compressed[--index];
+
+ for (i = 0; i<8; i++) {
+ if (index <= stopindex)
+ break;
+ if (index <= 0)
+ break;
+ if (out <= 0)
+ break;
+
+ if (control & 0x80) {
+ if (index < 2) {
+ fprintf(stderr, "Error, compression out of bounds");
+ goto clean;
+ }
+
+ index -= 2;
+
+ segmentoffset = compressed[index] | (compressed[index + 1] << 8);
+ segmentsize = ((segmentoffset >> 12) & 15) + 3;
+ segmentoffset &= 0x0FFF;
+ segmentoffset += 2;
+
+ if (out < segmentsize) {
+ fprintf(stderr, "Error, compression out of bounds");
+ goto clean;
+ }
+
+ for (j = 0; j<segmentsize; j++) {
+ u8 data;
+
+ if (out + segmentoffset >= decompressedsize) {
+ fprintf(stderr, "Error, compression out of bounds");
+ goto clean;
+ }
+
+ data = decompressed[out + segmentoffset];
+ decompressed[--out] = data;
+ }
+ }
+ else {
+ if (out < 1) {
+ fprintf(stderr, "Error, compression out of bounds");
+ goto clean;
+ }
+ decompressed[--out] = compressed[--index];
+ }
+ control <<= 1;
+ }
+ }
+
+ return 1;
+clean:
+ return 0;
+}
+
+KProcess* Boot_LoadFileFast(FILE* fd, u32 offset, u32* out_offset, KKernel * Kernel)
+{
+
+ if (fseek(fd, offset, SEEK_SET) != 0)
+ {
+ XDSERROR("failed to seek.");
+ return NULL;
+ }
+
+ exheader_header ex;
+ ctr_ncchheader loader_h;
+ u32 ncch_off = 0;
+
+ // Read header.
+ if (fread(&loader_h, sizeof(loader_h), 1, fd) != 1) {
+ XDSERROR("failed to read header.");
+ return NULL;
+ }
+
+ // Load NCCH
+ if (memcmp(&loader_h.magic, "NCCH", 4) != 0) {
+ XDSERROR("invalid magic.. wrong file?");
+ return NULL;
+ }
+
+ // Read Exheader.
+ if (fread(&ex, sizeof(ex), 1, fd) != 1) {
+ XDSERROR("failed to read exheader.");
+ return NULL;
+ }
+
+ bool is_compressed = ex.codesetinfo.flags.flag & 1;
+ char namereal[9];
+ strncpy(namereal, (char*)ex.codesetinfo.name, 9);
+
+ // Read ExeFS.
+ u32 exefs_off = Read32(loader_h.exefsoffset) * 0x200;
+ u32 exefs_sz = Read32(loader_h.exefssize) * 0x200;
+
+ if (fseek(fd, exefs_off + ncch_off + offset, SEEK_SET) != 0)
+ {
+ XDSERROR("failed to seek.");
+ return NULL;
+ }
+
+ exefs_header eh;
+ if (fread(&eh, sizeof(eh), 1, fd) != 1) {
+ XDSERROR("failed to read ExeFS header.");
+ return NULL;
+ }
+
+ for (u32 i = 0; i < 8; i++) {
+ u32 sec_size = Read32(eh.section[i].size);
+ u32 sec_off = Read32(eh.section[i].offset);
+
+ if (sec_size == 0)
+ continue;
+
+ eh.section[i].name[7] = '\0';
+
+ if (strcmp((char*)eh.section[i].name, ".code") == 0) {
+ sec_off += exefs_off + sizeof(eh);
+ if (fseek(fd, sec_off + ncch_off + offset, SEEK_SET) != 0)
+ {
+ XDSERROR("failed to seek.");
+ return NULL;
+ }
+
+ u8* sec = (u8*)malloc(AlignPage(sec_size));
+ if (sec == NULL) {
+ XDSERROR("section malloc failed.");
+ return NULL;
+ }
+
+ if (fread(sec, sec_size, 1, fd) != 1) {
+ XDSERROR("section fread failed.");
+ free(sec);
+ return NULL;
+ }
+
+
+ // Decompress first section if flag set.
+ if (i == 0 && is_compressed) {
+ u32 dec_size = GetDecompressedSize(sec, sec_size);
+ u8* dec = (u8*)malloc(AlignPage(dec_size));
+
+ if (!dec) {
+ XDSERROR("decompressed data block allocation failed.");
+ free(sec);
+ return NULL;
+ }
+
+ u32 firmexpected = Read32(ex.codesetinfo.text.codesize) + Read32(ex.codesetinfo.ro.codesize) + Read32(ex.codesetinfo.data.codesize);
+
+ if (Decompress(sec, sec_size, dec, dec_size) == 0) {
+ XDSERROR("section decompression failed.");
+ free(sec);
+ free(dec);
+ return NULL;
+ }
+
+ /*FILE * pFile;
+ pFile = fopen("code.code", "wb");
+ if (pFile != NULL)
+ {
+ fwrite(dec, 1, dec_size, pFile);
+ fclose(pFile);
+ }*/
+
+ free(sec);
+ sec = dec;
+ sec_size = dec_size;
+ }
+
+ // Load .code section.
+ u32 realcodesize = Read32(ex.codesetinfo.text.codesize);
+ u32 codesize = AlignPage(realcodesize);
+ u8* code = (u8*)malloc(codesize);
+ if (!code) {
+ XDSERROR("text data block allocation failed.");
+ free(sec);
+ return NULL;
+ }
+ memset(code,0, codesize);
+ memcpy(code, sec, realcodesize);
+
+ u32 realrodatasize = Read32(ex.codesetinfo.ro.codesize);
+ u32 rodatasize = AlignPage(realrodatasize);
+ u8* rodata = (u8*)malloc(rodatasize);
+ if (!rodata) {
+ XDSERROR("rodata data block allocation failed.");
+ free(code);
+ free(sec);
+ return NULL;
+ }
+ memset(rodata, 0, rodatasize);
+ memcpy(rodata, sec + realcodesize, realrodatasize);
+
+ u32 realdatasize = Read32(ex.codesetinfo.data.codesize);
+ u32 datasize = AlignPage(realdatasize);
+ u8* data = (u8*)malloc(datasize);
+ if (!data) {
+ XDSERROR("data data block allocation failed.");
+ free(code);
+ free(sec);
+ return NULL;
+ }
+ memset(data, 0, datasize);
+ memcpy(data, sec + realcodesize + realrodatasize, realdatasize);
+
+ KCodeSet* Codeset = new KCodeSet(
+ code, codesize / 0x1000,
+ rodata, rodatasize/0x1000,
+ data, datasize / 0x1000,
+ AlignPage(Read32(ex.codesetinfo.bsssize)) / 0x1000,
+ Read64(loader_h.programid),
+ namereal
+ );
+
+ KProcess* process = new KProcess(Codeset, 28, (u32*)ex.arm11kernelcaps.descriptors, Kernel,true);
+
+ //Start the process
+
+ //map stack
+ u32 unused;
+ u32 stacksize = Read32(ex.codesetinfo.stacksize);
+ process->getMemoryMap()->ControlMemory(&unused, 0x10000000 - stacksize, 0, stacksize, OPERATION_COMMIT, PERMISSION_RW);
+
+ KThread * thread = new KThread(SERVICECORE,process);
+ u32 startaddr = (process->m_exheader_flags & (1 << 12)) ? 0x14000000 : 0x00100000;
+ thread->m_context.reg_15 = startaddr;
+ thread->m_context.pc = startaddr;
+ thread->m_context.sp = 0x10000000;
+ thread->m_context.cpsr = 0x1F;
+ process->AddThread(thread);
+
+ free(rodata);
+ free(data);
+ free(code);
+ free(sec);
+
+ *out_offset = ftell(fd);
+
+ //no need to register them with fs and sm the pm dose that on its own core stuff always dose that correctly
+ return process;
+ }
+ }
+ return NULL;
+}
+FILE* openapp(u32 titlehigh, u32 titlelow) //used by pm
+{
+ for (u32 i = 0; i < 0x1000; i++) //TODO search for the correct tmd that needs to be impr that is also only a hack normaly the data is loaded from .firm not from the FS
+ {
+ char string[0x100];
+#if EMU_PLATFORM == PLATFORM_WINDOWS
+ sprintf_s(string, 0x100, "./NAND/title/%08X/%08X/content/%08x.tmd", titlehigh, titlelow, i);
+#else
+ snprintf(string, 0x100, "./NAND/title/%08X/%08X/content/%08x.tmd", titlehigh, titlelow, i);
+#endif
+ FILE* fd = fopen(string, "rb");
+ if (fd != NULL)
+ {
+ u8 temp[4];
+ if (fread(temp, 4, 1, fd) != 1)
+ {
+ XDSERROR("reading tmd Signature Type");
+ return NULL;
+ }
+ u32 Signature_Type = Read32revers(temp);
+ u32 y;
+ switch (Signature_Type)
+ {
+ case 0x010000:
+ y = 0x240;
+ break;
+ case 0x010001:
+ y = 0x140;
+ break;
+ case 0x010002:
+ y = 0x80;
+ break;
+ case 0x010003:
+ y = 0x240;
+ break;
+ case 0x010004:
+ y = 0x140;
+ break;
+ case 0x010005:
+ y = 0x80;
+ break;
+ default:
+ LOG("unknown Signature Type fallback");
+ y = 0x140;
+ break;
+ }
+ if (fseek(fd, y + 0x9C4, SEEK_SET) != 0)
+ {
+ XDSERROR("reading tmd Signature Type");
+ return NULL;
+ }
+ if (fread(temp, 4, 1, fd) != 1)
+ {
+ XDSERROR("reading tmd Signature Type");
+ return NULL;
+ }
+ u32 index = Read32revers(temp);
+ fclose(fd);
+
+#if EMU_PLATFORM == PLATFORM_WINDOWS
+ sprintf_s(string, 0x100, "./NAND/title/%08x/%08x/content/%08x.app", titlehigh, titlelow, index);
+#else
+ snprintf(string, 0x100, "./NAND/title/%08x/%08x/content/%08x.app", titlehigh, titlelow, index);
+#endif
+ fd = fopen(string, "rb");
+ if (fd == NULL)
+ {
+ XDSERROR("opening the container %s", string);
+ return NULL;
+ }
+ return fd;
+ }
+ }
+ return NULL;
+}
+
+s64 FindRomFSOffset(FILE* fd, char* name, u64 &out_size, u8* hash_out)
+{
+ if (fd == NULL)
+ {
+ return -1;
+ }
+ fseek(fd, 0, SEEK_SET);
+
+
+ //open the container
+ exheader_header ex;
+ ctr_ncchheader loader_h;
+ u32 ncch_off = 0;
+
+ // Read header.
+ if (fread(&loader_h, sizeof(loader_h), 1, fd) != 1) {
+ XDSERROR("failed to read header.");
+ return -1;
+ }
+ // Load NCCH
+ if (memcmp(&loader_h.magic, "NCCH", 4) != 0) {
+ XDSERROR("invalid magic.. wrong file?");
+ return -1;
+ }
+
+ // Read Exheader.
+ if (fread(&ex, sizeof(ex), 1, fd) != 1) {
+ XDSERROR("failed to read exheader.");
+ return -1;
+ }
+
+ // Read ExeFS.
+ u32 exefs_off = Read32(loader_h.romfsoffset) * 0x200;
+ out_size = Read32(loader_h.romfssize) * 0x200;
+
+ return exefs_off;
+}
+
+
+s64 FindTableOffset(FILE* fd,char* name,u64 &out_size, u8* hash_out)
+{
+ if (fd == NULL)
+ {
+ return -1;
+ }
+ fseek(fd, 0, SEEK_SET);
+
+
+ //open the container
+ exheader_header ex;
+ ctr_ncchheader loader_h;
+ u32 ncch_off = 0;
+
+ // Read header.
+ if (fread(&loader_h, sizeof(loader_h), 1, fd) != 1) {
+ XDSERROR("failed to read header.");
+ return -1;
+ }
+ // Load NCCH
+ if (memcmp(&loader_h.magic, "NCCH", 4) != 0) {
+ XDSERROR("invalid magic.. wrong file?");
+ return -1;
+ }
+
+ // Read Exheader.
+ if (fread(&ex, sizeof(ex), 1, fd) != 1) {
+ XDSERROR("failed to read exheader.");
+ return -1;
+ }
+
+ // Read ExeFS.
+ u32 exefs_off = Read32(loader_h.exefsoffset) * 0x200;
+ u32 exefs_sz = Read32(loader_h.exefssize) * 0x200;
+
+ if (fseek(fd, exefs_off + ncch_off, SEEK_SET) != 0)
+ {
+ XDSERROR("failed to seek.");
+ return -2;
+ }
+
+ exefs_header eh;
+ if (fread(&eh, sizeof(eh), 1, fd) != 1) {
+ XDSERROR("failed to read ExeFS header.");
+ return -1;
+ }
+
+ for (u32 i = 0; i < 8; i++) {
+ u32 sec_size = Read32(eh.section[i].size);
+ u32 sec_off = Read32(eh.section[i].offset);
+ out_size = sec_size;
+ if (sec_size == 0)
+ continue;
+
+ eh.section[i].name[7] = '\0';
+
+ if (hash_out != NULL)
+ {
+ memcpy(hash_out, eh.hashes[7-i], 0x20);
+ }
+
+ //sec_off = 0;
+
+ if (strcmp((char*)eh.section[i].name, name) == 0) {
+ return exefs_off + sizeof(eh) + sec_off;
+ }
+ }
+ XDSERROR("finding section");
+ return -1;
+}
+
+int Boot(KKernel* kernel)
+{
+ FILE* fd = openapp(0x00040138, 0x00000002);//this is firm
+ //open the firm to extrect the core modules
+ u64 temp;
+ s64 sec_off = FindTableOffset(fd, ".firm", temp, NULL);
+ u32 out_offset;
+ if (sec_off > 0)
+ {
+ KProcess* process = Boot_LoadFileFast(fd, sec_off + 0x200, &out_offset, kernel);//The first is most likely the NCCH container but that may change I don't know how to detect the correct container so I just do it by a static offset TODO
+ for (int j = 0; j < 4; j++) //boot all 5
+ {
+ process = Boot_LoadFileFast(fd, (out_offset + 0x1FF)&~0x1FF, &out_offset, kernel);
+ }
+ fclose(fd);
+ return 0; //it worked
+ }
+ fclose(fd);
+ XDSERROR("finding .firm section");
+ return -1;
+ XDSERROR("finding firm");
+ return -1;
+
+}
--- /dev/null
+#define NOMINMAX
+
+#include <stdio.h>
+#include "Kernel.h"
+#include "Gui.h"
+#include "Bootloader.h"
+
+#include "citraimport\GPU\window\emu_window_glfw.h"
+
+namespace VideoCore {
+ void Init(EmuWindow* emu_window);
+} // namespace
+
+
+static u8 code[0x1000000];
+
+KKernel* mykernel; //this is a citra hack
+
+int main(int argc, char* argv[]) {
+ /*FILE* fd = fopen("code.bin", "rb");
+ size_t size = fread(code, 1, sizeof(code), fd);
+ fclose(fd);*/
+
+ //citra hacks
+
+ EmuWindow_GLFW window;
+
+ VideoCore::Init(&window);
+
+ //citra hacks end
+
+ //MainWindow* wndMain = new MainWindow();
+ mykernel = new KKernel();
+
+ Mem_Init(false);
+ Mem_SharedMemInit();
+
+ Boot(mykernel);
+
+ //kernel->AddQuickCodeProcess(&code[0], size);
+ mykernel->ThreadsRunTemp();
+ return 0;
+}
+extern "C" void citraFireInterrupt(int id)
+{
+ LOG("cirta fire %02x",id);
+ mykernel->FireInterrupt(id);
+}
+bool novideo = true;
+extern "C" int citraPressedkey = 0;
+extern "C" bool citraSettingSkipGSP = false;
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+
+KArmCore::KArmCore(KKernel* kernel) : m_kernel(kernel), m_cpu()
+{
+ m_thread = NULL;
+}
+void KArmCore::SetThread(KThread* thread)
+{
+ if (m_thread)
+ {
+ m_cpu.SaveContext(m_thread->m_context);
+ }
+
+ m_thread = thread;
+
+ m_cpu.state->m_currentThread = m_thread;
+ m_cpu.LoadContext(m_thread->m_context);
+ m_cpu.state->m_MemoryMap = &m_thread->m_owner->m_memory;
+}
+u64 KArmCore::RunCycles(uint cycles)
+{
+ return m_cpu.Run(cycles);
+}
+void KArmCore::ReSchedule()
+{
+ m_cpu.PrepareReschedule();
+}
+void KArmCore::SetRegister(uint id, uint data)
+{
+ m_cpu.SetReg(id, data);
+}
+void KArmCore::Addticks(int ticks)
+{
+ m_cpu.AddTicks(ticks);
+}
+s64 KArmCore::Getticks()
+{
+ return m_cpu.GetTicks();
+}
\ No newline at end of file
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "Common.h"
+
+/// Generic ARM11 CPU interface
+class ARM_Interface {
+public:
+ ARM_Interface() {
+ num_instructions = 0;
+ }
+
+ virtual ~ARM_Interface() {
+ }
+
+ /**
+ * Runs the CPU for the given number of instructions
+ * @param num_instructions Number of instructions to run
+ */
+ u64 Run(int num_instructions) {
+ num_instructions = (int)ExecuteInstructions(num_instructions);
+ this->num_instructions += num_instructions;
+ return num_instructions;
+ }
+
+ /// Step CPU by one instruction
+ void Step() {
+ Run(1);
+ }
+
+ /**
+ * Set the Program Counter to an address
+ * @param addr Address to set PC to
+ */
+ virtual void SetPC(u32 addr) = 0;
+
+ /*
+ * Get the current Program Counter
+ * @return Returns current PC
+ */
+ virtual u32 GetPC() const = 0;
+
+ /**
+ * Get an ARM register
+ * @param index Register index (0-15)
+ * @return Returns the value in the register
+ */
+ virtual u32 GetReg(int index) const = 0;
+
+ /**
+ * Set an ARM register
+ * @param index Register index (0-15)
+ * @param value Value to set register to
+ */
+ virtual void SetReg(int index, u32 value) = 0;
+
+ /**
+ * Get the current CPSR register
+ * @return Returns the value of the CPSR register
+ */
+ virtual u32 GetCPSR() const = 0;
+
+ /**
+ * Set the current CPSR register
+ * @param cpsr Value to set CPSR to
+ */
+ virtual void SetCPSR(u32 cpsr) = 0;
+
+ /**
+ * Returns the number of clock ticks since the last rese
+ * @return Returns number of clock ticks
+ */
+ virtual u64 GetTicks() const = 0;
+
+ /**
+ * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
+ * @param ticks Number of ticks to advance the CPU core
+ */
+ virtual void AddTicks(u64 ticks) = 0;
+
+ /**
+ * Saves the current CPU context
+ * @param ctx Thread context to save
+ */
+ virtual void SaveContext(ThreadContext& ctx) = 0;
+
+ /**
+ * Loads a CPU context
+ * @param ctx Thread context to load
+ */
+ virtual void LoadContext(const ThreadContext& ctx) = 0;
+
+ /// Prepare core for thread reschedule (if needed to correctly handle state)
+ virtual void PrepareReschedule() = 0;
+
+ /// Getter for num_instructions
+ u64 GetNumInstructions() {
+ return num_instructions;
+ }
+
+ s64 down_count; ///< A decreasing counter of remaining cycles before the next event, decreased by the cpu run loop
+
+protected:
+
+ /**
+ * Executes the given number of instructions
+ * @param num_instructions Number of instructions to executes
+ */
+ virtual u64 ExecuteInstructions(int num_instructions) = 0;
+
+private:
+
+ u64 num_instructions; ///< Number of instructions executed
+
+};
--- /dev/null
+// Copyright 2006 The Android Open Source Project
+
+#include "Common.h"
+#include "util/Common.h"
+
+#include "arm/disassembler/arm_disasm.h"
+
+static const char *cond_names[] = {
+ "eq",
+ "ne",
+ "cs",
+ "cc",
+ "mi",
+ "pl",
+ "vs",
+ "vc",
+ "hi",
+ "ls",
+ "ge",
+ "lt",
+ "gt",
+ "le",
+ "",
+ "RESERVED"
+};
+
+const char *opcode_names[] = {
+ "invalid",
+ "undefined",
+ "adc",
+ "add",
+ "and",
+ "b",
+ "bl",
+ "bic",
+ "bkpt",
+ "blx",
+ "bx",
+ "cdp",
+ "clz",
+ "cmn",
+ "cmp",
+ "eor",
+ "ldc",
+ "ldm",
+ "ldr",
+ "ldrb",
+ "ldrbt",
+ "ldrh",
+ "ldrsb",
+ "ldrsh",
+ "ldrt",
+ "mcr",
+ "mla",
+ "mov",
+ "mrc",
+ "mrs",
+ "msr",
+ "mul",
+ "mvn",
+ "orr",
+ "pld",
+ "rsb",
+ "rsc",
+ "sbc",
+ "smlal",
+ "smull",
+ "stc",
+ "stm",
+ "str",
+ "strb",
+ "strbt",
+ "strh",
+ "strt",
+ "sub",
+ "swi",
+ "swp",
+ "swpb",
+ "teq",
+ "tst",
+ "umlal",
+ "umull",
+
+ "undefined",
+ "adc",
+ "add",
+ "and",
+ "asr",
+ "b",
+ "bic",
+ "bkpt",
+ "bl",
+ "blx",
+ "bx",
+ "cmn",
+ "cmp",
+ "eor",
+ "ldmia",
+ "ldr",
+ "ldrb",
+ "ldrh",
+ "ldrsb",
+ "ldrsh",
+ "lsl",
+ "lsr",
+ "mov",
+ "mul",
+ "mvn",
+ "neg",
+ "orr",
+ "pop",
+ "push",
+ "ror",
+ "sbc",
+ "stmia",
+ "str",
+ "strb",
+ "strh",
+ "sub",
+ "swi",
+ "tst",
+
+ NULL
+};
+
+// Indexed by the shift type (bits 6-5)
+static const char *shift_names[] = {
+ "LSL",
+ "LSR",
+ "ASR",
+ "ROR"
+};
+
+static const char* cond_to_str(int cond) {
+ return cond_names[cond];
+}
+
+std::string ARM_Disasm::Disassemble(uint32_t addr, uint32_t insn)
+{
+ Opcode opcode = Decode(insn);
+ switch (opcode) {
+ case OP_INVALID:
+ return "Invalid";
+ case OP_UNDEFINED:
+ return "Undefined";
+ case OP_ADC:
+ case OP_ADD:
+ case OP_AND:
+ case OP_BIC:
+ case OP_CMN:
+ case OP_CMP:
+ case OP_EOR:
+ case OP_MOV:
+ case OP_MVN:
+ case OP_ORR:
+ case OP_RSB:
+ case OP_RSC:
+ case OP_SBC:
+ case OP_SUB:
+ case OP_TEQ:
+ case OP_TST:
+ return DisassembleALU(opcode, insn);
+ case OP_B:
+ case OP_BL:
+ return DisassembleBranch(addr, opcode, insn);
+ case OP_BKPT:
+ return DisassembleBKPT(insn);
+ case OP_BLX:
+ // not supported yet
+ break;
+ case OP_BX:
+ return DisassembleBX(insn);
+ case OP_CDP:
+ return "cdp";
+ case OP_CLZ:
+ return DisassembleCLZ(insn);
+ case OP_LDC:
+ return "ldc";
+ case OP_LDM:
+ case OP_STM:
+ return DisassembleMemblock(opcode, insn);
+ case OP_LDR:
+ case OP_LDRB:
+ case OP_LDRBT:
+ case OP_LDRT:
+ case OP_STR:
+ case OP_STRB:
+ case OP_STRBT:
+ case OP_STRT:
+ return DisassembleMem(insn);
+ case OP_LDRH:
+ case OP_LDRSB:
+ case OP_LDRSH:
+ case OP_STRH:
+ return DisassembleMemHalf(insn);
+ case OP_MCR:
+ case OP_MRC:
+ return DisassembleMCR(opcode, insn);
+ case OP_MLA:
+ return DisassembleMLA(opcode, insn);
+ case OP_MRS:
+ return DisassembleMRS(insn);
+ case OP_MSR:
+ return DisassembleMSR(insn);
+ case OP_MUL:
+ return DisassembleMUL(opcode, insn);
+ case OP_PLD:
+ return DisassemblePLD(insn);
+ case OP_STC:
+ return "stc";
+ case OP_SWI:
+ return DisassembleSWI(insn);
+ case OP_SWP:
+ case OP_SWPB:
+ return DisassembleSWP(opcode, insn);
+ case OP_UMLAL:
+ case OP_UMULL:
+ case OP_SMLAL:
+ case OP_SMULL:
+ return DisassembleUMLAL(opcode, insn);
+ default:
+ return "Error";
+ }
+ return NULL;
+}
+
+std::string ARM_Disasm::DisassembleALU(Opcode opcode, uint32_t insn)
+{
+ static const uint8_t kNoOperand1 = 1;
+ static const uint8_t kNoDest = 2;
+ static const uint8_t kNoSbit = 4;
+
+ std::string rn_str;
+ std::string rd_str;
+
+ uint8_t flags = 0;
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t is_immed = (insn >> 25) & 0x1;
+ uint8_t bit_s = (insn >> 20) & 1;
+ uint8_t rn = (insn >> 16) & 0xf;
+ uint8_t rd = (insn >> 12) & 0xf;
+ uint8_t immed = insn & 0xff;
+
+ const char* opname = opcode_names[opcode];
+ switch (opcode) {
+ case OP_CMN:
+ case OP_CMP:
+ case OP_TEQ:
+ case OP_TST:
+ flags = kNoDest | kNoSbit;
+ break;
+ case OP_MOV:
+ case OP_MVN:
+ flags = kNoOperand1;
+ break;
+ default:
+ break;
+ }
+
+ // The "mov" instruction ignores the first operand (rn).
+ rn_str[0] = 0;
+ if ((flags & kNoOperand1) == 0) {
+ rn_str = Common::StringFromFormat("r%d, ", rn);
+ }
+
+ // The following instructions do not write the result register (rd):
+ // tst, teq, cmp, cmn.
+ rd_str[0] = 0;
+ if ((flags & kNoDest) == 0) {
+ rd_str = Common::StringFromFormat("r%d, ", rd);
+ }
+
+ const char *sbit_str = "";
+ if (bit_s && !(flags & kNoSbit))
+ sbit_str = "s";
+
+ if (is_immed) {
+ return Common::StringFromFormat("%s%s%s\t%s%s#%u ; 0x%x",
+ opname, cond_to_str(cond), sbit_str, rd_str.c_str(), rn_str.c_str(), immed, immed);
+ }
+
+ uint8_t shift_is_reg = (insn >> 4) & 1;
+ uint8_t rotate = (insn >> 8) & 0xf;
+ uint8_t rm = insn & 0xf;
+ uint8_t shift_type = (insn >> 5) & 0x3;
+ uint8_t rs = (insn >> 8) & 0xf;
+ uint8_t shift_amount = (insn >> 7) & 0x1f;
+ uint32_t rotated_val = immed;
+ uint8_t rotate2 = rotate << 1;
+ rotated_val = (rotated_val >> rotate2) | (rotated_val << (32 - rotate2));
+
+ if (!shift_is_reg && shift_type == 0 && shift_amount == 0) {
+ return Common::StringFromFormat("%s%s%s\t%s%sr%d",
+ opname, cond_to_str(cond), sbit_str, rd_str.c_str(), rn_str.c_str(), rm);
+ }
+
+ const char *shift_name = shift_names[shift_type];
+ if (shift_is_reg) {
+ return Common::StringFromFormat("%s%s%s\t%s%sr%d, %s r%d",
+ opname, cond_to_str(cond), sbit_str, rd_str.c_str(), rn_str.c_str(), rm,
+ shift_name, rs);
+ }
+ if (shift_amount == 0) {
+ if (shift_type == 3) {
+ return Common::StringFromFormat("%s%s%s\t%s%sr%d, RRX",
+ opname, cond_to_str(cond), sbit_str, rd_str.c_str(), rn_str.c_str(), rm);
+ }
+ shift_amount = 32;
+ }
+ return Common::StringFromFormat("%s%s%s\t%s%sr%d, %s #%u",
+ opname, cond_to_str(cond), sbit_str, rd_str.c_str(), rn_str.c_str(), rm,
+ shift_name, shift_amount);
+}
+
+std::string ARM_Disasm::DisassembleBranch(uint32_t addr, Opcode opcode, uint32_t insn)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint32_t offset = insn & 0xffffff;
+ // Sign-extend the 24-bit offset
+ if ((offset >> 23) & 1)
+ offset |= 0xff000000;
+
+ // Pre-compute the left-shift and the prefetch offset
+ offset <<= 2;
+ offset += 8;
+ addr += offset;
+ const char *opname = opcode_names[opcode];
+ return Common::StringFromFormat("%s%s\t0x%x", opname, cond_to_str(cond), addr);
+}
+
+std::string ARM_Disasm::DisassembleBX(uint32_t insn)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rn = insn & 0xf;
+ return Common::StringFromFormat("bx%s\tr%d", cond_to_str(cond), rn);
+}
+
+std::string ARM_Disasm::DisassembleBKPT(uint32_t insn)
+{
+ uint32_t immed = (((insn >> 8) & 0xfff) << 4) | (insn & 0xf);
+ return Common::StringFromFormat("bkpt\t#%d", immed);
+}
+
+std::string ARM_Disasm::DisassembleCLZ(uint32_t insn)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rd = (insn >> 12) & 0xf;
+ uint8_t rm = insn & 0xf;
+ return Common::StringFromFormat("clz%s\tr%d, r%d", cond_to_str(cond), rd, rm);
+}
+
+std::string ARM_Disasm::DisassembleMemblock(Opcode opcode, uint32_t insn)
+{
+ std::string tmp_reg;
+ std::string tmp_list;
+
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t write_back = (insn >> 21) & 0x1;
+ uint8_t bit_s = (insn >> 22) & 0x1;
+ uint8_t is_up = (insn >> 23) & 0x1;
+ uint8_t is_pre = (insn >> 24) & 0x1;
+ uint8_t rn = (insn >> 16) & 0xf;
+ uint16_t reg_list = insn & 0xffff;
+
+ const char *opname = opcode_names[opcode];
+
+ const char *bang = "";
+ if (write_back)
+ bang = "!";
+
+ const char *carret = "";
+ if (bit_s)
+ carret = "^";
+
+ const char *comma = "";
+ tmp_list[0] = 0;
+ for (int ii = 0; ii < 16; ++ii) {
+ if (reg_list & (1 << ii)) {
+ tmp_list += Common::StringFromFormat("%sr%d", comma, ii);
+ comma = ",";
+ }
+ }
+
+ const char *addr_mode = "";
+ if (is_pre) {
+ if (is_up) {
+ addr_mode = "ib";
+ } else {
+ addr_mode = "db";
+ }
+ } else {
+ if (is_up) {
+ addr_mode = "ia";
+ } else {
+ addr_mode = "da";
+ }
+ }
+
+ return Common::StringFromFormat("%s%s%s\tr%d%s, {%s}%s",
+ opname, cond_to_str(cond), addr_mode, rn, bang, tmp_list.c_str(), carret);
+}
+
+std::string ARM_Disasm::DisassembleMem(uint32_t insn)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t is_reg = (insn >> 25) & 0x1;
+ uint8_t is_load = (insn >> 20) & 0x1;
+ uint8_t write_back = (insn >> 21) & 0x1;
+ uint8_t is_byte = (insn >> 22) & 0x1;
+ uint8_t is_up = (insn >> 23) & 0x1;
+ uint8_t is_pre = (insn >> 24) & 0x1;
+ uint8_t rn = (insn >> 16) & 0xf;
+ uint8_t rd = (insn >> 12) & 0xf;
+ uint16_t offset = insn & 0xfff;
+
+ const char *opname = "ldr";
+ if (!is_load)
+ opname = "str";
+
+ const char *bang = "";
+ if (write_back)
+ bang = "!";
+
+ const char *minus = "";
+ if (is_up == 0)
+ minus = "-";
+
+ const char *byte = "";
+ if (is_byte)
+ byte = "b";
+
+ if (is_reg == 0) {
+ if (is_pre) {
+ if (offset == 0) {
+ return Common::StringFromFormat("%s%s%s\tr%d, [r%d]",
+ opname, cond_to_str(cond), byte, rd, rn);
+ } else {
+ return Common::StringFromFormat("%s%s%s\tr%d, [r%d, #%s%u]%s",
+ opname, cond_to_str(cond), byte, rd, rn, minus, offset, bang);
+ }
+ } else {
+ const char *transfer = "";
+ if (write_back)
+ transfer = "t";
+
+ return Common::StringFromFormat("%s%s%s%s\tr%d, [r%d], #%s%u",
+ opname, cond_to_str(cond), byte, transfer, rd, rn, minus, offset);
+ }
+ }
+
+ uint8_t rm = insn & 0xf;
+ uint8_t shift_type = (insn >> 5) & 0x3;
+ uint8_t shift_amount = (insn >> 7) & 0x1f;
+
+ const char *shift_name = shift_names[shift_type];
+
+ if (is_pre) {
+ if (shift_amount == 0) {
+ if (shift_type == 0) {
+ return Common::StringFromFormat("%s%s%s\tr%d, [r%d, %sr%d]%s",
+ opname, cond_to_str(cond), byte, rd, rn, minus, rm, bang);
+ }
+ if (shift_type == 3) {
+ return Common::StringFromFormat("%s%s%s\tr%d, [r%d, %sr%d, RRX]%s",
+ opname, cond_to_str(cond), byte, rd, rn, minus, rm, bang);
+ }
+ shift_amount = 32;
+ }
+ return Common::StringFromFormat("%s%s%s\tr%d, [r%d, %sr%d, %s #%u]%s",
+ opname, cond_to_str(cond), byte, rd, rn, minus, rm,
+ shift_name, shift_amount, bang);
+ }
+
+ const char *transfer = "";
+ if (write_back)
+ transfer = "t";
+
+ if (shift_amount == 0) {
+ if (shift_type == 0) {
+ return Common::StringFromFormat("%s%s%s%s\tr%d, [r%d], %sr%d",
+ opname, cond_to_str(cond), byte, transfer, rd, rn, minus, rm);
+ }
+ if (shift_type == 3) {
+ return Common::StringFromFormat("%s%s%s%s\tr%d, [r%d], %sr%d, RRX",
+ opname, cond_to_str(cond), byte, transfer, rd, rn, minus, rm);
+ }
+ shift_amount = 32;
+ }
+
+ return Common::StringFromFormat("%s%s%s%s\tr%d, [r%d], %sr%d, %s #%u",
+ opname, cond_to_str(cond), byte, transfer, rd, rn, minus, rm,
+ shift_name, shift_amount);
+}
+
+std::string ARM_Disasm::DisassembleMemHalf(uint32_t insn)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t is_load = (insn >> 20) & 0x1;
+ uint8_t write_back = (insn >> 21) & 0x1;
+ uint8_t is_immed = (insn >> 22) & 0x1;
+ uint8_t is_up = (insn >> 23) & 0x1;
+ uint8_t is_pre = (insn >> 24) & 0x1;
+ uint8_t rn = (insn >> 16) & 0xf;
+ uint8_t rd = (insn >> 12) & 0xf;
+ uint8_t bits_65 = (insn >> 5) & 0x3;
+ uint8_t rm = insn & 0xf;
+ uint8_t offset = (((insn >> 8) & 0xf) << 4) | (insn & 0xf);
+
+ const char *opname = "ldr";
+ if (is_load == 0)
+ opname = "str";
+
+ const char *width = "";
+ if (bits_65 == 1)
+ width = "h";
+ else if (bits_65 == 2)
+ width = "sb";
+ else
+ width = "sh";
+
+ const char *bang = "";
+ if (write_back)
+ bang = "!";
+ const char *minus = "";
+ if (is_up == 0)
+ minus = "-";
+
+ if (is_immed) {
+ if (is_pre) {
+ if (offset == 0) {
+ return Common::StringFromFormat("%s%sh\tr%d, [r%d]", opname, cond_to_str(cond), rd, rn);
+ } else {
+ return Common::StringFromFormat("%s%sh\tr%d, [r%d, #%s%u]%s",
+ opname, cond_to_str(cond), rd, rn, minus, offset, bang);
+ }
+ } else {
+ return Common::StringFromFormat("%s%sh\tr%d, [r%d], #%s%u",
+ opname, cond_to_str(cond), rd, rn, minus, offset);
+ }
+ }
+
+ if (is_pre) {
+ return Common::StringFromFormat("%s%sh\tr%d, [r%d, %sr%d]%s",
+ opname, cond_to_str(cond), rd, rn, minus, rm, bang);
+ } else {
+ return Common::StringFromFormat("%s%sh\tr%d, [r%d], %sr%d",
+ opname, cond_to_str(cond), rd, rn, minus, rm);
+ }
+}
+
+std::string ARM_Disasm::DisassembleMCR(Opcode opcode, uint32_t insn)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t crn = (insn >> 16) & 0xf;
+ uint8_t crd = (insn >> 12) & 0xf;
+ uint8_t cpnum = (insn >> 8) & 0xf;
+ uint8_t opcode2 = (insn >> 5) & 0x7;
+ uint8_t crm = insn & 0xf;
+
+ const char *opname = opcode_names[opcode];
+ return Common::StringFromFormat("%s%s\t%d, 0, r%d, cr%d, cr%d, {%d}",
+ opname, cond_to_str(cond), cpnum, crd, crn, crm, opcode2);
+}
+
+std::string ARM_Disasm::DisassembleMLA(Opcode opcode, uint32_t insn)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rd = (insn >> 16) & 0xf;
+ uint8_t rn = (insn >> 12) & 0xf;
+ uint8_t rs = (insn >> 8) & 0xf;
+ uint8_t rm = insn & 0xf;
+ uint8_t bit_s = (insn >> 20) & 1;
+
+ const char *opname = opcode_names[opcode];
+ return Common::StringFromFormat("%s%s%s\tr%d, r%d, r%d, r%d",
+ opname, cond_to_str(cond), bit_s ? "s" : "", rd, rm, rs, rn);
+}
+
+std::string ARM_Disasm::DisassembleUMLAL(Opcode opcode, uint32_t insn)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rdhi = (insn >> 16) & 0xf;
+ uint8_t rdlo = (insn >> 12) & 0xf;
+ uint8_t rs = (insn >> 8) & 0xf;
+ uint8_t rm = insn & 0xf;
+ uint8_t bit_s = (insn >> 20) & 1;
+
+ const char *opname = opcode_names[opcode];
+ return Common::StringFromFormat("%s%s%s\tr%d, r%d, r%d, r%d",
+ opname, cond_to_str(cond), bit_s ? "s" : "", rdlo, rdhi, rm, rs);
+}
+
+std::string ARM_Disasm::DisassembleMUL(Opcode opcode, uint32_t insn)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rd = (insn >> 16) & 0xf;
+ uint8_t rs = (insn >> 8) & 0xf;
+ uint8_t rm = insn & 0xf;
+ uint8_t bit_s = (insn >> 20) & 1;
+
+ const char *opname = opcode_names[opcode];
+ return Common::StringFromFormat("%s%s%s\tr%d, r%d, r%d",
+ opname, cond_to_str(cond), bit_s ? "s" : "", rd, rm, rs);
+}
+
+std::string ARM_Disasm::DisassembleMRS(uint32_t insn)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rd = (insn >> 12) & 0xf;
+ uint8_t ps = (insn >> 22) & 1;
+
+ return Common::StringFromFormat("mrs%s\tr%d, %s", cond_to_str(cond), rd, ps ? "spsr" : "cpsr");
+}
+
+std::string ARM_Disasm::DisassembleMSR(uint32_t insn)
+{
+ char flags[8];
+ int flag_index = 0;
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t is_immed = (insn >> 25) & 0x1;
+ uint8_t pd = (insn >> 22) & 1;
+ uint8_t mask = (insn >> 16) & 0xf;
+
+ if (mask & 1)
+ flags[flag_index++] = 'c';
+ if (mask & 2)
+ flags[flag_index++] = 'x';
+ if (mask & 4)
+ flags[flag_index++] = 's';
+ if (mask & 8)
+ flags[flag_index++] = 'f';
+ flags[flag_index] = 0;
+
+ if (is_immed) {
+ uint32_t immed = insn & 0xff;
+ uint8_t rotate = (insn >> 8) & 0xf;
+ uint8_t rotate2 = rotate << 1;
+ uint32_t rotated_val = (immed >> rotate2) | (immed << (32 - rotate2));
+ return Common::StringFromFormat("msr%s\t%s_%s, #0x%x",
+ cond_to_str(cond), pd ? "spsr" : "cpsr", flags, rotated_val);
+ }
+
+ uint8_t rm = insn & 0xf;
+
+ return Common::StringFromFormat("msr%s\t%s_%s, r%d",
+ cond_to_str(cond), pd ? "spsr" : "cpsr", flags, rm);
+}
+
+std::string ARM_Disasm::DisassemblePLD(uint32_t insn)
+{
+ uint8_t is_reg = (insn >> 25) & 0x1;
+ uint8_t is_up = (insn >> 23) & 0x1;
+ uint8_t rn = (insn >> 16) & 0xf;
+
+ const char *minus = "";
+ if (is_up == 0)
+ minus = "-";
+
+ if (is_reg) {
+ uint8_t rm = insn & 0xf;
+ return Common::StringFromFormat("pld\t[r%d, %sr%d]", rn, minus, rm);
+ }
+
+ uint16_t offset = insn & 0xfff;
+ if (offset == 0) {
+ return Common::StringFromFormat("pld\t[r%d]", rn);
+ } else {
+ return Common::StringFromFormat("pld\t[r%d, #%s%u]", rn, minus, offset);
+ }
+}
+
+std::string ARM_Disasm::DisassembleSWI(uint32_t insn)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint32_t sysnum = insn & 0x00ffffff;
+
+ return Common::StringFromFormat("swi%s 0x%x", cond_to_str(cond), sysnum);
+}
+
+std::string ARM_Disasm::DisassembleSWP(Opcode opcode, uint32_t insn)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rn = (insn >> 16) & 0xf;
+ uint8_t rd = (insn >> 12) & 0xf;
+ uint8_t rm = insn & 0xf;
+
+ const char *opname = opcode_names[opcode];
+ return Common::StringFromFormat("%s%s\tr%d, r%d, [r%d]", opname, cond_to_str(cond), rd, rm, rn);
+}
+
+Opcode ARM_Disasm::Decode(uint32_t insn) {
+ uint32_t bits27_26 = (insn >> 26) & 0x3;
+ switch (bits27_26) {
+ case 0x0:
+ return Decode00(insn);
+ case 0x1:
+ return Decode01(insn);
+ case 0x2:
+ return Decode10(insn);
+ case 0x3:
+ return Decode11(insn);
+ }
+ return OP_INVALID;
+}
+
+Opcode ARM_Disasm::Decode00(uint32_t insn) {
+ uint8_t bit25 = (insn >> 25) & 0x1;
+ uint8_t bit4 = (insn >> 4) & 0x1;
+ if (bit25 == 0 && bit4 == 1) {
+ if ((insn & 0x0ffffff0) == 0x012fff10) {
+ // Bx instruction
+ return OP_BX;
+ }
+ if ((insn & 0x0ff000f0) == 0x01600010) {
+ // Clz instruction
+ return OP_CLZ;
+ }
+ if ((insn & 0xfff000f0) == 0xe1200070) {
+ // Bkpt instruction
+ return OP_BKPT;
+ }
+ uint32_t bits7_4 = (insn >> 4) & 0xf;
+ if (bits7_4 == 0x9) {
+ if ((insn & 0x0ff00ff0) == 0x01000090) {
+ // Swp instruction
+ uint8_t bit22 = (insn >> 22) & 0x1;
+ if (bit22)
+ return OP_SWPB;
+ return OP_SWP;
+ }
+ // One of the multiply instructions
+ return DecodeMUL(insn);
+ }
+
+ uint8_t bit7 = (insn >> 7) & 0x1;
+ if (bit7 == 1) {
+ // One of the load/store halfword/byte instructions
+ return DecodeLDRH(insn);
+ }
+ }
+
+ // One of the data processing instructions
+ return DecodeALU(insn);
+}
+
+Opcode ARM_Disasm::Decode01(uint32_t insn) {
+ uint8_t is_reg = (insn >> 25) & 0x1;
+ uint8_t bit4 = (insn >> 4) & 0x1;
+ if (is_reg == 1 && bit4 == 1)
+ return OP_UNDEFINED;
+ uint8_t is_load = (insn >> 20) & 0x1;
+ uint8_t is_byte = (insn >> 22) & 0x1;
+ if ((insn & 0xfd70f000) == 0xf550f000) {
+ // Pre-load
+ return OP_PLD;
+ }
+ if (is_load) {
+ if (is_byte) {
+ // Load byte
+ return OP_LDRB;
+ }
+ // Load word
+ return OP_LDR;
+ }
+ if (is_byte) {
+ // Store byte
+ return OP_STRB;
+ }
+ // Store word
+ return OP_STR;
+}
+
+Opcode ARM_Disasm::Decode10(uint32_t insn) {
+ uint8_t bit25 = (insn >> 25) & 0x1;
+ if (bit25 == 0) {
+ // LDM/STM
+ uint8_t is_load = (insn >> 20) & 0x1;
+ if (is_load)
+ return OP_LDM;
+ return OP_STM;
+ }
+ // Branch or Branch with link
+ uint8_t is_link = (insn >> 24) & 1;
+ uint32_t offset = insn & 0xffffff;
+
+ // Sign-extend the 24-bit offset
+ if ((offset >> 23) & 1)
+ offset |= 0xff000000;
+
+ // Pre-compute the left-shift and the prefetch offset
+ offset <<= 2;
+ offset += 8;
+ if (is_link == 0)
+ return OP_B;
+ return OP_BL;
+}
+
+Opcode ARM_Disasm::Decode11(uint32_t insn) {
+ uint8_t bit25 = (insn >> 25) & 0x1;
+ if (bit25 == 0) {
+ // LDC, SDC
+ uint8_t is_load = (insn >> 20) & 0x1;
+ if (is_load) {
+ // LDC
+ return OP_LDC;
+ }
+ // STC
+ return OP_STC;
+ }
+
+ uint8_t bit24 = (insn >> 24) & 0x1;
+ if (bit24 == 0x1) {
+ // SWI
+ return OP_SWI;
+ }
+
+ uint8_t bit4 = (insn >> 4) & 0x1;
+ uint8_t cpnum = (insn >> 8) & 0xf;
+
+ if (cpnum == 15) {
+ // Special case for coprocessor 15
+ uint8_t opcode = (insn >> 21) & 0x7;
+ if (bit4 == 0 || opcode != 0) {
+ // This is an unexpected bit pattern. Create an undefined
+ // instruction in case this is ever executed.
+ return OP_UNDEFINED;
+ }
+
+ // MRC, MCR
+ uint8_t is_mrc = (insn >> 20) & 0x1;
+ if (is_mrc)
+ return OP_MRC;
+ return OP_MCR;
+ }
+
+ if (bit4 == 0) {
+ // CDP
+ return OP_CDP;
+ }
+ // MRC, MCR
+ uint8_t is_mrc = (insn >> 20) & 0x1;
+ if (is_mrc)
+ return OP_MRC;
+ return OP_MCR;
+}
+
+Opcode ARM_Disasm::DecodeMUL(uint32_t insn) {
+ uint8_t bit24 = (insn >> 24) & 0x1;
+ if (bit24 != 0) {
+ // This is an unexpected bit pattern. Create an undefined
+ // instruction in case this is ever executed.
+ return OP_UNDEFINED;
+ }
+ uint8_t bit23 = (insn >> 23) & 0x1;
+ uint8_t bit22_U = (insn >> 22) & 0x1;
+ uint8_t bit21_A = (insn >> 21) & 0x1;
+ if (bit23 == 0) {
+ // 32-bit multiply
+ if (bit22_U != 0) {
+ // This is an unexpected bit pattern. Create an undefined
+ // instruction in case this is ever executed.
+ return OP_UNDEFINED;
+ }
+ if (bit21_A == 0)
+ return OP_MUL;
+ return OP_MLA;
+ }
+ // 64-bit multiply
+ if (bit22_U == 0) {
+ // Unsigned multiply long
+ if (bit21_A == 0)
+ return OP_UMULL;
+ return OP_UMLAL;
+ }
+ // Signed multiply long
+ if (bit21_A == 0)
+ return OP_SMULL;
+ return OP_SMLAL;
+}
+
+Opcode ARM_Disasm::DecodeLDRH(uint32_t insn) {
+ uint8_t is_load = (insn >> 20) & 0x1;
+ uint8_t bits_65 = (insn >> 5) & 0x3;
+ if (is_load) {
+ if (bits_65 == 0x1) {
+ // Load unsigned halfword
+ return OP_LDRH;
+ } else if (bits_65 == 0x2) {
+ // Load signed byte
+ return OP_LDRSB;
+ }
+ // Signed halfword
+ if (bits_65 != 0x3) {
+ // This is an unexpected bit pattern. Create an undefined
+ // instruction in case this is ever executed.
+ return OP_UNDEFINED;
+ }
+ // Load signed halfword
+ return OP_LDRSH;
+ }
+ // Store halfword
+ if (bits_65 != 0x1) {
+ // This is an unexpected bit pattern. Create an undefined
+ // instruction in case this is ever executed.
+ return OP_UNDEFINED;
+ }
+ // Store halfword
+ return OP_STRH;
+}
+
+Opcode ARM_Disasm::DecodeALU(uint32_t insn) {
+ uint8_t is_immed = (insn >> 25) & 0x1;
+ uint8_t opcode = (insn >> 21) & 0xf;
+ uint8_t bit_s = (insn >> 20) & 1;
+ uint8_t shift_is_reg = (insn >> 4) & 1;
+ uint8_t bit7 = (insn >> 7) & 1;
+ if (!is_immed && shift_is_reg && (bit7 != 0)) {
+ // This is an unexpected bit pattern. Create an undefined
+ // instruction in case this is ever executed.
+ return OP_UNDEFINED;
+ }
+ switch (opcode) {
+ case 0x0:
+ return OP_AND;
+ case 0x1:
+ return OP_EOR;
+ case 0x2:
+ return OP_SUB;
+ case 0x3:
+ return OP_RSB;
+ case 0x4:
+ return OP_ADD;
+ case 0x5:
+ return OP_ADC;
+ case 0x6:
+ return OP_SBC;
+ case 0x7:
+ return OP_RSC;
+ case 0x8:
+ if (bit_s)
+ return OP_TST;
+ return OP_MRS;
+ case 0x9:
+ if (bit_s)
+ return OP_TEQ;
+ return OP_MSR;
+ case 0xa:
+ if (bit_s)
+ return OP_CMP;
+ return OP_MRS;
+ case 0xb:
+ if (bit_s)
+ return OP_CMN;
+ return OP_MSR;
+ case 0xc:
+ return OP_ORR;
+ case 0xd:
+ return OP_MOV;
+ case 0xe:
+ return OP_BIC;
+ case 0xf:
+ return OP_MVN;
+ }
+ // Unreachable
+ return OP_INVALID;
+}
--- /dev/null
+// Copyright 2006 The Android Open Source Project
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+
+// Note: this list of opcodes must match the list used to initialize
+// the opflags[] array in opcode.cpp.
+enum Opcode {
+ OP_INVALID,
+ OP_UNDEFINED,
+ OP_ADC,
+ OP_ADD,
+ OP_AND,
+ OP_B,
+ OP_BL,
+ OP_BIC,
+ OP_BKPT,
+ OP_BLX,
+ OP_BX,
+ OP_CDP,
+ OP_CLZ,
+ OP_CMN,
+ OP_CMP,
+ OP_EOR,
+ OP_LDC,
+ OP_LDM,
+ OP_LDR,
+ OP_LDRB,
+ OP_LDRBT,
+ OP_LDRH,
+ OP_LDRSB,
+ OP_LDRSH,
+ OP_LDRT,
+ OP_MCR,
+ OP_MLA,
+ OP_MOV,
+ OP_MRC,
+ OP_MRS,
+ OP_MSR,
+ OP_MUL,
+ OP_MVN,
+ OP_ORR,
+ OP_PLD,
+ OP_RSB,
+ OP_RSC,
+ OP_SBC,
+ OP_SMLAL,
+ OP_SMULL,
+ OP_STC,
+ OP_STM,
+ OP_STR,
+ OP_STRB,
+ OP_STRBT,
+ OP_STRH,
+ OP_STRT,
+ OP_SUB,
+ OP_SWI,
+ OP_SWP,
+ OP_SWPB,
+ OP_TEQ,
+ OP_TST,
+ OP_UMLAL,
+ OP_UMULL,
+
+ // Define thumb opcodes
+ OP_THUMB_UNDEFINED,
+ OP_THUMB_ADC,
+ OP_THUMB_ADD,
+ OP_THUMB_AND,
+ OP_THUMB_ASR,
+ OP_THUMB_B,
+ OP_THUMB_BIC,
+ OP_THUMB_BKPT,
+ OP_THUMB_BL,
+ OP_THUMB_BLX,
+ OP_THUMB_BX,
+ OP_THUMB_CMN,
+ OP_THUMB_CMP,
+ OP_THUMB_EOR,
+ OP_THUMB_LDMIA,
+ OP_THUMB_LDR,
+ OP_THUMB_LDRB,
+ OP_THUMB_LDRH,
+ OP_THUMB_LDRSB,
+ OP_THUMB_LDRSH,
+ OP_THUMB_LSL,
+ OP_THUMB_LSR,
+ OP_THUMB_MOV,
+ OP_THUMB_MUL,
+ OP_THUMB_MVN,
+ OP_THUMB_NEG,
+ OP_THUMB_ORR,
+ OP_THUMB_POP,
+ OP_THUMB_PUSH,
+ OP_THUMB_ROR,
+ OP_THUMB_SBC,
+ OP_THUMB_STMIA,
+ OP_THUMB_STR,
+ OP_THUMB_STRB,
+ OP_THUMB_STRH,
+ OP_THUMB_SUB,
+ OP_THUMB_SWI,
+ OP_THUMB_TST,
+
+ OP_END // must be last
+};
+
+class ARM_Disasm {
+ public:
+ static std::string Disassemble(uint32_t addr, uint32_t insn);
+ static Opcode Decode(uint32_t insn);
+
+ private:
+ static Opcode Decode00(uint32_t insn);
+ static Opcode Decode01(uint32_t insn);
+ static Opcode Decode10(uint32_t insn);
+ static Opcode Decode11(uint32_t insn);
+ static Opcode DecodeMUL(uint32_t insn);
+ static Opcode DecodeLDRH(uint32_t insn);
+ static Opcode DecodeALU(uint32_t insn);
+
+ static std::string DisassembleALU(Opcode opcode, uint32_t insn);
+ static std::string DisassembleBranch(uint32_t addr, Opcode opcode, uint32_t insn);
+ static std::string DisassembleBX(uint32_t insn);
+ static std::string DisassembleBKPT(uint32_t insn);
+ static std::string DisassembleCLZ(uint32_t insn);
+ static std::string DisassembleMemblock(Opcode opcode, uint32_t insn);
+ static std::string DisassembleMem(uint32_t insn);
+ static std::string DisassembleMemHalf(uint32_t insn);
+ static std::string DisassembleMCR(Opcode opcode, uint32_t insn);
+ static std::string DisassembleMLA(Opcode opcode, uint32_t insn);
+ static std::string DisassembleUMLAL(Opcode opcode, uint32_t insn);
+ static std::string DisassembleMUL(Opcode opcode, uint32_t insn);
+ static std::string DisassembleMRS(uint32_t insn);
+ static std::string DisassembleMSR(uint32_t insn);
+ static std::string DisassemblePLD(uint32_t insn);
+ static std::string DisassembleSWI(uint32_t insn);
+ static std::string DisassembleSWP(Opcode opcode, uint32_t insn);
+};
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+//massive fixed by ichfly XDS/3dmoo team
+
+#include "Kernel.h"
+
+#include "arm/skyeye_common/armcpu.h"
+#include "arm/skyeye_common/armemu.h"
+#include "arm/skyeye_common/vfp/vfp.h"
+
+#include "arm/dyncom/arm_dyncom.h"
+#include "arm/dyncom/arm_dyncom_interpreter.h"
+#include "arm/dyncom/arm_dyncom_run.h"
+
+//#define CACHE_BUFFER_SIZE (64 * 1024 * 2000)
+
+
+const static cpu_config_t s_arm11_cpu_info = {
+ "armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE
+};
+
+ARM_DynCom::ARM_DynCom() {
+ state = (ARMul_State*)malloc(sizeof(ARMul_State));
+
+ ARMul_NewState(state);
+ ARMul_SelectProcessor(state, ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop);
+
+ state->abort_model = 0;
+ state->cpu = (cpu_config_t*)&s_arm11_cpu_info;
+
+ state->bigendSig = LOW;
+ state->lateabtSig = LOW;
+ state->NirqSig = HIGH;
+
+ // Reset the core to initial state
+ ARMul_Reset(state);
+ state->NextInstr = RESUME; // NOTE: This will be overwritten by LoadContext
+ state->Emulate = RUN;
+
+ // Switch to the desired privilege mode.
+ switch_mode(state, 0);
+
+ state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack
+ state->Reg[15] = 0x00000000;
+}
+
+ARM_DynCom::~ARM_DynCom() {
+}
+
+void ARM_DynCom::SetPC(u32 pc) {
+ state->Reg[15] = pc;
+}
+
+u32 ARM_DynCom::GetPC() const {
+ return state->Reg[15];
+}
+
+u32 ARM_DynCom::GetReg(int index) const {
+ return state->Reg[index];
+}
+
+void ARM_DynCom::SetReg(int index, u32 value) {
+ state->Reg[index] = value;
+}
+
+u32 ARM_DynCom::GetCPSR() const {
+ return state->Cpsr;
+}
+
+void ARM_DynCom::SetCPSR(u32 cpsr) {
+ state->Cpsr = cpsr;
+}
+
+void ARM_DynCom::AddTicks(u64 ticks) {
+ down_count -= ticks;
+ //if (down_count < 0)
+ //CoreTiming::Advance(); //ichfly todo
+}
+
+u64 ARM_DynCom::ExecuteInstructions(int num_instructions) {
+ state->NumInstrsToExecute = num_instructions;
+
+ // Dyncom only breaks on instruction dispatch. This only happens on every instruction when
+ // executing one instruction at a time. Otherwise, if a block is being executed, more
+ // instructions may actually be executed than specified.
+ unsigned ticks_executed = InterpreterMainLoop(state);
+ AddTicks(ticks_executed);
+ return ticks_executed;
+}
+
+void ARM_DynCom::SaveContext(ThreadContext& ctx) {
+ memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers));
+ memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers));
+
+ ctx.sp = state->Reg[13];
+ ctx.lr = state->Reg[14];
+ ctx.pc = state->pc;
+ ctx.cpsr = state->Cpsr;
+
+ ctx.fpscr = state->VFP[1];
+ ctx.fpexc = state->VFP[2];
+
+ ctx.reg_15 = state->Reg[15];
+ ctx.mode = state->NextInstr;
+
+ //Dyncore
+ state->m_currentThread->m_owner->repretBuffersize = state->inst_buffsize;
+ state->m_currentThread->m_owner->repretBuffertop = state->inst_bufftop;
+
+ ctx.NFlag = state->NFlag;
+ ctx.ZFlag = state->ZFlag;
+ ctx.CFlag = state->CFlag;
+ ctx.VFlag = state->VFlag;
+ ctx.IFFlags = state->IFFlags;
+
+}
+
+void ARM_DynCom::LoadContext(const ThreadContext& ctx) {
+ memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers));
+ memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers));
+
+ state->Reg[13] = ctx.sp;
+ state->Reg[14] = ctx.lr;
+ state->pc = ctx.pc;
+ state->Cpsr = ctx.cpsr;
+ state->Mode = ctx.cpsr&0x1F;
+
+ state->VFP[1] = ctx.fpscr;
+ state->VFP[2] = ctx.fpexc;
+
+ state->Reg[15] = ctx.reg_15;
+ state->NextInstr = ctx.mode;
+
+ //Dyncore
+ state->inst_buf = state->m_currentThread->m_owner->repretBuffer;
+ state->inst_buffsize = state->m_currentThread->m_owner->repretBuffersize;
+ state->inst_bufftop = state->m_currentThread->m_owner->repretBuffertop;
+ state->CreamCache = state->m_currentThread->m_owner->CreamCache;
+
+ state->NFlag = ctx.NFlag;
+ state->ZFlag = ctx.ZFlag;
+ state->CFlag = ctx.CFlag;
+ state->VFlag = ctx.VFlag;
+ state->IFFlags = ctx.IFFlags;
+}
+
+void ARM_DynCom::PrepareReschedule() {
+ state->NumInstrsToExecute = 0;
+}
+u64 ARM_DynCom::GetTicks() const {
+ return state->NumInstrs;
+}
\ No newline at end of file
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include "arm/arm_interface.h"
+#include "arm/skyeye_common/armdefs.h"
+
+class ARM_DynCom final : virtual public ARM_Interface {
+public:
+
+ ARM_DynCom();
+ ~ARM_DynCom();
+
+ /**
+ * Set the Program Counter to an address
+ * @param pc Address to set PC to
+ */
+ void SetPC(u32 pc) override;
+
+ /*
+ * Get the current Program Counter
+ * @return Returns current PC
+ */
+ u32 GetPC() const override;
+
+ /**
+ * Get an ARM register
+ * @param index Register index (0-15)
+ * @return Returns the value in the register
+ */
+ u32 GetReg(int index) const override;
+
+ /**
+ * Set an ARM register
+ * @param index Register index (0-15)
+ * @param value Value to set register to
+ */
+ void SetReg(int index, u32 value) override;
+
+ /**
+ * Get the current CPSR register
+ * @return Returns the value of the CPSR register
+ */
+ u32 GetCPSR() const override;
+
+ /**
+ * Set the current CPSR register
+ * @param cpsr Value to set CPSR to
+ */
+ void SetCPSR(u32 cpsr) override;
+
+ /**
+ * Returns the number of clock ticks since the last reset
+ * @return Returns number of clock ticks
+ */
+ u64 GetTicks() const override;
+
+ /**
+ * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
+ * @param ticks Number of ticks to advance the CPU core
+ */
+ void AddTicks(u64 ticks) override;
+
+ /**
+ * Saves the current CPU context
+ * @param ctx Thread context to save
+ */
+ void SaveContext(ThreadContext& ctx) override;
+
+ /**
+ * Loads a CPU context
+ * @param ctx Thread context to load
+ */
+ void LoadContext(const ThreadContext& ctx) override;
+
+ /// Prepare core for thread reschedule (if needed to correctly handle state)
+ void PrepareReschedule() override;
+
+ /**
+ * Executes the given number of instructions
+ * @param num_instructions Number of instructions to executes
+ */
+ u64 ExecuteInstructions(int num_instructions) override;
+ ARMul_State* state;
+
+private:
+};
--- /dev/null
+// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+//massive fixed by ichfly XDS/3dmoo team
+
+#include "Kernel.h"
+
+#include "arm/skyeye_common/arm_regformat.h"
+#include "arm/skyeye_common/armdefs.h"
+#include "arm/dyncom/arm_dyncom_dec.h"
+
+#define BITS(s, a, b) ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1))
+#define BIT(s, n) ((s >> (n)) & 1)
+
+const ISEITEM arm_instruction[] = {
+ { "vmla", 4, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 4, 4, 0 },
+ { "vmls", 7, ARMVFP2, 28, 31, 0xF, 25, 27, 0x1, 23, 23, 1, 11, 11, 0, 8, 9, 0x2, 6, 6, 1, 4, 4, 0 },
+ { "vnmla", 4, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 4, 4, 0 },
+ { "vnmla", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 },
+ { "vnmls", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 },
+ { "vnmul", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 },
+ { "vmul", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 },
+ { "vadd", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x3, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 },
+ { "vsub", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x3, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 },
+ { "vdiv", 5, ARMVFP2, 23, 27, 0x1D, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 },
+ { "vmov(i)", 4, ARMVFP3, 23, 27, 0x1D, 20, 21, 0x3, 9, 11, 0x5, 4, 7, 0 },
+ { "vmov(r)", 5, ARMVFP3, 23, 27, 0x1D, 16, 21, 0x30, 9, 11, 0x5, 6, 7, 1, 4, 4, 0 },
+ { "vabs", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x30, 9, 11, 0x5, 6, 7, 3, 4, 4, 0 },
+ { "vneg", 5, ARMVFP2, 23, 27, 0x1D, 17, 21, 0x18, 9, 11, 0x5, 6, 7, 1, 4, 4, 0 },
+ { "vsqrt", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x31, 9, 11, 0x5, 6, 7, 3, 4, 4, 0 },
+ { "vcmp", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x34, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 },
+ { "vcmp2", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x35, 9, 11, 0x5, 0, 6, 0x40 },
+ { "vcvt(bds)", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x37, 9, 11, 0x5, 6, 7, 3, 4, 4, 0 },
+ { "vcvt(bff)", 6, ARMVFP3, 23, 27, 0x1D, 19, 21, 0x7, 17, 17, 0x1, 9, 11, 5, 6, 6, 1 },
+ { "vcvt(bfi)", 5, ARMVFP2, 23, 27, 0x1D, 19, 21, 0x7, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 },
+ { "vmovbrs", 3, ARMVFP2, 21, 27, 0x70, 8, 11, 0xA, 0, 6, 0x10 },
+ { "vmsr", 2, ARMVFP2, 20, 27, 0xEE, 0, 11, 0xA10 },
+ { "vmovbrc", 4, ARMVFP2, 23, 27, 0x1C, 20, 20, 0x0, 8, 11, 0xB, 0, 4, 0x10 },
+ { "vmrs", 2, ARMVFP2, 20, 27, 0xEF, 0, 11, 0xA10 },
+ { "vmovbcr", 4, ARMVFP2, 24, 27, 0xE, 20, 20, 1, 8, 11, 0xB, 0, 4, 0x10 },
+ { "vmovbrrss", 3, ARMVFP2, 21, 27, 0x62, 8, 11, 0xA, 4, 4, 1 },
+ { "vmovbrrd", 3, ARMVFP2, 21, 27, 0x62, 6, 11, 0x2C, 4, 4, 1 },
+ { "vstr", 3, ARMVFP2, 24, 27, 0xD, 20, 21, 0, 9, 11, 5 },
+ { "vpush", 3, ARMVFP2, 23, 27, 0x1A, 16, 21, 0x2D, 9, 11, 5 },
+ { "vstm", 3, ARMVFP2, 25, 27, 0x6, 20, 20, 0, 9, 11, 5 },
+ { "vpop", 3, ARMVFP2, 23, 27, 0x19, 16, 21, 0x3D, 9, 11, 5 },
+ { "vldr", 3, ARMVFP2, 24, 27, 0xD, 20, 21, 1, 9, 11, 5 },
+ { "vldm", 3, ARMVFP2, 25, 27, 0x6, 20, 20, 1, 9, 11, 5 },
+
+ { "srs", 4, 6, 25, 31, 0x0000007c, 22, 22, 0x00000001, 16, 20, 0x0000000d, 8, 11, 0x00000005 },
+ { "rfe", 4, 6, 25, 31, 0x0000007c, 22, 22, 0x00000000, 20, 20, 0x00000001, 8, 11, 0x0000000a },
+ { "bkpt", 2, 3, 20, 27, 0x00000012, 4, 7, 0x00000007 },
+ { "blx", 1, 3, 25, 31, 0x0000007d },
+ { "cps", 3, 6, 20, 31, 0x00000f10, 16, 16, 0x00000000, 5, 5, 0x00000000 },
+ { "pld", 4, 4, 26, 31, 0x0000003d, 24, 24, 0x00000001, 20, 22, 0x00000005, 12, 15, 0x0000000f },
+ { "setend", 2, 6, 16, 31, 0x0000f101, 4, 7, 0x00000000 },
+ { "clrex", 1, 6, 0, 31, 0xf57ff01f },
+ { "rev16", 2, 6, 16, 27, 0x000006bf, 4, 11, 0x000000fb },
+ { "usad8", 3, 6, 20, 27, 0x00000078, 12, 15, 0x0000000f, 4, 7, 0x00000001 },
+ { "sxtb", 2, 6, 16, 27, 0x000006af, 4, 7, 0x00000007 },
+ { "uxtb", 2, 6, 16, 27, 0x000006ef, 4, 7, 0x00000007 },
+ { "sxth", 2, 6, 16, 27, 0x000006bf, 4, 7, 0x00000007 },
+ { "sxtb16", 2, 6, 16, 27, 0x0000068f, 4, 7, 0x00000007 },
+ { "uxth", 2, 6, 16, 27, 0x000006ff, 4, 7, 0x00000007 },
+ { "uxtb16", 2, 6, 16, 27, 0x000006cf, 4, 7, 0x00000007 },
+ { "cpy", 2, 6, 20, 27, 0x0000001a, 4, 11, 0x00000000 },
+ { "uxtab", 2, 6, 20, 27, 0x0000006e, 4, 9, 0x00000007 },
+ { "ssub8", 2, 6, 20, 27, 0x00000061, 4, 7, 0x0000000f },
+ { "shsub8", 2, 6, 20, 27, 0x00000063, 4, 7, 0x0000000f },
+ { "ssubaddx", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000005 },
+ { "strex", 2, 6, 20, 27, 0x00000018, 4, 7, 0x00000009 },
+ { "strexb", 2, 7, 20, 27, 0x0000001c, 4, 7, 0x00000009 },
+ { "swp", 2, 0, 20, 27, 0x00000010, 4, 7, 0x00000009 },
+ { "swpb", 2, 0, 20, 27, 0x00000014, 4, 7, 0x00000009 },
+ { "ssub16", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000007 },
+ { "ssat16", 2, 6, 20, 27, 0x0000006a, 4, 7, 0x00000003 },
+ { "shsubaddx", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000005 },
+ { "qsubaddx", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000005 },
+ { "shaddsubx", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000003 },
+ { "shadd8", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000009 },
+ { "shadd16", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000001 },
+ { "sel", 2, 6, 20, 27, 0x00000068, 4, 7, 0x0000000b },
+ { "saddsubx", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000003 },
+ { "sadd8", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000009 },
+ { "sadd16", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000001 },
+ { "shsub16", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000007 },
+ { "umaal", 2, 6, 20, 27, 0x00000004, 4, 7, 0x00000009 },
+ { "uxtab16", 2, 6, 20, 27, 0x0000006c, 4, 7, 0x00000007 },
+ { "usubaddx", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000005 },
+ { "usub8", 2, 6, 20, 27, 0x00000065, 4, 7, 0x0000000f },
+ { "usub16", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000007 },
+ { "usat16", 2, 6, 20, 27, 0x0000006e, 4, 7, 0x00000003 },
+ { "usada8", 2, 6, 20, 27, 0x00000078, 4, 7, 0x00000001 },
+ { "uqsubaddx", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000005 },
+ { "uqsub8", 2, 6, 20, 27, 0x00000066, 4, 7, 0x0000000f },
+ { "uqsub16", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000007 },
+ { "uqaddsubx", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000003 },
+ { "uqadd8", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000009 },
+ { "uqadd16", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000001 },
+ { "sxtab", 2, 6, 20, 27, 0x0000006a, 4, 7, 0x00000007 },
+ { "uhsubaddx", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000005 },
+ { "uhsub8", 2, 6, 20, 27, 0x00000067, 4, 7, 0x0000000f },
+ { "uhsub16", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000007 },
+ { "uhaddsubx", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000003 },
+ { "uhadd8", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000009 },
+ { "uhadd16", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000001 },
+ { "uaddsubx", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000003 },
+ { "uadd8", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000009 },
+ { "uadd16", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000001 },
+ { "sxtah", 2, 6, 20, 27, 0x0000006b, 4, 7, 0x00000007 },
+ { "sxtab16", 2, 6, 20, 27, 0x00000068, 4, 7, 0x00000007 },
+ { "qadd8", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000009 },
+ { "bxj", 2, 5, 20, 27, 0x00000012, 4, 7, 0x00000002 },
+ { "clz", 2, 3, 20, 27, 0x00000016, 4, 7, 0x00000001 },
+ { "uxtah", 2, 6, 20, 27, 0x0000006f, 4, 7, 0x00000007 },
+ { "bx", 2, 2, 20, 27, 0x00000012, 4, 7, 0x00000001 },
+ { "rev", 2, 6, 20, 27, 0x0000006b, 4, 7, 0x00000003 },
+ { "blx", 2, 3, 20, 27, 0x00000012, 4, 7, 0x00000003 },
+ { "revsh", 2, 6, 20, 27, 0x0000006f, 4, 7, 0x0000000b },
+ { "qadd", 2, 4, 20, 27, 0x00000010, 4, 7, 0x00000005 },
+ { "qadd16", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000001 },
+ { "qaddsubx", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000003 },
+ { "ldrex", 2, 0, 20, 27, 0x00000019, 4, 7, 0x00000009 },
+ { "qdadd", 2, 4, 20, 27, 0x00000014, 4, 7, 0x00000005 },
+ { "qdsub", 2, 4, 20, 27, 0x00000016, 4, 7, 0x00000005 },
+ { "qsub", 2, 4, 20, 27, 0x00000012, 4, 7, 0x00000005 },
+ { "ldrexb", 2, 7, 20, 27, 0x0000001d, 4, 7, 0x00000009 },
+ { "qsub8", 2, 6, 20, 27, 0x00000062, 4, 7, 0x0000000f },
+ { "qsub16", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000007 },
+ { "smuad", 4, 6, 20, 27, 0x00000070, 12, 15, 0x0000000f, 6, 7, 0x00000000, 4, 4, 0x00000001 },
+ { "smmul", 4, 6, 20, 27, 0x00000075, 12, 15, 0x0000000f, 6, 7, 0x00000000, 4, 4, 0x00000001 },
+ { "smusd", 4, 6, 20, 27, 0x00000070, 12, 15, 0x0000000f, 6, 7, 0x00000001, 4, 4, 0x00000001 },
+ { "smlsd", 3, 6, 20, 27, 0x00000070, 6, 7, 0x00000001, 4, 4, 0x00000001 },
+ { "smlsld", 3, 6, 20, 27, 0x00000074, 6, 7, 0x00000001, 4, 4, 0x00000001 },
+ { "smmla", 3, 6, 20, 27, 0x00000075, 6, 7, 0x00000000, 4, 4, 0x00000001 },
+ { "smmls", 3, 6, 20, 27, 0x00000075, 6, 7, 0x00000003, 4, 4, 0x00000001 },
+ { "smlald", 3, 6, 20, 27, 0x00000074, 6, 7, 0x00000000, 4, 4, 0x00000001 },
+ { "smlad", 3, 6, 20, 27, 0x00000070, 6, 7, 0x00000000, 4, 4, 0x00000001 },
+ { "smlaw", 3, 4, 20, 27, 0x00000012, 7, 7, 0x00000001, 4, 5, 0x00000000 },
+ { "smulw", 3, 4, 20, 27, 0x00000012, 7, 7, 0x00000001, 4, 5, 0x00000002 },
+ { "pkhtb", 2, 6, 20, 27, 0x00000068, 4, 6, 0x00000005 },
+ { "pkhbt", 2, 6, 20, 27, 0x00000068, 4, 6, 0x00000001 },
+ { "smul", 3, 4, 20, 27, 0x00000016, 7, 7, 0x00000001, 4, 4, 0x00000000 },
+ { "smlalxy", 3, 4, 20, 27, 0x00000014, 7, 7, 0x00000001, 4, 4, 0x00000000 },
+ { "smla", 3, 4, 20, 27, 0x00000010, 7, 7, 0x00000001, 4, 4, 0x00000000 },
+ { "mcrr", 1, 6, 20, 27, 0x000000c4 },
+ { "mrrc", 1, 6, 20, 27, 0x000000c5 },
+ { "cmp", 2, 0, 26, 27, 0x00000000, 20, 24, 0x00000015 },
+ { "tst", 2, 0, 26, 27, 0x00000000, 20, 24, 0x00000011 },
+ { "teq", 2, 0, 26, 27, 0x00000000, 20, 24, 0x00000013 },
+ { "cmn", 2, 0, 26, 27, 0x00000000, 20, 24, 0x00000017 },
+ { "smull", 2, 0, 21, 27, 0x00000006, 4, 7, 0x00000009 },
+ { "umull", 2, 0, 21, 27, 0x00000004, 4, 7, 0x00000009 },
+ { "umlal", 2, 0, 21, 27, 0x00000005, 4, 7, 0x00000009 },
+ { "smlal", 2, 0, 21, 27, 0x00000007, 4, 7, 0x00000009 },
+ { "mul", 2, 0, 21, 27, 0x00000000, 4, 7, 0x00000009 },
+ { "mla", 2, 0, 21, 27, 0x00000001, 4, 7, 0x00000009 },
+ { "ssat", 2, 6, 21, 27, 0x00000035, 4, 5, 0x00000001 },
+ { "usat", 2, 6, 21, 27, 0x00000037, 4, 5, 0x00000001 },
+ { "mrs", 4, 0, 23, 27, 0x00000002, 20, 21, 0x00000000, 16, 19, 0x0000000f, 0, 11, 0x00000000 },
+ { "msr", 3, 0, 23, 27, 0x00000002, 20, 21, 0x00000002, 4, 7, 0x00000000 },
+ { "and", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000000 },
+ { "bic", 2, 0, 26, 27, 0x00000000, 21, 24, 0x0000000e },
+ { "ldm", 3, 0, 25, 27, 0x00000004, 20, 22, 0x00000005, 15, 15, 0x00000000 },
+ { "eor", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000001 },
+ { "add", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000004 },
+ { "rsb", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000003 },
+ { "rsc", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000007 },
+ { "sbc", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000006 },
+ { "adc", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000005 },
+ { "sub", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000002 },
+ { "orr", 2, 0, 26, 27, 0x00000000, 21, 24, 0x0000000c },
+ { "mvn", 2, 0, 26, 27, 0x00000000, 21, 24, 0x0000000f },
+ { "mov", 2, 0, 26, 27, 0x00000000, 21, 24, 0x0000000d },
+ { "stm", 2, 0, 25, 27, 0x00000004, 20, 22, 0x00000004 },
+ { "ldm", 4, 0, 25, 27, 0x00000004, 22, 22, 0x00000001, 20, 20, 0x00000001, 15, 15, 0x00000001 },
+ { "ldrsh", 3, 2, 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000f },
+ { "stm", 3, 0, 25, 27, 0x00000004, 22, 22, 0x00000000, 20, 20, 0x00000000 },
+ { "ldm", 3, 0, 25, 27, 0x00000004, 22, 22, 0x00000000, 20, 20, 0x00000001 },
+ { "ldrsb", 3, 2, 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000d },
+ { "strd", 3, 4, 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000f },
+ { "ldrh", 3, 0, 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000b },
+ { "strh", 3, 0, 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000b },
+ { "ldrd", 3, 4, 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000d },
+ { "strt", 3, 0, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000002 },
+ { "strbt", 3, 0, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000006 },
+ { "ldrbt", 3, 0, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000007 },
+ { "ldrt", 3, 0, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000003 },
+ { "mrc", 3, 6, 24, 27, 0x0000000e, 20, 20, 0x00000001, 4, 4, 0x00000001 },
+ { "mcr", 3, 0, 24, 27, 0x0000000e, 20, 20, 0x00000000, 4, 4, 0x00000001 },
+ { "msr", 2, 0, 23, 27, 0x00000006, 20, 21, 0x00000002 },
+ { "ldrb", 3, 0, 26, 27, 0x00000001, 22, 22, 0x00000001, 20, 20, 0x00000001 },
+ { "strb", 3, 0, 26, 27, 0x00000001, 22, 22, 0x00000001, 20, 20, 0x00000000 },
+ { "ldr", 4, 0, 28, 31, 0x0000000e, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000001 },
+ { "ldrcond", 3, 0, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000001 },
+ { "str", 3, 0, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000000 },
+ { "cdp", 2, 0, 24, 27, 0x0000000e, 4, 4, 0x00000000 },
+ { "stc", 2, 0, 25, 27, 0x00000006, 20, 20, 0x00000000 },
+ { "ldc", 2, 0, 25, 27, 0x00000006, 20, 20, 0x00000001 },
+ { "swi", 1, 0, 24, 27, 0x0000000f },
+ { "bbl", 1, 0, 25, 27, 0x00000005 },
+ { "ldrexd", 2, ARMV6K, 20, 27, 0x0000001B, 4, 7, 0x00000009 },
+ { "strexd", 2, ARMV6K, 20, 27, 0x0000001A, 4, 7, 0x00000009 },
+ { "ldrexh", 2, ARMV6K, 20, 27, 0x0000001F, 4, 7, 0x00000009 },
+ { "strexh", 2, ARMV6K, 20, 27, 0x0000001E, 4, 7, 0x00000009 },
+};
+
+const ISEITEM arm_exclusion_code[] = {
+ { "vmla", 0, ARMVFP2, 0 },
+ { "vmls", 0, ARMVFP2, 0 },
+ { "vnmla", 0, ARMVFP2, 0 },
+ { "vnmla", 0, ARMVFP2, 0 },
+ { "vnmls", 0, ARMVFP2, 0 },
+ { "vnmul", 0, ARMVFP2, 0 },
+ { "vmul", 0, ARMVFP2, 0 },
+ { "vadd", 0, ARMVFP2, 0 },
+ { "vsub", 0, ARMVFP2, 0 },
+ { "vdiv", 0, ARMVFP2, 0 },
+ { "vmov(i)", 0, ARMVFP3, 0 },
+ { "vmov(r)", 0, ARMVFP3, 0 },
+ { "vabs", 0, ARMVFP2, 0 },
+ { "vneg", 0, ARMVFP2, 0 },
+ { "vsqrt", 0, ARMVFP2, 0 },
+ { "vcmp", 0, ARMVFP2, 0 },
+ { "vcmp2", 0, ARMVFP2, 0 },
+ { "vcvt(bff)", 0, ARMVFP3, 4, 4, 1 },
+ { "vcvt(bds)", 0, ARMVFP2, 0 },
+ { "vcvt(bfi)", 0, ARMVFP2, 0 },
+ { "vmovbrs", 0, ARMVFP2, 0 },
+ { "vmsr", 0, ARMVFP2, 0 },
+ { "vmovbrc", 0, ARMVFP2, 0 },
+ { "vmrs", 0, ARMVFP2, 0 },
+ { "vmovbcr", 0, ARMVFP2, 0 },
+ { "vmovbrrss", 0, ARMVFP2, 0 },
+ { "vmovbrrd", 0, ARMVFP2, 0 },
+ { "vstr", 0, ARMVFP2, 0 },
+ { "vpush", 0, ARMVFP2, 0 },
+ { "vstm", 0, ARMVFP2, 0 },
+ { "vpop", 0, ARMVFP2, 0 },
+ { "vldr", 0, ARMVFP2, 0 },
+ { "vldm", 0, ARMVFP2, 0 },
+
+ { "srs", 0, 6, 0 },
+ { "rfe", 0, 6, 0 },
+ { "bkpt", 0, 3, 0 },
+ { "blx", 0, 3, 0 },
+ { "cps", 0, 6, 0 },
+ { "pld", 0, 4, 0 },
+ { "setend", 0, 6, 0 },
+ { "clrex", 0, 6, 0 },
+ { "rev16", 0, 6, 0 },
+ { "usad8", 0, 6, 0 },
+ { "sxtb", 0, 6, 0 },
+ { "uxtb", 0, 6, 0 },
+ { "sxth", 0, 6, 0 },
+ { "sxtb16", 0, 6, 0 },
+ { "uxth", 0, 6, 0 },
+ { "uxtb16", 0, 6, 0 },
+ { "cpy", 0, 6, 0 },
+ { "uxtab", 0, 6, 0 },
+ { "ssub8", 0, 6, 0 },
+ { "shsub8", 0, 6, 0 },
+ { "ssubaddx", 0, 6, 0 },
+ { "strex", 0, 6, 0 },
+ { "strexb", 0, 7, 0 },
+ { "swp", 0, 0, 0 },
+ { "swpb", 0, 0, 0 },
+ { "ssub16", 0, 6, 0 },
+ { "ssat16", 0, 6, 0 },
+ { "shsubaddx", 0, 6, 0 },
+ { "qsubaddx", 0, 6, 0 },
+ { "shaddsubx", 0, 6, 0 },
+ { "shadd8", 0, 6, 0 },
+ { "shadd16", 0, 6, 0 },
+ { "sel", 0, 6, 0 },
+ { "saddsubx", 0, 6, 0 },
+ { "sadd8", 0, 6, 0 },
+ { "sadd16", 0, 6, 0 },
+ { "shsub16", 0, 6, 0 },
+ { "umaal", 0, 6, 0 },
+ { "uxtab16", 0, 6, 0 },
+ { "usubaddx", 0, 6, 0 },
+ { "usub8", 0, 6, 0 },
+ { "usub16", 0, 6, 0 },
+ { "usat16", 0, 6, 0 },
+ { "usada8", 0, 6, 0 },
+ { "uqsubaddx", 0, 6, 0 },
+ { "uqsub8", 0, 6, 0 },
+ { "uqsub16", 0, 6, 0 },
+ { "uqaddsubx", 0, 6, 0 },
+ { "uqadd8", 0, 6, 0 },
+ { "uqadd16", 0, 6, 0 },
+ { "sxtab", 0, 6, 0 },
+ { "uhsubaddx", 0, 6, 0 },
+ { "uhsub8", 0, 6, 0 },
+ { "uhsub16", 0, 6, 0 },
+ { "uhaddsubx", 0, 6, 0 },
+ { "uhadd8", 0, 6, 0 },
+ { "uhadd16", 0, 6, 0 },
+ { "uaddsubx", 0, 6, 0 },
+ { "uadd8", 0, 6, 0 },
+ { "uadd16", 0, 6, 0 },
+ { "sxtah", 0, 6, 0 },
+ { "sxtab16", 0, 6, 0 },
+ { "qadd8", 0, 6, 0 },
+ { "bxj", 0, 5, 0 },
+ { "clz", 0, 3, 0 },
+ { "uxtah", 0, 6, 0 },
+ { "bx", 0, 2, 0 },
+ { "rev", 0, 6, 0 },
+ { "blx", 0, 3, 0 },
+ { "revsh", 0, 6, 0 },
+ { "qadd", 0, 4, 0 },
+ { "qadd16", 0, 6, 0 },
+ { "qaddsubx", 0, 6, 0 },
+ { "ldrex", 0, 0, 0 },
+ { "qdadd", 0, 4, 0 },
+ { "qdsub", 0, 4, 0 },
+ { "qsub", 0, 4, 0 },
+ { "ldrexb", 0, 7, 0 },
+ { "qsub8", 0, 6, 0 },
+ { "qsub16", 0, 6, 0 },
+ { "smuad", 0, 6, 0 },
+ { "smmul", 0, 6, 0 },
+ { "smusd", 0, 6, 0 },
+ { "smlsd", 0, 6, 0 },
+ { "smlsld", 0, 6, 0 },
+ { "smmla", 0, 6, 0 },
+ { "smmls", 0, 6, 0 },
+ { "smlald", 0, 6, 0 },
+ { "smlad", 0, 6, 0 },
+ { "smlaw", 0, 4, 0 },
+ { "smulw", 0, 4, 0 },
+ { "pkhtb", 0, 6, 0 },
+ { "pkhbt", 0, 6, 0 },
+ { "smul", 0, 4, 0 },
+ { "smlal", 0, 4, 0 },
+ { "smla", 0, 4, 0 },
+ { "mcrr", 0, 6, 0 },
+ { "mrrc", 0, 6, 0 },
+ { "cmp", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "tst", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "teq", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "cmn", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "smull", 0, 0, 0 },
+ { "umull", 0, 0, 0 },
+ { "umlal", 0, 0, 0 },
+ { "smlal", 0, 0, 0 },
+ { "mul", 0, 0, 0 },
+ { "mla", 0, 0, 0 },
+ { "ssat", 0, 6, 0 },
+ { "usat", 0, 6, 0 },
+ { "mrs", 0, 0, 0 },
+ { "msr", 0, 0, 0 },
+ { "and", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "bic", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "ldm", 0, 0, 0 },
+ { "eor", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "add", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "rsb", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "rsc", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "sbc", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "adc", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "sub", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "orr", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "mvn", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "mov", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
+ { "stm", 0, 0, 0 },
+ { "ldm", 0, 0, 0 },
+ { "ldrsh", 0, 2, 0 },
+ { "stm", 0, 0, 0 },
+ { "ldm", 0, 0, 0 },
+ { "ldrsb", 0, 2, 0 },
+ { "strd", 0, 4, 0 },
+ { "ldrh", 0, 0, 0 },
+ { "strh", 0, 0, 0 },
+ { "ldrd", 0, 4, 0 },
+ { "strt", 0, 0, 0 },
+ { "strbt", 0, 0, 0 },
+ { "ldrbt", 0, 0, 0 },
+ { "ldrt", 0, 0, 0 },
+ { "mrc", 0, 6, 0 },
+ { "mcr", 0, 0, 0 },
+ { "msr", 0, 0, 0 },
+ { "ldrb", 0, 0, 0 },
+ { "strb", 0, 0, 0 },
+ { "ldr", 0, 0, 0 },
+ { "ldrcond", 1, 0, 28, 31, 0x0000000e },
+ { "str", 0, 0, 0 },
+ { "cdp", 0, 0, 0 },
+ { "stc", 0, 0, 0 },
+ { "ldc", 0, 0, 0 },
+ { "swi", 0, 0, 0 },
+ { "bbl", 0, 0, 0 },
+ { "ldrexd", 0, ARMV6K, 0 },
+ { "strexd", 0, ARMV6K, 0 },
+ { "ldrexh", 0, ARMV6K, 0 },
+ { "strexh", 0, ARMV6K, 0 },
+
+ { "bl_1_thumb", 0, INVALID, 0 }, // Should be table[-4]
+ { "bl_2_thumb", 0, INVALID, 0 }, // Should be located at the end of the table[-3]
+ { "blx_1_thumb", 0, INVALID, 0 }, // Should be located at table[-2]
+ { "invalid", 0, INVALID, 0 }
+};
+
+int decode_arm_instr(uint32_t instr, int32_t *idx) {
+ int n = 0;
+ int base = 0;
+ int ret = DECODE_FAILURE;
+ int i = 0;
+ int instr_slots = sizeof(arm_instruction) / sizeof(ISEITEM);
+
+ for (i = 0; i < instr_slots; i++) {
+ n = arm_instruction[i].attribute_value;
+ base = 0;
+
+ while (n) {
+ if (arm_instruction[i].content[base + 1] == 31 && arm_instruction[i].content[base] == 0) {
+ // clrex
+ if (instr != arm_instruction[i].content[base + 2]) {
+ break;
+ }
+ } else if (BITS(instr, arm_instruction[i].content[base], arm_instruction[i].content[base + 1]) != arm_instruction[i].content[base + 2]) {
+ break;
+ }
+ base += 3;
+ n--;
+ }
+
+ // All conditions is satisfied.
+ if (n == 0)
+ ret = DECODE_SUCCESS;
+
+ if (ret == DECODE_SUCCESS) {
+ n = arm_exclusion_code[i].attribute_value;
+ if (n != 0) {
+ base = 0;
+ while (n) {
+ if (BITS(instr, arm_exclusion_code[i].content[base], arm_exclusion_code[i].content[base + 1]) != arm_exclusion_code[i].content[base + 2]) {
+ break;
+ }
+ base += 3;
+ n--;
+ }
+
+ // All conditions is satisfied.
+ if (n == 0)
+ ret = DECODE_FAILURE;
+ }
+ }
+
+ if (ret == DECODE_SUCCESS) {
+ *idx = i;
+ return ret;
+ }
+ }
+ return ret;
+}
--- /dev/null
+// Copyright 2012 Michael Kang, 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+int decode_arm_instr(uint32_t instr, int32_t *idx);
+
+enum DECODE_STATUS {
+ DECODE_SUCCESS,
+ DECODE_FAILURE
+};
+
+struct instruction_set_encoding_item {
+ const char *name;
+ int attribute_value;
+ int version;
+ u32 content[21];
+};
+
+typedef struct instruction_set_encoding_item ISEITEM;
+
+// ARM versions
+enum {
+ INVALID = 0,
+ ARMALL,
+ ARMV4,
+ ARMV4T,
+ ARMV5T,
+ ARMV5TE,
+ ARMV5TEJ,
+ ARMV6,
+ ARM1176JZF_S,
+ ARMVFP2,
+ ARMVFP3,
+ ARMV6K,
+};
+
+extern const ISEITEM arm_instruction[];
--- /dev/null
+// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+//massive fixed by ichfly XDS/3dmoo team
+
+//#define inst_debug
+#define LOGMODULE "fs"
+
+#include "Kernel.h"
+#define CITRA_IGNORE_EXIT(x)
+
+#include <algorithm>
+#include <unordered_map>
+#include <stdio.h>
+#include <assert.h>
+#include <cstdio>
+#include <vector>
+
+using namespace std;
+
+#include "Common.h"
+
+
+#include "arm/skyeye_common/armdefs.h"
+#include "arm/skyeye_common/armmmu.h"
+#include "arm_dyncom_thumb.h"
+#include "arm_dyncom_run.h"
+#include "arm/skyeye_common/vfp/vfp.h"
+#include "arm/disassembler/arm_disasm.h"
+
+#include "arm/memory.h"
+
+enum {
+ COND = (1 << 0),
+ NON_BRANCH = (1 << 1),
+ DIRECT_BRANCH = (1 << 2),
+ INDIRECT_BRANCH = (1 << 3),
+ CALL = (1 << 4),
+ RET = (1 << 5),
+ END_OF_PAGE = (1 << 6),
+ THUMB = (1 << 7)
+};
+
+
+#undef BITS
+#undef BIT
+#define BITS(s, a, b) ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1))
+#define BIT(s, n) ((s >> (n)) & 1)
+
+#define RM BITS(sht_oper, 0, 3)
+#define RS BITS(sht_oper, 8, 11)
+
+#define glue(x, y) x ## y
+#define DPO(s) glue(DataProcessingOperands, s)
+#define ROTATE_RIGHT(n, i, l) ((n << (l - i)) | (n >> i))
+#define ROTATE_LEFT(n, i, l) ((n >> (l - i)) | (n << i))
+#define ROTATE_RIGHT_32(n, i) ROTATE_RIGHT(n, i, 32)
+#define ROTATE_LEFT_32(n, i) ROTATE_LEFT(n, i, 32)
+
+#define rotr(x,n) ( (x >> n) | ((x & ((1 << (n + 1)) - 1)) << (32 - n)) )
+
+extern void switch_mode(arm_core_t *core, uint32_t mode);
+
+typedef arm_core_t arm_processor;
+typedef unsigned int(*shtop_fp_t)(arm_processor *cpu, unsigned int sht_oper);
+
+typedef unsigned int (*shtop_fp_t)(ARMul_State* cpu, unsigned int sht_oper);
+
+// Defines a reservation granule of 2 words, which protects the first 2 words starting at the tag.
+// This is the smallest granule allowed by the v7 spec, and is coincidentally just large enough to
+// support LDR/STREXD.
+static const ARMword RESERVATION_GRANULE_MASK = 0xFFFFFFF8;
+
+// Exclusive memory access
+static int exclusive_detect(ARMul_State* state, ARMword addr) {
+ if(state->exclusive_tag == (addr & RESERVATION_GRANULE_MASK))
+ return 0;
+ else
+ return -1;
+}
+
+static void add_exclusive_addr(ARMul_State* state, ARMword addr){
+ state->exclusive_tag = addr & RESERVATION_GRANULE_MASK;
+ return;
+}
+
+static void remove_exclusive(ARMul_State* state, ARMword addr){
+ state->exclusive_tag = 0xFFFFFFFF;
+}
+
+static unsigned int DPO(Immediate)(ARMul_State* cpu, unsigned int sht_oper) {
+ unsigned int immed_8 = BITS(sht_oper, 0, 7);
+ unsigned int rotate_imm = BITS(sht_oper, 8, 11);
+ unsigned int shifter_operand = ROTATE_RIGHT_32(immed_8, rotate_imm * 2);
+ if (rotate_imm == 0)
+ cpu->shifter_carry_out = cpu->CFlag;
+ else
+ cpu->shifter_carry_out = BIT(shifter_operand, 31);
+ return shifter_operand;
+}
+
+static unsigned int DPO(Register)(ARMul_State* cpu, unsigned int sht_oper) {
+ unsigned int rm = CHECK_READ_REG15(cpu, RM);
+ unsigned int shifter_operand = rm;
+ cpu->shifter_carry_out = cpu->CFlag;
+ return shifter_operand;
+}
+
+static unsigned int DPO(LogicalShiftLeftByImmediate)(ARMul_State* cpu, unsigned int sht_oper) {
+ int shift_imm = BITS(sht_oper, 7, 11);
+ unsigned int rm = CHECK_READ_REG15(cpu, RM);
+ unsigned int shifter_operand;
+ if (shift_imm == 0) {
+ shifter_operand = rm;
+ cpu->shifter_carry_out = cpu->CFlag;
+ } else {
+ shifter_operand = rm << shift_imm;
+ cpu->shifter_carry_out = BIT(rm, 32 - shift_imm);
+ }
+ return shifter_operand;
+}
+
+static unsigned int DPO(LogicalShiftLeftByRegister)(ARMul_State* cpu, unsigned int sht_oper) {
+ int shifter_operand;
+ unsigned int rm = CHECK_READ_REG15(cpu, RM);
+ unsigned int rs = CHECK_READ_REG15(cpu, RS);
+ if (BITS(rs, 0, 7) == 0) {
+ shifter_operand = rm;
+ cpu->shifter_carry_out = cpu->CFlag;
+ } else if (BITS(rs, 0, 7) < 32) {
+ shifter_operand = rm << BITS(rs, 0, 7);
+ cpu->shifter_carry_out = BIT(rm, 32 - BITS(rs, 0, 7));
+ } else if (BITS(rs, 0, 7) == 32) {
+ shifter_operand = 0;
+ cpu->shifter_carry_out = BIT(rm, 0);
+ } else {
+ shifter_operand = 0;
+ cpu->shifter_carry_out = 0;
+ }
+ return shifter_operand;
+}
+
+static unsigned int DPO(LogicalShiftRightByImmediate)(ARMul_State* cpu, unsigned int sht_oper) {
+ unsigned int rm = CHECK_READ_REG15(cpu, RM);
+ unsigned int shifter_operand;
+ int shift_imm = BITS(sht_oper, 7, 11);
+ if (shift_imm == 0) {
+ shifter_operand = 0;
+ cpu->shifter_carry_out = BIT(rm, 31);
+ } else {
+ shifter_operand = rm >> shift_imm;
+ cpu->shifter_carry_out = BIT(rm, shift_imm - 1);
+ }
+ return shifter_operand;
+}
+
+static unsigned int DPO(LogicalShiftRightByRegister)(ARMul_State* cpu, unsigned int sht_oper) {
+ unsigned int rs = CHECK_READ_REG15(cpu, RS);
+ unsigned int rm = CHECK_READ_REG15(cpu, RM);
+ unsigned int shifter_operand;
+ if (BITS(rs, 0, 7) == 0) {
+ shifter_operand = rm;
+ cpu->shifter_carry_out = cpu->CFlag;
+ } else if (BITS(rs, 0, 7) < 32) {
+ shifter_operand = rm >> BITS(rs, 0, 7);
+ cpu->shifter_carry_out = BIT(rm, BITS(rs, 0, 7) - 1);
+ } else if (BITS(rs, 0, 7) == 32) {
+ shifter_operand = 0;
+ cpu->shifter_carry_out = BIT(rm, 31);
+ } else {
+ shifter_operand = 0;
+ cpu->shifter_carry_out = 0;
+ }
+ return shifter_operand;
+}
+
+static unsigned int DPO(ArithmeticShiftRightByImmediate)(ARMul_State* cpu, unsigned int sht_oper) {
+ unsigned int rm = CHECK_READ_REG15(cpu, RM);
+ unsigned int shifter_operand;
+ int shift_imm = BITS(sht_oper, 7, 11);
+ if (shift_imm == 0) {
+ if (BIT(rm, 31) == 0)
+ shifter_operand = 0;
+ else
+ shifter_operand = 0xFFFFFFFF;
+ cpu->shifter_carry_out = BIT(rm, 31);
+ } else {
+ shifter_operand = static_cast<int>(rm) >> shift_imm;
+ cpu->shifter_carry_out = BIT(rm, shift_imm - 1);
+ }
+ return shifter_operand;
+}
+
+static unsigned int DPO(ArithmeticShiftRightByRegister)(ARMul_State* cpu, unsigned int sht_oper) {
+ unsigned int rs = CHECK_READ_REG15(cpu, RS);
+ unsigned int rm = CHECK_READ_REG15(cpu, RM);
+ unsigned int shifter_operand;
+ if (BITS(rs, 0, 7) == 0) {
+ shifter_operand = rm;
+ cpu->shifter_carry_out = cpu->CFlag;
+ } else if (BITS(rs, 0, 7) < 32) {
+ shifter_operand = static_cast<int>(rm) >> BITS(rs, 0, 7);
+ cpu->shifter_carry_out = BIT(rm, BITS(rs, 0, 7) - 1);
+ } else {
+ if (BIT(rm, 31) == 0)
+ shifter_operand = 0;
+ else
+ shifter_operand = 0xffffffff;
+ cpu->shifter_carry_out = BIT(rm, 31);
+ }
+ return shifter_operand;
+}
+
+static unsigned int DPO(RotateRightByImmediate)(ARMul_State* cpu, unsigned int sht_oper) {
+ unsigned int shifter_operand;
+ unsigned int rm = CHECK_READ_REG15(cpu, RM);
+ int shift_imm = BITS(sht_oper, 7, 11);
+ if (shift_imm == 0) {
+ shifter_operand = (cpu->CFlag << 31) | (rm >> 1);
+ cpu->shifter_carry_out = BIT(rm, 0);
+ } else {
+ shifter_operand = ROTATE_RIGHT_32(rm, shift_imm);
+ cpu->shifter_carry_out = BIT(rm, shift_imm - 1);
+ }
+ return shifter_operand;
+}
+
+static unsigned int DPO(RotateRightByRegister)(ARMul_State* cpu, unsigned int sht_oper) {
+ unsigned int rm = CHECK_READ_REG15(cpu, RM);
+ unsigned int rs = CHECK_READ_REG15(cpu, RS);
+ unsigned int shifter_operand;
+ if (BITS(rs, 0, 7) == 0) {
+ shifter_operand = rm;
+ cpu->shifter_carry_out = cpu->CFlag;
+ } else if (BITS(rs, 0, 4) == 0) {
+ shifter_operand = rm;
+ cpu->shifter_carry_out = BIT(rm, 31);
+ } else {
+ shifter_operand = ROTATE_RIGHT_32(rm, BITS(rs, 0, 4));
+ cpu->shifter_carry_out = BIT(rm, BITS(rs, 0, 4) - 1);
+ }
+ return shifter_operand;
+}
+
+typedef void (*get_addr_fp_t)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr, unsigned int rw);
+
+typedef struct _ldst_inst {
+ unsigned int inst;
+ get_addr_fp_t get_addr;
+} ldst_inst;
+#define DEBUG_MSG LOG("inst is %x", inst); CITRA_IGNORE_EXIT(0)
+
+int CondPassed(ARMul_State* cpu, unsigned int cond);
+
+#define LnSWoUB(s) glue(LnSWoUB, s)
+#define MLnS(s) glue(MLnS, s)
+#define LdnStM(s) glue(LdnStM, s)
+
+#define W_BIT BIT(inst, 21)
+#define U_BIT BIT(inst, 23)
+#define I_BIT BIT(inst, 25)
+#define P_BIT BIT(inst, 24)
+#define OFFSET_12 BITS(inst, 0, 11)
+
+static void LnSWoUB(ImmediateOffset)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int addr;
+
+ if (U_BIT)
+ addr = CHECK_READ_REG15_WA(cpu, Rn) + OFFSET_12;
+ else
+ addr = CHECK_READ_REG15_WA(cpu, Rn) - OFFSET_12;
+
+ virt_addr = addr;
+}
+
+static void LnSWoUB(RegisterOffset)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int Rm = BITS(inst, 0, 3);
+ unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
+ unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
+ unsigned int addr;
+
+ if (U_BIT)
+ addr = rn + rm;
+ else
+ addr = rn - rm;
+
+ virt_addr = addr;
+}
+
+static void LnSWoUB(ImmediatePostIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int addr = CHECK_READ_REG15_WA(cpu, Rn);
+
+ if (U_BIT)
+ cpu->Reg[Rn] += OFFSET_12;
+ else
+ cpu->Reg[Rn] -= OFFSET_12;
+
+ virt_addr = addr;
+}
+
+static void LnSWoUB(ImmediatePreIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int addr;
+
+ if (U_BIT)
+ addr = CHECK_READ_REG15_WA(cpu, Rn) + OFFSET_12;
+ else
+ addr = CHECK_READ_REG15_WA(cpu, Rn) - OFFSET_12;
+
+ virt_addr = addr;
+
+ if (CondPassed(cpu, BITS(inst, 28, 31)))
+ cpu->Reg[Rn] = addr;
+}
+
+static void MLnS(RegisterPreIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int addr;
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int Rm = BITS(inst, 0, 3);
+ unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
+ unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
+
+ if (U_BIT)
+ addr = rn + rm;
+ else
+ addr = rn - rm;
+
+ virt_addr = addr;
+
+ if (CondPassed(cpu, BITS(inst, 28, 31)))
+ cpu->Reg[Rn] = addr;
+}
+
+static void LnSWoUB(RegisterPreIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int Rm = BITS(inst, 0, 3);
+ unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
+ unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
+ unsigned int addr;
+
+ if (U_BIT)
+ addr = rn + rm;
+ else
+ addr = rn - rm;
+
+ virt_addr = addr;
+
+ if (CondPassed(cpu, BITS(inst, 28, 31))) {
+ cpu->Reg[Rn] = addr;
+ }
+}
+
+static void LnSWoUB(ScaledRegisterPreIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int shift = BITS(inst, 5, 6);
+ unsigned int shift_imm = BITS(inst, 7, 11);
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int Rm = BITS(inst, 0, 3);
+ unsigned int index;
+ unsigned int addr;
+ unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
+ unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
+
+ switch (shift) {
+ case 0:
+ index = rm << shift_imm;
+ break;
+ case 1:
+ if (shift_imm == 0) {
+ index = 0;
+ } else {
+ index = rm >> shift_imm;
+ }
+ break;
+ case 2:
+ if (shift_imm == 0) { // ASR #32
+ if (BIT(rm, 31) == 1)
+ index = 0xFFFFFFFF;
+ else
+ index = 0;
+ } else {
+ index = static_cast<int>(rm) >> shift_imm;
+ }
+ break;
+ case 3:
+ if (shift_imm == 0) {
+ index = (cpu->CFlag << 31) | (rm >> 1);
+ } else {
+ index = ROTATE_RIGHT_32(rm, shift_imm);
+ }
+ break;
+ }
+
+ if (U_BIT)
+ addr = rn + index;
+ else
+ addr = rn - index;
+
+ virt_addr = addr;
+
+ if (CondPassed(cpu, BITS(inst, 28, 31)))
+ cpu->Reg[Rn] = addr;
+}
+
+static void LnSWoUB(ScaledRegisterPostIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int shift = BITS(inst, 5, 6);
+ unsigned int shift_imm = BITS(inst, 7, 11);
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int Rm = BITS(inst, 0, 3);
+ unsigned int index;
+ unsigned int addr = CHECK_READ_REG15_WA(cpu, Rn);
+ unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
+
+ switch (shift) {
+ case 0:
+ index = rm << shift_imm;
+ break;
+ case 1:
+ if (shift_imm == 0) {
+ index = 0;
+ } else {
+ index = rm >> shift_imm;
+ }
+ break;
+ case 2:
+ if (shift_imm == 0) { // ASR #32
+ if (BIT(rm, 31) == 1)
+ index = 0xFFFFFFFF;
+ else
+ index = 0;
+ } else {
+ index = static_cast<int>(rm) >> shift_imm;
+ }
+ break;
+ case 3:
+ if (shift_imm == 0) {
+ index = (cpu->CFlag << 31) | (rm >> 1);
+ } else {
+ index = ROTATE_RIGHT_32(rm, shift_imm);
+ }
+ break;
+ }
+
+ virt_addr = addr;
+
+ if (CondPassed(cpu, BITS(inst, 28, 31))) {
+ if (U_BIT)
+ cpu->Reg[Rn] += index;
+ else
+ cpu->Reg[Rn] -= index;
+ }
+}
+
+static void LnSWoUB(RegisterPostIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int Rm = BITS(inst, 0, 3);
+ unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
+
+ virt_addr = CHECK_READ_REG15_WA(cpu, Rn);
+
+ if (CondPassed(cpu, BITS(inst, 28, 31))) {
+ if (U_BIT) {
+ cpu->Reg[Rn] += rm;
+ } else {
+ cpu->Reg[Rn] -= rm;
+ }
+ }
+}
+
+static void MLnS(ImmediateOffset)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int immedL = BITS(inst, 0, 3);
+ unsigned int immedH = BITS(inst, 8, 11);
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int addr;
+
+ unsigned int offset_8 = (immedH << 4) | immedL;
+
+ if (U_BIT)
+ addr = CHECK_READ_REG15_WA(cpu, Rn) + offset_8;
+ else
+ addr = CHECK_READ_REG15_WA(cpu, Rn) - offset_8;
+
+ virt_addr = addr;
+}
+
+static void MLnS(RegisterOffset)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int addr;
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int Rm = BITS(inst, 0, 3);
+ unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
+ unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
+
+ if (U_BIT)
+ addr = rn + rm;
+ else
+ addr = rn - rm;
+
+ virt_addr = addr;
+}
+
+static void MLnS(ImmediatePreIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int immedH = BITS(inst, 8, 11);
+ unsigned int immedL = BITS(inst, 0, 3);
+ unsigned int addr;
+ unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
+ unsigned int offset_8 = (immedH << 4) | immedL;
+
+ if (U_BIT)
+ addr = rn + offset_8;
+ else
+ addr = rn - offset_8;
+
+ virt_addr = addr;
+
+ if (CondPassed(cpu, BITS(inst, 28, 31)))
+ cpu->Reg[Rn] = addr;
+}
+
+static void MLnS(ImmediatePostIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int immedH = BITS(inst, 8, 11);
+ unsigned int immedL = BITS(inst, 0, 3);
+ unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
+
+ virt_addr = rn;
+
+ if (CondPassed(cpu, BITS(inst, 28, 31))) {
+ unsigned int offset_8 = (immedH << 4) | immedL;
+ if (U_BIT)
+ rn += offset_8;
+ else
+ rn -= offset_8;
+
+ cpu->Reg[Rn] = rn;
+ }
+}
+
+static void MLnS(RegisterPostIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int Rm = BITS(inst, 0, 3);
+ unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
+
+ virt_addr = CHECK_READ_REG15_WA(cpu, Rn);
+
+ if (CondPassed(cpu, BITS(inst, 28, 31))) {
+ if (U_BIT)
+ cpu->Reg[Rn] += rm;
+ else
+ cpu->Reg[Rn] -= rm;
+ }
+}
+
+static void LdnStM(DecrementBefore)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int i = BITS(inst, 0, 15);
+ int count = 0;
+
+ while (i) {
+ if (i & 1) count++;
+ i = i >> 1;
+ }
+
+ virt_addr = CHECK_READ_REG15_WA(cpu, Rn) - count * 4;
+
+ if (CondPassed(cpu, BITS(inst, 28, 31)) && BIT(inst, 21))
+ cpu->Reg[Rn] -= count * 4;
+}
+
+static void LdnStM(IncrementBefore)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int i = BITS(inst, 0, 15);
+ int count = 0;
+
+ while (i) {
+ if (i & 1) count++;
+ i = i >> 1;
+ }
+
+ virt_addr = CHECK_READ_REG15_WA(cpu, Rn) + 4;
+
+ if (CondPassed(cpu, BITS(inst, 28, 31)) && BIT(inst, 21))
+ cpu->Reg[Rn] += count * 4;
+}
+
+static void LdnStM(IncrementAfter)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int i = BITS(inst, 0, 15);
+ int count = 0;
+
+ while(i) {
+ if (i & 1) count++;
+ i = i >> 1;
+ }
+
+ virt_addr = CHECK_READ_REG15_WA(cpu, Rn);
+
+ if (CondPassed(cpu, BITS(inst, 28, 31)) && BIT(inst, 21))
+ cpu->Reg[Rn] += count * 4;
+}
+
+static void LdnStM(DecrementAfter)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int i = BITS(inst, 0, 15);
+ int count = 0;
+ while(i) {
+ if(i & 1) count++;
+ i = i >> 1;
+ }
+ unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
+ unsigned int start_addr = rn - count * 4 + 4;
+
+ virt_addr = start_addr;
+
+ if (CondPassed(cpu, BITS(inst, 28, 31)) && BIT(inst, 21)) {
+ cpu->Reg[Rn] -= count * 4;
+ }
+}
+
+static void LnSWoUB(ScaledRegisterOffset)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr, unsigned int rw) {
+ unsigned int shift = BITS(inst, 5, 6);
+ unsigned int shift_imm = BITS(inst, 7, 11);
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int Rm = BITS(inst, 0, 3);
+ unsigned int index;
+ unsigned int addr;
+ unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
+ unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
+
+ switch (shift) {
+ case 0:
+ index = rm << shift_imm;
+ break;
+ case 1:
+ if (shift_imm == 0) {
+ index = 0;
+ } else {
+ index = rm >> shift_imm;
+ }
+ break;
+ case 2:
+ if (shift_imm == 0) { // ASR #32
+ if (BIT(rm, 31) == 1)
+ index = 0xFFFFFFFF;
+ else
+ index = 0;
+ } else {
+ index = static_cast<int>(rm) >> shift_imm;
+ }
+ break;
+ case 3:
+ if (shift_imm == 0) {
+ index = (cpu->CFlag << 31) | (rm >> 1);
+ } else {
+ index = ROTATE_RIGHT_32(rm, shift_imm);
+ }
+ break;
+ }
+
+ if (U_BIT) {
+ addr = rn + index;
+ } else
+ addr = rn - index;
+
+ virt_addr = addr;
+}
+
+typedef struct _arm_inst {
+ unsigned int idx;
+ unsigned int cond;
+ int br;
+ int load_r15;
+ char component[0];
+} arm_inst;
+
+typedef struct generic_arm_inst {
+ u32 Ra;
+ u32 Rm;
+ u32 Rn;
+ u32 Rd;
+ u8 op1;
+ u8 op2;
+} generic_arm_inst;
+
+typedef struct _adc_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} adc_inst;
+
+typedef struct _add_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} add_inst;
+
+typedef struct _orr_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} orr_inst;
+
+typedef struct _and_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} and_inst;
+
+typedef struct _eor_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} eor_inst;
+
+typedef struct _bbl_inst {
+ unsigned int L;
+ int signed_immed_24;
+ unsigned int next_addr;
+ unsigned int jmp_addr;
+} bbl_inst;
+
+typedef struct _bx_inst {
+ unsigned int Rm;
+} bx_inst;
+
+typedef struct _blx_inst {
+ union {
+ int32_t signed_immed_24;
+ uint32_t Rm;
+ } val;
+ unsigned int inst;
+} blx_inst;
+
+typedef struct _clz_inst {
+ unsigned int Rm;
+ unsigned int Rd;
+} clz_inst;
+
+typedef struct _cps_inst {
+ unsigned int imod0;
+ unsigned int imod1;
+ unsigned int mmod;
+ unsigned int A, I, F;
+ unsigned int mode;
+} cps_inst;
+
+typedef struct _clrex_inst {
+} clrex_inst;
+
+typedef struct _cpy_inst {
+ unsigned int Rm;
+ unsigned int Rd;
+} cpy_inst;
+
+typedef struct _bic_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} bic_inst;
+
+typedef struct _sub_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} sub_inst;
+
+typedef struct _tst_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} tst_inst;
+
+typedef struct _cmn_inst {
+ unsigned int I;
+ unsigned int Rn;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} cmn_inst;
+
+typedef struct _teq_inst {
+ unsigned int I;
+ unsigned int Rn;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} teq_inst;
+
+typedef struct _stm_inst {
+ unsigned int inst;
+} stm_inst;
+
+struct bkpt_inst {
+ u32 imm;
+};
+
+struct blx1_inst {
+ unsigned int addr;
+};
+
+struct blx2_inst {
+ unsigned int Rm;
+};
+
+typedef struct _stc_inst {
+} stc_inst;
+
+typedef struct _ldc_inst {
+} ldc_inst;
+
+typedef struct _swi_inst {
+ unsigned int num;
+} swi_inst;
+
+typedef struct _cmp_inst {
+ unsigned int I;
+ unsigned int Rn;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} cmp_inst;
+
+typedef struct _mov_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} mov_inst;
+
+typedef struct _mvn_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} mvn_inst;
+
+typedef struct _rev_inst {
+ unsigned int Rd;
+ unsigned int Rm;
+ unsigned int op1;
+ unsigned int op2;
+} rev_inst;
+
+typedef struct _rsb_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} rsb_inst;
+
+typedef struct _rsc_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} rsc_inst;
+
+typedef struct _sbc_inst {
+ unsigned int I;
+ unsigned int S;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int shifter_operand;
+ shtop_fp_t shtop_func;
+} sbc_inst;
+
+typedef struct _mul_inst {
+ unsigned int S;
+ unsigned int Rd;
+ unsigned int Rs;
+ unsigned int Rm;
+} mul_inst;
+
+typedef struct _smul_inst {
+ unsigned int Rd;
+ unsigned int Rs;
+ unsigned int Rm;
+ unsigned int x;
+ unsigned int y;
+} smul_inst;
+
+typedef struct _umull_inst {
+ unsigned int S;
+ unsigned int RdHi;
+ unsigned int RdLo;
+ unsigned int Rs;
+ unsigned int Rm;
+} umull_inst;
+typedef struct _smlad_inst {
+ unsigned int m;
+ unsigned int Rm;
+ unsigned int Rd;
+ unsigned int Ra;
+ unsigned int Rn;
+ unsigned int op1;
+ unsigned int op2;
+} smlad_inst;
+
+typedef struct _smla_inst {
+ unsigned int x;
+ unsigned int y;
+ unsigned int Rm;
+ unsigned int Rd;
+ unsigned int Rs;
+ unsigned int Rn;
+} smla_inst;
+
+typedef struct smlalxy_inst {
+ unsigned int x;
+ unsigned int y;
+ unsigned int RdLo;
+ unsigned int RdHi;
+ unsigned int Rm;
+ unsigned int Rn;
+} smlalxy_inst;
+
+typedef struct ssat_inst {
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int imm5;
+ unsigned int sat_imm;
+ unsigned int shift_type;
+} ssat_inst;
+
+typedef struct umaal_inst {
+ unsigned int Rn;
+ unsigned int Rm;
+ unsigned int RdHi;
+ unsigned int RdLo;
+} umaal_inst;
+
+typedef struct _umlal_inst {
+ unsigned int S;
+ unsigned int Rm;
+ unsigned int Rs;
+ unsigned int RdHi;
+ unsigned int RdLo;
+} umlal_inst;
+
+typedef struct _smlal_inst {
+ unsigned int S;
+ unsigned int Rm;
+ unsigned int Rs;
+ unsigned int RdHi;
+ unsigned int RdLo;
+} smlal_inst;
+
+typedef struct smlald_inst {
+ unsigned int RdLo;
+ unsigned int RdHi;
+ unsigned int Rm;
+ unsigned int Rn;
+ unsigned int swap;
+ unsigned int op1;
+ unsigned int op2;
+} smlald_inst;
+
+typedef struct _mla_inst {
+ unsigned int S;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int Rs;
+ unsigned int Rm;
+} mla_inst;
+
+typedef struct _mrc_inst {
+ unsigned int opcode_1;
+ unsigned int opcode_2;
+ unsigned int cp_num;
+ unsigned int crn;
+ unsigned int crm;
+ unsigned int Rd;
+ unsigned int inst;
+} mrc_inst;
+
+typedef struct _mcr_inst {
+ unsigned int opcode_1;
+ unsigned int opcode_2;
+ unsigned int cp_num;
+ unsigned int crn;
+ unsigned int crm;
+ unsigned int Rd;
+ unsigned int inst;
+} mcr_inst;
+
+typedef struct _mrs_inst {
+ unsigned int R;
+ unsigned int Rd;
+} mrs_inst;
+
+typedef struct _msr_inst {
+ unsigned int field_mask;
+ unsigned int R;
+ unsigned int inst;
+} msr_inst;
+
+typedef struct _pld_inst {
+} pld_inst;
+
+typedef struct _sxtb_inst {
+ unsigned int Rd;
+ unsigned int Rm;
+ unsigned int rotate;
+} sxtb_inst;
+
+typedef struct _sxtab_inst {
+ unsigned int Rd;
+ unsigned int Rn;
+ unsigned int Rm;
+ unsigned rotate;
+} sxtab_inst;
+
+typedef struct _sxtah_inst {
+ unsigned int Rd;
+ unsigned int Rn;
+ unsigned int Rm;
+ unsigned int rotate;
+} sxtah_inst;
+
+typedef struct _sxth_inst {
+ unsigned int Rd;
+ unsigned int Rm;
+ unsigned int rotate;
+} sxth_inst;
+
+typedef struct _uxtab_inst {
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int rotate;
+ unsigned int Rm;
+} uxtab_inst;
+
+typedef struct _uxtah_inst {
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int rotate;
+ unsigned int Rm;
+} uxtah_inst;
+
+typedef struct _uxth_inst {
+ unsigned int Rd;
+ unsigned int Rm;
+ unsigned int rotate;
+} uxth_inst;
+
+typedef struct _cdp_inst {
+ unsigned int opcode_1;
+ unsigned int CRn;
+ unsigned int CRd;
+ unsigned int cp_num;
+ unsigned int opcode_2;
+ unsigned int CRm;
+ unsigned int inst;
+}cdp_inst;
+
+typedef struct _uxtb_inst {
+ unsigned int Rd;
+ unsigned int Rm;
+ unsigned int rotate;
+} uxtb_inst;
+
+typedef struct _swp_inst {
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned int Rm;
+} swp_inst;
+
+typedef struct setend_inst {
+ unsigned int set_bigend;
+} setend_inst;
+
+typedef struct _b_2_thumb {
+ unsigned int imm;
+}b_2_thumb;
+typedef struct _b_cond_thumb {
+ unsigned int imm;
+ unsigned int cond;
+}b_cond_thumb;
+
+typedef struct _bl_1_thumb {
+ unsigned int imm;
+}bl_1_thumb;
+typedef struct _bl_2_thumb {
+ unsigned int imm;
+}bl_2_thumb;
+typedef struct _blx_1_thumb {
+ unsigned int imm;
+ unsigned int instr;
+}blx_1_thumb;
+
+typedef struct _pkh_inst {
+ unsigned int Rm;
+ unsigned int Rn;
+ unsigned int Rd;
+ unsigned char imm;
+} pkh_inst;
+
+typedef arm_inst * ARM_INST_PTR;
+
+inline void *AllocBuffer(ARMul_State* cpu,unsigned int size) {
+ int start = cpu->inst_bufftop;
+ cpu->inst_bufftop += size;
+ if (cpu->inst_bufftop > cpu->inst_buffsize) {
+ XDSERROR("inst_buf is full");
+ CITRA_IGNORE_EXIT(-1);
+ }
+ return (void *)&cpu->inst_buf[start];
+}
+
+int CondPassed(ARMul_State* cpu, unsigned int cond) {
+ #define NFLAG cpu->NFlag
+ #define ZFLAG cpu->ZFlag
+ #define CFLAG cpu->CFlag
+ #define VFLAG cpu->VFlag
+
+ int temp;
+
+ switch (cond) {
+ case 0x0:
+ temp = ZFLAG;
+ break;
+ case 0x1: // NE
+ temp = !ZFLAG;
+ break;
+ case 0x6: // VS
+ temp = VFLAG;
+ break;
+ case 0x7: // VC
+ temp = !VFLAG;
+ break;
+ case 0x4: // MI
+ temp = NFLAG;
+ break;
+ case 0x5: // PL
+ temp = !NFLAG;
+ break;
+ case 0x2: // CS
+ temp = CFLAG;
+ break;
+ case 0x3: // CC
+ temp = !CFLAG;
+ break;
+ case 0x8: // HI
+ temp = (CFLAG && !ZFLAG);
+ break;
+ case 0x9: // LS
+ temp = (!CFLAG || ZFLAG);
+ break;
+ case 0xa: // GE
+ temp = ((!NFLAG && !VFLAG) || (NFLAG && VFLAG));
+ break;
+ case 0xb: // LT
+ temp = ((NFLAG && !VFLAG) || (!NFLAG && VFLAG));
+ break;
+ case 0xc: // GT
+ temp = ((!NFLAG && !VFLAG && !ZFLAG) || (NFLAG && VFLAG && !ZFLAG));
+ break;
+ case 0xd: // LE
+ temp = ((NFLAG && !VFLAG) || (!NFLAG && VFLAG)) || ZFLAG;
+ break;
+ case 0xe: // AL
+ temp = 1;
+ break;
+ case 0xf:
+ temp = 1;
+ break;
+ }
+ return temp;
+}
+
+enum DECODE_STATUS {
+ DECODE_SUCCESS,
+ DECODE_FAILURE
+};
+
+int decode_arm_instr(uint32_t instr, int32_t *idx);
+
+static shtop_fp_t get_shtop(unsigned int inst) {
+ if (BIT(inst, 25)) {
+ return DPO(Immediate);
+ } else if (BITS(inst, 4, 11) == 0) {
+ return DPO(Register);
+ } else if (BITS(inst, 4, 6) == 0) {
+ return DPO(LogicalShiftLeftByImmediate);
+ } else if (BITS(inst, 4, 7) == 1) {
+ return DPO(LogicalShiftLeftByRegister);
+ } else if (BITS(inst, 4, 6) == 2) {
+ return DPO(LogicalShiftRightByImmediate);
+ } else if (BITS(inst, 4, 7) == 3) {
+ return DPO(LogicalShiftRightByRegister);
+ } else if (BITS(inst, 4, 6) == 4) {
+ return DPO(ArithmeticShiftRightByImmediate);
+ } else if (BITS(inst, 4, 7) == 5) {
+ return DPO(ArithmeticShiftRightByRegister);
+ } else if (BITS(inst, 4, 6) == 6) {
+ return DPO(RotateRightByImmediate);
+ } else if (BITS(inst, 4, 7) == 7) {
+ return DPO(RotateRightByRegister);
+ }
+ return nullptr;
+}
+
+static get_addr_fp_t get_calc_addr_op(unsigned int inst) {
+ if (BITS(inst, 24, 27) == 5 && BIT(inst, 21) == 0) {
+ return LnSWoUB(ImmediateOffset);
+ } else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 0 && BITS(inst, 4, 11) == 0) {
+ return LnSWoUB(RegisterOffset);
+ } else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 0 && BIT(inst, 4) == 0) {
+ return LnSWoUB(ScaledRegisterOffset);
+ } else if (BITS(inst, 24, 27) == 5 && BIT(inst, 21) == 1) {
+ return LnSWoUB(ImmediatePreIndexed);
+ } else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 1 && BITS(inst, 4, 11) == 0) {
+ return LnSWoUB(RegisterPreIndexed);
+ } else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 1 && BIT(inst, 4) == 0) {
+ return LnSWoUB(ScaledRegisterPreIndexed);
+ } else if (BITS(inst, 24, 27) == 4 && BIT(inst, 21) == 0) {
+ return LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 24, 27) == 6 && BIT(inst, 21) == 0 && BITS(inst, 4, 11) == 0) {
+ return LnSWoUB(RegisterPostIndexed);
+ } else if (BITS(inst, 24, 27) == 6 && BIT(inst, 21) == 0 && BIT(inst, 4) == 0) {
+ return LnSWoUB(ScaledRegisterPostIndexed);
+ } else if (BITS(inst, 24, 27) == 1 && BITS(inst, 21, 22) == 2 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
+ return MLnS(ImmediateOffset);
+ } else if (BITS(inst, 24, 27) == 1 && BITS(inst, 21, 22) == 0 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
+ return MLnS(RegisterOffset);
+ } else if (BITS(inst, 24, 27) == 1 && BITS(inst, 21, 22) == 3 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
+ return MLnS(ImmediatePreIndexed);
+ } else if (BITS(inst, 24, 27) == 1 && BITS(inst, 21, 22) == 1 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
+ return MLnS(RegisterPreIndexed);
+ } else if (BITS(inst, 24, 27) == 0 && BITS(inst, 21, 22) == 2 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
+ return MLnS(ImmediatePostIndexed);
+ } else if (BITS(inst, 24, 27) == 0 && BITS(inst, 21, 22) == 0 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
+ return MLnS(RegisterPostIndexed);
+ } else if (BITS(inst, 23, 27) == 0x11) {
+ return LdnStM(IncrementAfter);
+ } else if (BITS(inst, 23, 27) == 0x13) {
+ return LdnStM(IncrementBefore);
+ } else if (BITS(inst, 23, 27) == 0x10) {
+ return LdnStM(DecrementAfter);
+ } else if (BITS(inst, 23, 27) == 0x12) {
+ return LdnStM(DecrementBefore);
+ }
+ return nullptr;
+}
+
+#define INTERPRETER_TRANSLATE(s) glue(InterpreterTranslate_, s)
+
+#define CHECK_RN (inst_cream->Rn == 15)
+#define CHECK_RM (inst_cream->Rm == 15)
+#define CHECK_RS (inst_cream->Rs == 15)
+
+#define UNIMPLEMENTED_INSTRUCTION(mnemonic) \
+ XDSERROR("unimplemented instruction: %s", mnemonic); \
+ CITRA_IGNORE_EXIT(-1); \
+ return nullptr;
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(adc)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(adc_inst));
+ adc_inst *inst_cream = (adc_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(add)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(add_inst));
+ add_inst *inst_cream = (add_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(and)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(and_inst));
+ and_inst *inst_cream = (and_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+ if (inst_cream->Rd == 15)
+ inst_base->br = INDIRECT_BRANCH;
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(bbl)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ #define POSBRANCH ((inst & 0x7fffff) << 2)
+ #define NEGBRANCH ((0xff000000 |(inst & 0xffffff)) << 2)
+
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(bbl_inst));
+ bbl_inst *inst_cream = (bbl_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = DIRECT_BRANCH;
+
+ if (BIT(inst, 24))
+ inst_base->br = CALL;
+ if (BITS(inst, 28, 31) <= 0xe)
+ inst_base->br |= COND;
+
+ inst_cream->L = BIT(inst, 24);
+ inst_cream->signed_immed_24 = BIT(inst, 23) ? NEGBRANCH : POSBRANCH;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(bic)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(bic_inst));
+ bic_inst *inst_cream = (bic_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+
+ if (inst_cream->Rd == 15)
+ inst_base->br = INDIRECT_BRANCH;
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(bkpt)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(bkpt_inst));
+ bkpt_inst* const inst_cream = (bkpt_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->imm = BITS(inst, 8, 19) | BITS(inst, 0, 3);
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(blx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(blx_inst));
+ blx_inst *inst_cream = (blx_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = INDIRECT_BRANCH;
+
+ inst_cream->inst = inst;
+ if (BITS(inst, 20, 27) == 0x12 && BITS(inst, 4, 7) == 0x3) {
+ inst_cream->val.Rm = BITS(inst, 0, 3);
+ } else {
+ inst_cream->val.signed_immed_24 = BITS(inst, 0, 23);
+ }
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(bx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(bx_inst));
+ bx_inst *inst_cream = (bx_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = INDIRECT_BRANCH;
+
+ inst_cream->Rm = BITS(inst, 0, 3);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(bxj)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(bx)(cpu,inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(ARMul_State* cpu,unsigned int inst, int index) {
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(cdp_inst));
+ cdp_inst *inst_cream = (cdp_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->CRm = BITS(inst, 0, 3);
+ inst_cream->CRd = BITS(inst, 12, 15);
+ inst_cream->CRn = BITS(inst, 16, 19);
+ inst_cream->cp_num = BITS(inst, 8, 11);
+ inst_cream->opcode_2 = BITS(inst, 5, 7);
+ inst_cream->opcode_1 = BITS(inst, 20, 23);
+ inst_cream->inst = inst;
+
+ LOG("inst %x index %x", inst, index);
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(clrex)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(clrex_inst));
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(clz)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(clz_inst));
+ clz_inst *inst_cream = (clz_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ if (CHECK_RM)
+ inst_base->load_r15 = 1;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(cmn)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(cmn_inst));
+ cmn_inst *inst_cream = (cmn_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->Rn = BITS(inst, 16, 19);
+
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(cmp)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(cmp_inst));
+ cmp_inst *inst_cream = (cmp_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(cps)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(cps_inst));
+ cps_inst *inst_cream = (cps_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->imod0 = BIT(inst, 18);
+ inst_cream->imod1 = BIT(inst, 19);
+ inst_cream->mmod = BIT(inst, 17);
+ inst_cream->A = BIT(inst, 8);
+ inst_cream->I = BIT(inst, 7);
+ inst_cream->F = BIT(inst, 6);
+ inst_cream->mode = BITS(inst, 0, 4);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(cpy)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(mov_inst));
+ mov_inst *inst_cream = (mov_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(eor)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(eor_inst));
+ eor_inst *inst_cream = (eor_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldc)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldc_inst));
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldm)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ if (BIT(inst, 15)) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(sxth)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(sxtb_inst));
+ sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->rotate = BITS(inst, 10, 11);
+ if (CHECK_RM)
+ inst_base->load_r15 = 1;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldr)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrcond)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uxth)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(uxth_inst));
+ uxth_inst *inst_cream = (uxth_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->rotate = BITS(inst, 10, 11);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ if (CHECK_RM)
+ inst_base->load_r15 = 1;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtah)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(uxtah_inst));
+ uxtah_inst *inst_cream = (uxtah_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->rotate = BITS(inst, 10, 11);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ if (CHECK_RM || CHECK_RN)
+ inst_base->load_r15 = 1;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrb)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrbt)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ if (BITS(inst, 25, 27) == 2) {
+ inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 25, 27) == 3) {
+ inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed);
+ } else {
+ DEBUG_MSG;
+ }
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrd)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrex)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst *inst_cream = (generic_arm_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = (BITS(inst, 12, 15) == 15) ? INDIRECT_BRANCH : NON_BRANCH; // Branch if dest is R15
+
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrexb)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(ldrex)(cpu,inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrexh)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(ldrex)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrexd)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(ldrex)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrh)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsb)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsh)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrt)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ if (BITS(inst, 25, 27) == 2) {
+ inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 25, 27) == 3) {
+ inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed);
+ } else {
+ // Reaching this would indicate the thumb version
+ // of this instruction, however the 3DS CPU doesn't
+ // support this variant (the 3DS CPU is only ARMv6K,
+ // while this variant is added in ARMv6T2).
+ // So it's sufficient for citra to not implement this.
+ DEBUG_MSG;
+ }
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(mcr)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(mcr_inst));
+ mcr_inst *inst_cream = (mcr_inst *)inst_base->component;
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->crn = BITS(inst, 16, 19);
+ inst_cream->crm = BITS(inst, 0, 3);
+ inst_cream->opcode_1 = BITS(inst, 21, 23);
+ inst_cream->opcode_2 = BITS(inst, 5, 7);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->cp_num = BITS(inst, 8, 11);
+ inst_cream->inst = inst;
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(ARMul_State* cpu,unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("MCRR"); }
+static ARM_INST_PTR INTERPRETER_TRANSLATE(mla)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(mla_inst));
+ mla_inst *inst_cream = (mla_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rn = BITS(inst, 12, 15);
+ inst_cream->Rd = BITS(inst, 16, 19);
+ inst_cream->Rs = BITS(inst, 8, 11);
+ inst_cream->Rm = BITS(inst, 0, 3);
+
+ if (CHECK_RM || CHECK_RN || CHECK_RS)
+ inst_base->load_r15 = 1;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(mov)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(mov_inst));
+ mov_inst *inst_cream = (mov_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(mrc)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(mrc_inst));
+ mrc_inst *inst_cream = (mrc_inst *)inst_base->component;
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->crn = BITS(inst, 16, 19);
+ inst_cream->crm = BITS(inst, 0, 3);
+ inst_cream->opcode_1 = BITS(inst, 21, 23);
+ inst_cream->opcode_2 = BITS(inst, 5, 7);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->cp_num = BITS(inst, 8, 11);
+ inst_cream->inst = inst;
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(ARMul_State* cpu,unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("MRRC"); }
+static ARM_INST_PTR INTERPRETER_TRANSLATE(mrs)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(mrs_inst));
+ mrs_inst *inst_cream = (mrs_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->R = BIT(inst, 22);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(msr)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(msr_inst));
+ msr_inst *inst_cream = (msr_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->field_mask = BITS(inst, 16, 19);
+ inst_cream->R = BIT(inst, 22);
+ inst_cream->inst = inst;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(mul)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(mul_inst));
+ mul_inst *inst_cream = (mul_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rs = BITS(inst, 8, 11);
+ inst_cream->Rd = BITS(inst, 16, 19);
+
+ if (CHECK_RM || CHECK_RS)
+ inst_base->load_r15 = 1;
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(mvn)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(mvn_inst));
+ mvn_inst *inst_cream = (mvn_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(orr)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(orr_inst));
+ orr_inst *inst_cream = (orr_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(pkhbt)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(pkh_inst));
+ pkh_inst *inst_cream = (pkh_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->imm = BITS(inst, 7, 11);
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(pkhtb)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(pkhbt)(cpu, inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(pld_inst));
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->op1 = BITS(inst, 21, 22);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(qadd)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(qadd)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(qadd)(cpu, inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->op1 = BITS(inst, 20, 21);
+ inst_cream->op2 = BITS(inst, 5, 7);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(qadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(qadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(qadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(qadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(qadd8)(cpu, inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(rev)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(rev_inst));
+ rev_inst* const inst_cream = (rev_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->op1 = BITS(inst, 20, 22);
+ inst_cream->op2 = BITS(inst, 5, 7);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(rev16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(rev)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(revsh)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(rev)(cpu, inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(rfe)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* const inst_cream = (ldst_inst*)inst_base->component;
+
+ inst_base->cond = AL;
+ inst_base->idx = index;
+ inst_base->br = INDIRECT_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(rsb)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(rsb_inst));
+ rsb_inst *inst_cream = (rsb_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(rsc)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(rsc_inst));
+ rsc_inst *inst_cream = (rsc_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->op1 = BITS(inst, 20, 21);
+ inst_cream->op2 = BITS(inst, 5, 7);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(sadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(sadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(sadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(sadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(sadd8)(cpu, inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(sbc_inst));
+ sbc_inst *inst_cream = (sbc_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->op1 = BITS(inst, 20, 22);
+ inst_cream->op2 = BITS(inst, 5, 7);
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(setend_inst));
+ setend_inst* const inst_cream = (setend_inst*)inst_base->component;
+
+ inst_base->cond = AL;
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->set_bigend = BIT(inst, 9);
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->op1 = BITS(inst, 20, 21);
+ inst_cream->op2 = BITS(inst, 5, 7);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(shadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(shaddsubx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(shadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(shsub8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(shadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(shsub16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(shadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(shsubaddx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(shadd8)(cpu, inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smla)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(smla_inst));
+ smla_inst *inst_cream = (smla_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->x = BIT(inst, 5);
+ inst_cream->y = BIT(inst, 6);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rs = BITS(inst, 8, 11);
+ inst_cream->Rd = BITS(inst, 16, 19);
+ inst_cream->Rn = BITS(inst, 12, 15);
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smlad)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(smlad_inst));
+ smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->m = BIT(inst, 5);
+ inst_cream->Rn = BITS(inst, 0, 3);
+ inst_cream->Rm = BITS(inst, 8, 11);
+ inst_cream->Rd = BITS(inst, 16, 19);
+ inst_cream->Ra = BITS(inst, 12, 15);
+ inst_cream->op1 = BITS(inst, 20, 22);
+ inst_cream->op2 = BITS(inst, 5, 7);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smuad)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(smlad)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smusd)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(smlad)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smlsd)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(smlad)(cpu, inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smlal)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(umlal_inst));
+ umlal_inst *inst_cream = (umlal_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rs = BITS(inst, 8, 11);
+ inst_cream->RdHi = BITS(inst, 16, 19);
+ inst_cream->RdLo = BITS(inst, 12, 15);
+
+ if (CHECK_RM || CHECK_RS)
+ inst_base->load_r15 = 1;
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(smlalxy_inst));
+ smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->x = BIT(inst, 5);
+ inst_cream->y = BIT(inst, 6);
+ inst_cream->RdLo = BITS(inst, 12, 15);
+ inst_cream->RdHi = BITS(inst, 16, 19);
+ inst_cream->Rn = BITS(inst, 0, 4);
+ inst_cream->Rm = BITS(inst, 8, 11);
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(smlad_inst));
+ smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Ra = BITS(inst, 12, 15);
+ inst_cream->Rm = BITS(inst, 8, 11);
+ inst_cream->Rn = BITS(inst, 0, 3);
+ inst_cream->Rd = BITS(inst, 16, 19);
+ inst_cream->m = BIT(inst, 6);
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smlald)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(smlald_inst));
+ smlald_inst* const inst_cream = (smlald_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rm = BITS(inst, 8, 11);
+ inst_cream->Rn = BITS(inst, 0, 3);
+ inst_cream->RdLo = BITS(inst, 12, 15);
+ inst_cream->RdHi = BITS(inst, 16, 19);
+ inst_cream->swap = BIT(inst, 5);
+ inst_cream->op1 = BITS(inst, 20, 22);
+ inst_cream->op2 = BITS(inst, 5, 7);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smlsld)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(smlald)(cpu, inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smmla)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(smlad_inst));
+ smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->m = BIT(inst, 5);
+ inst_cream->Ra = BITS(inst, 12, 15);
+ inst_cream->Rm = BITS(inst, 8, 11);
+ inst_cream->Rn = BITS(inst, 0, 3);
+ inst_cream->Rd = BITS(inst, 16, 19);
+ inst_cream->op1 = BITS(inst, 20, 22);
+ inst_cream->op2 = BITS(inst, 5, 7);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smmls)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(smmla)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smmul)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(smmla)(cpu, inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smul)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(smul_inst));
+ smul_inst *inst_cream = (smul_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rd = BITS(inst, 16, 19);
+ inst_cream->Rs = BITS(inst, 8, 11);
+ inst_cream->Rm = BITS(inst, 0, 3);
+
+ inst_cream->x = BIT(inst, 5);
+ inst_cream->y = BIT(inst, 6);
+
+ if (CHECK_RM || CHECK_RS)
+ inst_base->load_r15 = 1;
+ return inst_base;
+
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smull)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(umull_inst));
+ umull_inst *inst_cream = (umull_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rs = BITS(inst, 8, 11);
+ inst_cream->RdHi = BITS(inst, 16, 19);
+ inst_cream->RdLo = BITS(inst, 12, 15);
+
+ if (CHECK_RM || CHECK_RS)
+ inst_base->load_r15 = 1;
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(smulw)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(smlad_inst));
+ smlad_inst *inst_cream = (smlad_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->m = BIT(inst, 6);
+ inst_cream->Rm = BITS(inst, 8, 11);
+ inst_cream->Rn = BITS(inst, 0, 3);
+ inst_cream->Rd = BITS(inst, 16, 19);
+
+ if (CHECK_RM || CHECK_RN)
+ inst_base->load_r15 = 1;
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* const inst_cream = (ldst_inst*)inst_base->component;
+
+ inst_base->cond = AL;
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ssat_inst));
+ ssat_inst* const inst_cream = (ssat_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rn = BITS(inst, 0, 3);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->imm5 = BITS(inst, 7, 11);
+ inst_cream->sat_imm = BITS(inst, 16, 20);
+ inst_cream->shift_type = BIT(inst, 6);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ssat_inst));
+ ssat_inst* const inst_cream = (ssat_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rn = BITS(inst, 0, 3);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->sat_imm = BITS(inst, 16, 19);
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(stc_inst));
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(stm)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(sxtb_inst));
+ sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->rotate = BITS(inst, 10, 11);
+
+ if (CHECK_RM)
+ inst_base->load_r15 = 1;
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(str)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(uxth_inst));
+ uxth_inst *inst_cream = (uxth_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->rotate = BITS(inst, 10, 11);
+ inst_cream->Rm = BITS(inst, 0, 3);
+
+ if (CHECK_RM)
+ inst_base->load_r15 = 1;
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(uxtab_inst));
+ uxtab_inst *inst_cream = (uxtab_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->rotate = BITS(inst, 10, 11);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(strb)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(strbt)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+
+ if (BITS(inst, 25, 27) == 2) {
+ inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 25, 27) == 3) {
+ inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed);
+ } else {
+ DEBUG_MSG;
+ }
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(strd)(ARMul_State* cpu,unsigned int inst, int index){
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(strex)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst *inst_cream = (generic_arm_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->Rm = BITS(inst, 0, 3);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(strexb)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(strex)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(strexh)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(strex)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(strexd)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(strex)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(strh)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ inst_cream->get_addr = get_calc_addr_op(inst);
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(strt)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->inst = inst;
+ if (BITS(inst, 25, 27) == 2) {
+ inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 25, 27) == 3) {
+ inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed);
+ } else {
+ // Reaching this would indicate the thumb version
+ // of this instruction, however the 3DS CPU doesn't
+ // support this variant (the 3DS CPU is only ARMv6K,
+ // while this variant is added in ARMv6T2).
+ // So it's sufficient for citra to not implement this.
+ DEBUG_MSG;
+ }
+
+ if (BITS(inst, 12, 15) == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(sub)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(sub_inst));
+ sub_inst *inst_cream = (sub_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(swi)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(swi_inst));
+ swi_inst *inst_cream = (swi_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->num = BITS(inst, 0, 23);
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(swp)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(swp_inst));
+ swp_inst *inst_cream = (swp_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->Rm = BITS(inst, 0, 3);
+
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(swpb)(ARMul_State* cpu,unsigned int inst, int index){
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(swp_inst));
+ swp_inst *inst_cream = (swp_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->Rm = BITS(inst, 0, 3);
+
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab)(ARMul_State* cpu,unsigned int inst, int index){
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(sxtab_inst));
+ sxtab_inst *inst_cream = (sxtab_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->rotate = BITS(inst, 10, 11);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(sxtab_inst));
+ sxtab_inst* const inst_cream = (sxtab_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->rotate = BITS(inst, 10, 11);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(sxtab16)(cpu, inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(ARMul_State* cpu,unsigned int inst, int index) {
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(sxtah_inst));
+ sxtah_inst *inst_cream = (sxtah_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->rotate = BITS(inst, 10, 11);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(teq)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(teq_inst));
+ teq_inst *inst_cream = (teq_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(tst)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(tst_inst));
+ tst_inst *inst_cream = (tst_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->I = BIT(inst, 25);
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->shifter_operand = BITS(inst, 0, 11);
+ inst_cream->shtop_func = get_shtop(inst);
+ if (inst_cream->Rd == 15) {
+ inst_base->br = INDIRECT_BRANCH;
+ }
+
+ if (CHECK_RN)
+ inst_base->load_r15 = 1;
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->op1 = BITS(inst, 20, 21);
+ inst_cream->op2 = BITS(inst, 5, 7);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(usub8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(usub16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(usubaddx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uadd8)(cpu, inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->op1 = BITS(inst, 20, 21);
+ inst_cream->op2 = BITS(inst, 5, 7);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uhadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uhadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uhadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uhadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uhadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(umaal_inst));
+ umaal_inst* const inst_cream = (umaal_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rm = BITS(inst, 8, 11);
+ inst_cream->Rn = BITS(inst, 0, 3);
+ inst_cream->RdLo = BITS(inst, 12, 15);
+ inst_cream->RdHi = BITS(inst, 16, 19);
+
+ if (CHECK_RM || CHECK_RN)
+ inst_base->load_r15 = 1;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(umlal_inst));
+ umlal_inst *inst_cream = (umlal_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rs = BITS(inst, 8, 11);
+ inst_cream->RdHi = BITS(inst, 16, 19);
+ inst_cream->RdLo = BITS(inst, 12, 15);
+
+ if (CHECK_RM || CHECK_RS)
+ inst_base->load_r15 = 1;
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(umull)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(umull_inst));
+ umull_inst *inst_cream = (umull_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->S = BIT(inst, 20);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rs = BITS(inst, 8, 11);
+ inst_cream->RdHi = BITS(inst, 16, 19);
+ inst_cream->RdLo = BITS(inst, 12, 15);
+
+ if (CHECK_RM || CHECK_RS)
+ inst_base->load_r15 = 1;
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(b_2_thumb)(ARMul_State* cpu,unsigned int tinst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(b_2_thumb));
+ b_2_thumb *inst_cream = (b_2_thumb *)inst_base->component;
+
+ inst_cream->imm = ((tinst & 0x3FF) << 1) | ((tinst & (1 << 10)) ? 0xFFFFF800 : 0);
+
+ inst_base->idx = index;
+ inst_base->br = DIRECT_BRANCH;
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(b_cond_thumb)(ARMul_State* cpu,unsigned int tinst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(b_cond_thumb));
+ b_cond_thumb *inst_cream = (b_cond_thumb *)inst_base->component;
+
+ inst_cream->imm = (((tinst & 0x7F) << 1) | ((tinst & (1 << 7)) ? 0xFFFFFF00 : 0));
+ inst_cream->cond = ((tinst >> 8) & 0xf);
+ inst_base->idx = index;
+ inst_base->br = DIRECT_BRANCH;
+
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(bl_1_thumb)(ARMul_State* cpu,unsigned int tinst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(bl_1_thumb));
+ bl_1_thumb *inst_cream = (bl_1_thumb *)inst_base->component;
+
+ inst_cream->imm = (((tinst & 0x07FF) << 12) | ((tinst & (1 << 10)) ? 0xFF800000 : 0));
+
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(bl_2_thumb)(ARMul_State* cpu,unsigned int tinst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(bl_2_thumb));
+ bl_2_thumb *inst_cream = (bl_2_thumb *)inst_base->component;
+
+ inst_cream->imm = (tinst & 0x07FF) << 1;
+
+ inst_base->idx = index;
+ inst_base->br = DIRECT_BRANCH;
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(blx_1_thumb)(ARMul_State* cpu,unsigned int tinst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(blx_1_thumb));
+ blx_1_thumb *inst_cream = (blx_1_thumb *)inst_base->component;
+
+ inst_cream->imm = (tinst & 0x07FF) << 1;
+ inst_cream->instr = tinst;
+
+ inst_base->idx = index;
+ inst_base->br = DIRECT_BRANCH;
+ return inst_base;
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->op1 = BITS(inst, 20, 21);
+ inst_cream->op2 = BITS(inst, 5, 7);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uqadd8)(cpu,inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uqaddsubx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uqadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uqadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uqadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uqsubaddx)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uqadd8)(cpu, inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(usada8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->op1 = BITS(inst, 20, 24);
+ inst_cream->op2 = BITS(inst, 5, 7);
+ inst_cream->Rd = BITS(inst, 16, 19);
+ inst_cream->Rm = BITS(inst, 8, 11);
+ inst_cream->Rn = BITS(inst, 0, 3);
+ inst_cream->Ra = BITS(inst, 12, 15);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(usad8)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(usada8)(cpu,inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(usat)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(ssat)(cpu,inst, index);
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(usat16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(ssat16)(cpu,inst, index);
+}
+
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(uxtab_inst));
+ uxtab_inst* const inst_cream = (uxtab_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+ inst_cream->rotate = BITS(inst, 10, 11);
+
+ return inst_base;
+}
+static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb16)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uxtab16)(cpu,inst, index);
+}
+
+// Floating point VFPv3 structures and instructions
+
+#define VFP_INTERPRETER_STRUCT
+#include "arm/skyeye_common/vfp/vfpinstr.cpp"
+#undef VFP_INTERPRETER_STRUCT
+
+#define VFP_INTERPRETER_TRANS
+#include "arm/skyeye_common/vfp/vfpinstr.cpp"
+#undef VFP_INTERPRETER_TRANS
+
+typedef ARM_INST_PTR(*transop_fp_t)(ARMul_State* cpu,unsigned int, int);
+
+const transop_fp_t arm_instruction_trans[] = {
+ INTERPRETER_TRANSLATE(vmla),
+ INTERPRETER_TRANSLATE(vmls),
+ INTERPRETER_TRANSLATE(vnmla),
+ INTERPRETER_TRANSLATE(vnmla),
+ INTERPRETER_TRANSLATE(vnmls),
+ INTERPRETER_TRANSLATE(vnmul),
+ INTERPRETER_TRANSLATE(vmul),
+ INTERPRETER_TRANSLATE(vadd),
+ INTERPRETER_TRANSLATE(vsub),
+ INTERPRETER_TRANSLATE(vdiv),
+ INTERPRETER_TRANSLATE(vmovi),
+ INTERPRETER_TRANSLATE(vmovr),
+ INTERPRETER_TRANSLATE(vabs),
+ INTERPRETER_TRANSLATE(vneg),
+ INTERPRETER_TRANSLATE(vsqrt),
+ INTERPRETER_TRANSLATE(vcmp),
+ INTERPRETER_TRANSLATE(vcmp2),
+ INTERPRETER_TRANSLATE(vcvtbds),
+ INTERPRETER_TRANSLATE(vcvtbff),
+ INTERPRETER_TRANSLATE(vcvtbfi),
+ INTERPRETER_TRANSLATE(vmovbrs),
+ INTERPRETER_TRANSLATE(vmsr),
+ INTERPRETER_TRANSLATE(vmovbrc),
+ INTERPRETER_TRANSLATE(vmrs),
+ INTERPRETER_TRANSLATE(vmovbcr),
+ INTERPRETER_TRANSLATE(vmovbrrss),
+ INTERPRETER_TRANSLATE(vmovbrrd),
+ INTERPRETER_TRANSLATE(vstr),
+ INTERPRETER_TRANSLATE(vpush),
+ INTERPRETER_TRANSLATE(vstm),
+ INTERPRETER_TRANSLATE(vpop),
+ INTERPRETER_TRANSLATE(vldr),
+ INTERPRETER_TRANSLATE(vldm),
+
+ INTERPRETER_TRANSLATE(srs),
+ INTERPRETER_TRANSLATE(rfe),
+ INTERPRETER_TRANSLATE(bkpt),
+ INTERPRETER_TRANSLATE(blx),
+ INTERPRETER_TRANSLATE(cps),
+ INTERPRETER_TRANSLATE(pld),
+ INTERPRETER_TRANSLATE(setend),
+ INTERPRETER_TRANSLATE(clrex),
+ INTERPRETER_TRANSLATE(rev16),
+ INTERPRETER_TRANSLATE(usad8),
+ INTERPRETER_TRANSLATE(sxtb),
+ INTERPRETER_TRANSLATE(uxtb),
+ INTERPRETER_TRANSLATE(sxth),
+ INTERPRETER_TRANSLATE(sxtb16),
+ INTERPRETER_TRANSLATE(uxth),
+ INTERPRETER_TRANSLATE(uxtb16),
+ INTERPRETER_TRANSLATE(cpy),
+ INTERPRETER_TRANSLATE(uxtab),
+ INTERPRETER_TRANSLATE(ssub8),
+ INTERPRETER_TRANSLATE(shsub8),
+ INTERPRETER_TRANSLATE(ssubaddx),
+ INTERPRETER_TRANSLATE(strex),
+ INTERPRETER_TRANSLATE(strexb),
+ INTERPRETER_TRANSLATE(swp),
+ INTERPRETER_TRANSLATE(swpb),
+ INTERPRETER_TRANSLATE(ssub16),
+ INTERPRETER_TRANSLATE(ssat16),
+ INTERPRETER_TRANSLATE(shsubaddx),
+ INTERPRETER_TRANSLATE(qsubaddx),
+ INTERPRETER_TRANSLATE(shaddsubx),
+ INTERPRETER_TRANSLATE(shadd8),
+ INTERPRETER_TRANSLATE(shadd16),
+ INTERPRETER_TRANSLATE(sel),
+ INTERPRETER_TRANSLATE(saddsubx),
+ INTERPRETER_TRANSLATE(sadd8),
+ INTERPRETER_TRANSLATE(sadd16),
+ INTERPRETER_TRANSLATE(shsub16),
+ INTERPRETER_TRANSLATE(umaal),
+ INTERPRETER_TRANSLATE(uxtab16),
+ INTERPRETER_TRANSLATE(usubaddx),
+ INTERPRETER_TRANSLATE(usub8),
+ INTERPRETER_TRANSLATE(usub16),
+ INTERPRETER_TRANSLATE(usat16),
+ INTERPRETER_TRANSLATE(usada8),
+ INTERPRETER_TRANSLATE(uqsubaddx),
+ INTERPRETER_TRANSLATE(uqsub8),
+ INTERPRETER_TRANSLATE(uqsub16),
+ INTERPRETER_TRANSLATE(uqaddsubx),
+ INTERPRETER_TRANSLATE(uqadd8),
+ INTERPRETER_TRANSLATE(uqadd16),
+ INTERPRETER_TRANSLATE(sxtab),
+ INTERPRETER_TRANSLATE(uhsubaddx),
+ INTERPRETER_TRANSLATE(uhsub8),
+ INTERPRETER_TRANSLATE(uhsub16),
+ INTERPRETER_TRANSLATE(uhaddsubx),
+ INTERPRETER_TRANSLATE(uhadd8),
+ INTERPRETER_TRANSLATE(uhadd16),
+ INTERPRETER_TRANSLATE(uaddsubx),
+ INTERPRETER_TRANSLATE(uadd8),
+ INTERPRETER_TRANSLATE(uadd16),
+ INTERPRETER_TRANSLATE(sxtah),
+ INTERPRETER_TRANSLATE(sxtab16),
+ INTERPRETER_TRANSLATE(qadd8),
+ INTERPRETER_TRANSLATE(bxj),
+ INTERPRETER_TRANSLATE(clz),
+ INTERPRETER_TRANSLATE(uxtah),
+ INTERPRETER_TRANSLATE(bx),
+ INTERPRETER_TRANSLATE(rev),
+ INTERPRETER_TRANSLATE(blx),
+ INTERPRETER_TRANSLATE(revsh),
+ INTERPRETER_TRANSLATE(qadd),
+ INTERPRETER_TRANSLATE(qadd16),
+ INTERPRETER_TRANSLATE(qaddsubx),
+ INTERPRETER_TRANSLATE(ldrex),
+ INTERPRETER_TRANSLATE(qdadd),
+ INTERPRETER_TRANSLATE(qdsub),
+ INTERPRETER_TRANSLATE(qsub),
+ INTERPRETER_TRANSLATE(ldrexb),
+ INTERPRETER_TRANSLATE(qsub8),
+ INTERPRETER_TRANSLATE(qsub16),
+ INTERPRETER_TRANSLATE(smuad),
+ INTERPRETER_TRANSLATE(smmul),
+ INTERPRETER_TRANSLATE(smusd),
+ INTERPRETER_TRANSLATE(smlsd),
+ INTERPRETER_TRANSLATE(smlsld),
+ INTERPRETER_TRANSLATE(smmla),
+ INTERPRETER_TRANSLATE(smmls),
+ INTERPRETER_TRANSLATE(smlald),
+ INTERPRETER_TRANSLATE(smlad),
+ INTERPRETER_TRANSLATE(smlaw),
+ INTERPRETER_TRANSLATE(smulw),
+ INTERPRETER_TRANSLATE(pkhtb),
+ INTERPRETER_TRANSLATE(pkhbt),
+ INTERPRETER_TRANSLATE(smul),
+ INTERPRETER_TRANSLATE(smlalxy),
+ INTERPRETER_TRANSLATE(smla),
+ INTERPRETER_TRANSLATE(mcrr),
+ INTERPRETER_TRANSLATE(mrrc),
+ INTERPRETER_TRANSLATE(cmp),
+ INTERPRETER_TRANSLATE(tst),
+ INTERPRETER_TRANSLATE(teq),
+ INTERPRETER_TRANSLATE(cmn),
+ INTERPRETER_TRANSLATE(smull),
+ INTERPRETER_TRANSLATE(umull),
+ INTERPRETER_TRANSLATE(umlal),
+ INTERPRETER_TRANSLATE(smlal),
+ INTERPRETER_TRANSLATE(mul),
+ INTERPRETER_TRANSLATE(mla),
+ INTERPRETER_TRANSLATE(ssat),
+ INTERPRETER_TRANSLATE(usat),
+ INTERPRETER_TRANSLATE(mrs),
+ INTERPRETER_TRANSLATE(msr),
+ INTERPRETER_TRANSLATE(and),
+ INTERPRETER_TRANSLATE(bic),
+ INTERPRETER_TRANSLATE(ldm),
+ INTERPRETER_TRANSLATE(eor),
+ INTERPRETER_TRANSLATE(add),
+ INTERPRETER_TRANSLATE(rsb),
+ INTERPRETER_TRANSLATE(rsc),
+ INTERPRETER_TRANSLATE(sbc),
+ INTERPRETER_TRANSLATE(adc),
+ INTERPRETER_TRANSLATE(sub),
+ INTERPRETER_TRANSLATE(orr),
+ INTERPRETER_TRANSLATE(mvn),
+ INTERPRETER_TRANSLATE(mov),
+ INTERPRETER_TRANSLATE(stm),
+ INTERPRETER_TRANSLATE(ldm),
+ INTERPRETER_TRANSLATE(ldrsh),
+ INTERPRETER_TRANSLATE(stm),
+ INTERPRETER_TRANSLATE(ldm),
+ INTERPRETER_TRANSLATE(ldrsb),
+ INTERPRETER_TRANSLATE(strd),
+ INTERPRETER_TRANSLATE(ldrh),
+ INTERPRETER_TRANSLATE(strh),
+ INTERPRETER_TRANSLATE(ldrd),
+ INTERPRETER_TRANSLATE(strt),
+ INTERPRETER_TRANSLATE(strbt),
+ INTERPRETER_TRANSLATE(ldrbt),
+ INTERPRETER_TRANSLATE(ldrt),
+ INTERPRETER_TRANSLATE(mrc),
+ INTERPRETER_TRANSLATE(mcr),
+ INTERPRETER_TRANSLATE(msr),
+ INTERPRETER_TRANSLATE(ldrb),
+ INTERPRETER_TRANSLATE(strb),
+ INTERPRETER_TRANSLATE(ldr),
+ INTERPRETER_TRANSLATE(ldrcond),
+ INTERPRETER_TRANSLATE(str),
+ INTERPRETER_TRANSLATE(cdp),
+ INTERPRETER_TRANSLATE(stc),
+ INTERPRETER_TRANSLATE(ldc),
+ INTERPRETER_TRANSLATE(swi),
+ INTERPRETER_TRANSLATE(bbl),
+ INTERPRETER_TRANSLATE(ldrexd),
+ INTERPRETER_TRANSLATE(strexd),
+ INTERPRETER_TRANSLATE(ldrexh),
+ INTERPRETER_TRANSLATE(strexh),
+
+ // All the thumb instructions should be placed the end of table
+ INTERPRETER_TRANSLATE(b_2_thumb),
+ INTERPRETER_TRANSLATE(b_cond_thumb),
+ INTERPRETER_TRANSLATE(bl_1_thumb),
+ INTERPRETER_TRANSLATE(bl_2_thumb),
+ INTERPRETER_TRANSLATE(blx_1_thumb)
+};
+
+static void insert_bb(ARMul_State * cpu,unsigned int addr, int start) {
+ (*(cpu->CreamCache))[addr] = start;
+}
+
+static int find_bb(ARMul_State * cpu, unsigned int addr, int& start) {
+ int ret = -1;
+ bb_map::const_iterator it = cpu->CreamCache->find(addr);
+ if (it != cpu->CreamCache->end()) {
+ start = static_cast<int>(it->second);
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+ return ret;
+}
+
+enum {
+ FETCH_SUCCESS,
+ FETCH_FAILURE
+};
+
+static tdstate decode_thumb_instr(ARMul_State* cpu, uint32_t inst, addr_t addr, uint32_t* arm_inst, uint32_t* inst_size, ARM_INST_PTR* ptr_inst_base){
+ // Check if in Thumb mode
+ tdstate ret = thumb_translate (addr, inst, arm_inst, inst_size);
+ if(ret == t_branch){
+ // TODO: FIXME, endian should be judged
+ u32 tinstr;
+ if((addr & 0x3) != 0)
+ tinstr = inst >> 16;
+ else
+ tinstr = inst & 0xFFFF;
+
+ int inst_index;
+ int table_length = sizeof(arm_instruction_trans) / sizeof(transop_fp_t);
+
+ switch((tinstr & 0xF800) >> 11){
+ case 26:
+ case 27:
+ if (((tinstr & 0x0F00) != 0x0E00) && ((tinstr & 0x0F00) != 0x0F00)){
+ inst_index = table_length - 4;
+ *ptr_inst_base = arm_instruction_trans[inst_index](cpu,tinstr, inst_index);
+ } else {
+ XDSERROR("thumb decoder error");
+ }
+ break;
+ case 28:
+ // Branch 2, unconditional branch
+ inst_index = table_length - 5;
+ *ptr_inst_base = arm_instruction_trans[inst_index](cpu,tinstr, inst_index);
+ break;
+
+ case 8:
+ case 29:
+ // For BLX 1 thumb instruction
+ inst_index = table_length - 1;
+ *ptr_inst_base = arm_instruction_trans[inst_index](cpu,tinstr, inst_index);
+ break;
+ case 30:
+ // For BL 1 thumb instruction
+ inst_index = table_length - 3;
+ *ptr_inst_base = arm_instruction_trans[inst_index](cpu,tinstr, inst_index);
+ break;
+ case 31:
+ // For BL 2 thumb instruction
+ inst_index = table_length - 2;
+ *ptr_inst_base = arm_instruction_trans[inst_index](cpu,tinstr, inst_index);
+ break;
+ default:
+ ret = t_undefined;
+ break;
+ }
+ }
+ return ret;
+}
+
+enum {
+ KEEP_GOING,
+ FETCH_EXCEPTION
+};
+
+typedef struct instruction_set_encoding_item ISEITEM;
+
+extern const ISEITEM arm_instruction[];
+
+static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, addr_t addr) {
+
+ // Decode instruction, get index
+ // Allocate memory and init InsCream
+ // Go on next, until terminal instruction
+ // Save start addr of basicblock in CreamCache
+ ARM_INST_PTR inst_base = nullptr;
+ unsigned int inst, inst_size = 4;
+ int idx;
+ int ret = NON_BRANCH;
+ int thumb = 0;
+ int size = 0; // instruction size of basic block
+ bb_start = cpu->inst_bufftop;
+
+ if (cpu->TFlag)
+ thumb = THUMB;
+
+ addr_t phys_addr = addr;
+ addr_t pc_start = cpu->Reg[15];
+
+ while(ret == NON_BRANCH) {
+ inst = ARMul_LoadInstrS(cpu, phys_addr & 0xFFFFFFFC, 4);
+
+ size++;
+ // If we are in thumb instruction, we will translate one thumb to one corresponding arm instruction
+ if (cpu->TFlag) {
+ uint32_t arm_inst;
+ tdstate state;
+ state = decode_thumb_instr(cpu, inst, phys_addr, &arm_inst, &inst_size, &inst_base);
+
+ // We have translated the branch instruction of thumb in thumb decoder
+ if(state == t_branch){
+ goto translated;
+ }
+ inst = arm_inst;
+ }
+
+ ret = decode_arm_instr(inst, &idx);
+ if (ret == DECODE_FAILURE) {
+ std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst);
+ XDSERROR("Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, disasm.c_str(), inst);
+ XDSERROR("cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]);
+ CITRA_IGNORE_EXIT(-1);
+ }
+ inst_base = arm_instruction_trans[idx](cpu,inst, idx);
+translated:
+ phys_addr += inst_size;
+
+ if ((phys_addr & 0xfff) == 0) {
+ inst_base->br = END_OF_PAGE;
+ }
+ ret = inst_base->br;
+ };
+ insert_bb(cpu,pc_start, bb_start);
+ return KEEP_GOING;
+}
+
+static int clz(unsigned int x) {
+ int n;
+ if (x == 0) return (32);
+ n = 1;
+ if ((x >> 16) == 0) { n = n + 16; x = x << 16;}
+ if ((x >> 24) == 0) { n = n + 8; x = x << 8;}
+ if ((x >> 28) == 0) { n = n + 4; x = x << 4;}
+ if ((x >> 30) == 0) { n = n + 2; x = x << 2;}
+ n = n - (x >> 31);
+ return n;
+}
+#ifdef inst_debug
+void inst_deb(ARMul_State* cpu)
+{
+ if (strcmp(LOGMODULE, cpu->m_currentThread->m_owner->GetName()) == 0)
+ {
+ if (cpu->Reg[15] >= 0x00100040)
+ {
+ for (int i = 0; i < 4; i++)
+ LOG("%08x %08x %08x %08x", cpu->Reg[0 + i * 4], cpu->Reg[1 + i * 4], cpu->Reg[2 + i * 4], cpu->Reg[3 + i * 4]);
+ LOG("");
+ }
+ }
+}
+#endif
+unsigned InterpreterMainLoop(ARMul_State* state) {
+
+#undef RM
+#undef RS
+
+#define CRn inst_cream->crn
+#define OPCODE_1 inst_cream->opcode_1
+#define OPCODE_2 inst_cream->opcode_2
+#define CRm inst_cream->crm
+#define CP15_REG(n) cpu->CP15[CP15(n)]
+#define RD cpu->Reg[inst_cream->Rd]
+#define RD2 cpu->Reg[inst_cream->Rd + 1]
+#define RN cpu->Reg[inst_cream->Rn]
+#define RM cpu->Reg[inst_cream->Rm]
+#define RS cpu->Reg[inst_cream->Rs]
+#define RDHI cpu->Reg[inst_cream->RdHi]
+#define RDLO cpu->Reg[inst_cream->RdLo]
+#define LINK_RTN_ADDR (cpu->Reg[14] = cpu->Reg[15] + 4)
+#define SET_PC (cpu->Reg[15] = cpu->Reg[15] + 8 + inst_cream->signed_immed_24)
+#define SHIFTER_OPERAND inst_cream->shtop_func(cpu, inst_cream->shifter_operand)
+
+#ifdef inst_debug
+#define FETCH_INST inst_deb(cpu); \
+ if (inst_base->br != NON_BRANCH) goto DISPATCH; \
+ inst_base = (arm_inst *)&cpu->inst_buf[ptr]
+#else
+#define FETCH_INST if (inst_base->br != NON_BRANCH) goto DISPATCH; \
+ inst_base = (arm_inst *)&cpu->inst_buf[ptr]
+#endif
+
+ #define INC_PC(l) ptr += sizeof(arm_inst) + l
+
+// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a
+// clunky switch statement.
+#if defined __GNUC__ || defined __clang__
+#define GOTO_NEXT_INST \
+ if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
+ num_instrs++; \
+ goto *InstLabel[inst_base->idx]
+#else
+#define GOTO_NEXT_INST \
+ if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
+ num_instrs++; \
+ switch(inst_base->idx) { \
+ case 0: goto VMLA_INST; \
+ case 1: goto VMLS_INST; \
+ case 2: goto VNMLA_INST; \
+ case 3: goto VNMLA_INST; \
+ case 4: goto VNMLS_INST; \
+ case 5: goto VNMUL_INST; \
+ case 6: goto VMUL_INST; \
+ case 7: goto VADD_INST; \
+ case 8: goto VSUB_INST; \
+ case 9: goto VDIV_INST; \
+ case 10: goto VMOVI_INST; \
+ case 11: goto VMOVR_INST; \
+ case 12: goto VABS_INST; \
+ case 13: goto VNEG_INST; \
+ case 14: goto VSQRT_INST; \
+ case 15: goto VCMP_INST; \
+ case 16: goto VCMP2_INST; \
+ case 17: goto VCVTBDS_INST; \
+ case 18: goto VCVTBFF_INST; \
+ case 19: goto VCVTBFI_INST; \
+ case 20: goto VMOVBRS_INST; \
+ case 21: goto VMSR_INST; \
+ case 22: goto VMOVBRC_INST; \
+ case 23: goto VMRS_INST; \
+ case 24: goto VMOVBCR_INST; \
+ case 25: goto VMOVBRRSS_INST; \
+ case 26: goto VMOVBRRD_INST; \
+ case 27: goto VSTR_INST; \
+ case 28: goto VPUSH_INST; \
+ case 29: goto VSTM_INST; \
+ case 30: goto VPOP_INST; \
+ case 31: goto VLDR_INST; \
+ case 32: goto VLDM_INST ; \
+ case 33: goto SRS_INST; \
+ case 34: goto RFE_INST; \
+ case 35: goto BKPT_INST; \
+ case 36: goto BLX_INST; \
+ case 37: goto CPS_INST; \
+ case 38: goto PLD_INST; \
+ case 39: goto SETEND_INST; \
+ case 40: goto CLREX_INST; \
+ case 41: goto REV16_INST; \
+ case 42: goto USAD8_INST; \
+ case 43: goto SXTB_INST; \
+ case 44: goto UXTB_INST; \
+ case 45: goto SXTH_INST; \
+ case 46: goto SXTB16_INST; \
+ case 47: goto UXTH_INST; \
+ case 48: goto UXTB16_INST; \
+ case 49: goto CPY_INST; \
+ case 50: goto UXTAB_INST; \
+ case 51: goto SSUB8_INST; \
+ case 52: goto SHSUB8_INST; \
+ case 53: goto SSUBADDX_INST; \
+ case 54: goto STREX_INST; \
+ case 55: goto STREXB_INST; \
+ case 56: goto SWP_INST; \
+ case 57: goto SWPB_INST; \
+ case 58: goto SSUB16_INST; \
+ case 59: goto SSAT16_INST; \
+ case 60: goto SHSUBADDX_INST; \
+ case 61: goto QSUBADDX_INST; \
+ case 62: goto SHADDSUBX_INST; \
+ case 63: goto SHADD8_INST; \
+ case 64: goto SHADD16_INST; \
+ case 65: goto SEL_INST; \
+ case 66: goto SADDSUBX_INST; \
+ case 67: goto SADD8_INST; \
+ case 68: goto SADD16_INST; \
+ case 69: goto SHSUB16_INST; \
+ case 70: goto UMAAL_INST; \
+ case 71: goto UXTAB16_INST; \
+ case 72: goto USUBADDX_INST; \
+ case 73: goto USUB8_INST; \
+ case 74: goto USUB16_INST; \
+ case 75: goto USAT16_INST; \
+ case 76: goto USADA8_INST; \
+ case 77: goto UQSUBADDX_INST; \
+ case 78: goto UQSUB8_INST; \
+ case 79: goto UQSUB16_INST; \
+ case 80: goto UQADDSUBX_INST; \
+ case 81: goto UQADD8_INST; \
+ case 82: goto UQADD16_INST; \
+ case 83: goto SXTAB_INST; \
+ case 84: goto UHSUBADDX_INST; \
+ case 85: goto UHSUB8_INST; \
+ case 86: goto UHSUB16_INST; \
+ case 87: goto UHADDSUBX_INST; \
+ case 88: goto UHADD8_INST; \
+ case 89: goto UHADD16_INST; \
+ case 90: goto UADDSUBX_INST; \
+ case 91: goto UADD8_INST; \
+ case 92: goto UADD16_INST; \
+ case 93: goto SXTAH_INST; \
+ case 94: goto SXTAB16_INST; \
+ case 95: goto QADD8_INST; \
+ case 96: goto BXJ_INST; \
+ case 97: goto CLZ_INST; \
+ case 98: goto UXTAH_INST; \
+ case 99: goto BX_INST; \
+ case 100: goto REV_INST; \
+ case 101: goto BLX_INST; \
+ case 102: goto REVSH_INST; \
+ case 103: goto QADD_INST; \
+ case 104: goto QADD16_INST; \
+ case 105: goto QADDSUBX_INST; \
+ case 106: goto LDREX_INST; \
+ case 107: goto QDADD_INST; \
+ case 108: goto QDSUB_INST; \
+ case 109: goto QSUB_INST; \
+ case 110: goto LDREXB_INST; \
+ case 111: goto QSUB8_INST; \
+ case 112: goto QSUB16_INST; \
+ case 113: goto SMUAD_INST; \
+ case 114: goto SMMUL_INST; \
+ case 115: goto SMUSD_INST; \
+ case 116: goto SMLSD_INST; \
+ case 117: goto SMLSLD_INST; \
+ case 118: goto SMMLA_INST; \
+ case 119: goto SMMLS_INST; \
+ case 120: goto SMLALD_INST; \
+ case 121: goto SMLAD_INST; \
+ case 122: goto SMLAW_INST; \
+ case 123: goto SMULW_INST; \
+ case 124: goto PKHTB_INST; \
+ case 125: goto PKHBT_INST; \
+ case 126: goto SMUL_INST; \
+ case 127: goto SMLALXY_INST; \
+ case 128: goto SMLA_INST; \
+ case 129: goto MCRR_INST; \
+ case 130: goto MRRC_INST; \
+ case 131: goto CMP_INST; \
+ case 132: goto TST_INST; \
+ case 133: goto TEQ_INST; \
+ case 134: goto CMN_INST; \
+ case 135: goto SMULL_INST; \
+ case 136: goto UMULL_INST; \
+ case 137: goto UMLAL_INST; \
+ case 138: goto SMLAL_INST; \
+ case 139: goto MUL_INST; \
+ case 140: goto MLA_INST; \
+ case 141: goto SSAT_INST; \
+ case 142: goto USAT_INST; \
+ case 143: goto MRS_INST; \
+ case 144: goto MSR_INST; \
+ case 145: goto AND_INST; \
+ case 146: goto BIC_INST; \
+ case 147: goto LDM_INST; \
+ case 148: goto EOR_INST; \
+ case 149: goto ADD_INST; \
+ case 150: goto RSB_INST; \
+ case 151: goto RSC_INST; \
+ case 152: goto SBC_INST; \
+ case 153: goto ADC_INST; \
+ case 154: goto SUB_INST; \
+ case 155: goto ORR_INST; \
+ case 156: goto MVN_INST; \
+ case 157: goto MOV_INST; \
+ case 158: goto STM_INST; \
+ case 159: goto LDM_INST; \
+ case 160: goto LDRSH_INST; \
+ case 161: goto STM_INST; \
+ case 162: goto LDM_INST; \
+ case 163: goto LDRSB_INST; \
+ case 164: goto STRD_INST; \
+ case 165: goto LDRH_INST; \
+ case 166: goto STRH_INST; \
+ case 167: goto LDRD_INST; \
+ case 168: goto STRT_INST; \
+ case 169: goto STRBT_INST; \
+ case 170: goto LDRBT_INST; \
+ case 171: goto LDRT_INST; \
+ case 172: goto MRC_INST; \
+ case 173: goto MCR_INST; \
+ case 174: goto MSR_INST; \
+ case 175: goto LDRB_INST; \
+ case 176: goto STRB_INST; \
+ case 177: goto LDR_INST; \
+ case 178: goto LDRCOND_INST ; \
+ case 179: goto STR_INST; \
+ case 180: goto CDP_INST; \
+ case 181: goto STC_INST; \
+ case 182: goto LDC_INST; \
+ case 183: goto SWI_INST; \
+ case 184: goto BBL_INST; \
+ case 185: goto LDREXD_INST; \
+ case 186: goto STREXD_INST; \
+ case 187: goto LDREXH_INST; \
+ case 188: goto STREXH_INST; \
+ case 189: goto B_2_THUMB ; \
+ case 190: goto B_COND_THUMB ; \
+ case 191: goto BL_1_THUMB ; \
+ case 192: goto BL_2_THUMB ; \
+ case 193: goto BLX_1_THUMB ; \
+ case 194: goto DISPATCH; \
+ case 195: goto INIT_INST_LENGTH; \
+ case 196: goto END; \
+ }
+#endif
+
+ #define UPDATE_NFLAG(dst) (cpu->NFlag = BIT(dst, 31) ? 1 : 0)
+ #define UPDATE_ZFLAG(dst) (cpu->ZFlag = dst ? 0 : 1)
+ #define UPDATE_CFLAG_WITH_SC (cpu->CFlag = cpu->shifter_carry_out)
+
+ #define SAVE_NZCVT cpu->Cpsr = (cpu->Cpsr & 0x0fffffdf) | \
+ (cpu->NFlag << 31) | \
+ (cpu->ZFlag << 30) | \
+ (cpu->CFlag << 29) | \
+ (cpu->VFlag << 28) | \
+ (cpu->TFlag << 5)
+ #define LOAD_NZCVT cpu->NFlag = (cpu->Cpsr >> 31); \
+ cpu->ZFlag = (cpu->Cpsr >> 30) & 1; \
+ cpu->CFlag = (cpu->Cpsr >> 29) & 1; \
+ cpu->VFlag = (cpu->Cpsr >> 28) & 1; \
+ cpu->TFlag = (cpu->Cpsr >> 5) & 1;
+
+ #define CurrentModeHasSPSR (cpu->Mode != SYSTEM32MODE) && (cpu->Mode != USER32MODE)
+ #define PC (cpu->Reg[15])
+ #define CHECK_EXT_INT if (!cpu->NirqSig && !(cpu->Cpsr & 0x80)) goto END;
+
+ ARMul_State* cpu = state;
+
+ // GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback
+ // to a clunky switch statement.
+#if defined __GNUC__ || defined __clang__
+ void *InstLabel[] = {
+ &&VMLA_INST, &&VMLS_INST, &&VNMLA_INST, &&VNMLA_INST, &&VNMLS_INST, &&VNMUL_INST, &&VMUL_INST, &&VADD_INST, &&VSUB_INST,
+ &&VDIV_INST, &&VMOVI_INST, &&VMOVR_INST, &&VABS_INST, &&VNEG_INST, &&VSQRT_INST, &&VCMP_INST, &&VCMP2_INST, &&VCVTBDS_INST,
+ &&VCVTBFF_INST, &&VCVTBFI_INST, &&VMOVBRS_INST, &&VMSR_INST, &&VMOVBRC_INST, &&VMRS_INST, &&VMOVBCR_INST, &&VMOVBRRSS_INST,
+ &&VMOVBRRD_INST, &&VSTR_INST, &&VPUSH_INST, &&VSTM_INST, &&VPOP_INST, &&VLDR_INST, &&VLDM_INST,
+
+ &&SRS_INST,&&RFE_INST,&&BKPT_INST,&&BLX_INST,&&CPS_INST,&&PLD_INST,&&SETEND_INST,&&CLREX_INST,&&REV16_INST,&&USAD8_INST,&&SXTB_INST,
+ &&UXTB_INST,&&SXTH_INST,&&SXTB16_INST,&&UXTH_INST,&&UXTB16_INST,&&CPY_INST,&&UXTAB_INST,&&SSUB8_INST,&&SHSUB8_INST,&&SSUBADDX_INST,
+ &&STREX_INST,&&STREXB_INST,&&SWP_INST,&&SWPB_INST,&&SSUB16_INST,&&SSAT16_INST,&&SHSUBADDX_INST,&&QSUBADDX_INST,&&SHADDSUBX_INST,
+ &&SHADD8_INST,&&SHADD16_INST,&&SEL_INST,&&SADDSUBX_INST,&&SADD8_INST,&&SADD16_INST,&&SHSUB16_INST,&&UMAAL_INST,&&UXTAB16_INST,
+ &&USUBADDX_INST,&&USUB8_INST,&&USUB16_INST,&&USAT16_INST,&&USADA8_INST,&&UQSUBADDX_INST,&&UQSUB8_INST,&&UQSUB16_INST,
+ &&UQADDSUBX_INST,&&UQADD8_INST,&&UQADD16_INST,&&SXTAB_INST,&&UHSUBADDX_INST,&&UHSUB8_INST,&&UHSUB16_INST,&&UHADDSUBX_INST,&&UHADD8_INST,
+ &&UHADD16_INST,&&UADDSUBX_INST,&&UADD8_INST,&&UADD16_INST,&&SXTAH_INST,&&SXTAB16_INST,&&QADD8_INST,&&BXJ_INST,&&CLZ_INST,&&UXTAH_INST,
+ &&BX_INST,&&REV_INST,&&BLX_INST,&&REVSH_INST,&&QADD_INST,&&QADD16_INST,&&QADDSUBX_INST,&&LDREX_INST,&&QDADD_INST,&&QDSUB_INST,
+ &&QSUB_INST,&&LDREXB_INST,&&QSUB8_INST,&&QSUB16_INST,&&SMUAD_INST,&&SMMUL_INST,&&SMUSD_INST,&&SMLSD_INST,&&SMLSLD_INST,&&SMMLA_INST,
+ &&SMMLS_INST,&&SMLALD_INST,&&SMLAD_INST,&&SMLAW_INST,&&SMULW_INST,&&PKHTB_INST,&&PKHBT_INST,&&SMUL_INST,&&SMLALXY_INST,&&SMLA_INST,
+ &&MCRR_INST,&&MRRC_INST,&&CMP_INST,&&TST_INST,&&TEQ_INST,&&CMN_INST,&&SMULL_INST,&&UMULL_INST,&&UMLAL_INST,&&SMLAL_INST,&&MUL_INST,
+ &&MLA_INST,&&SSAT_INST,&&USAT_INST,&&MRS_INST,&&MSR_INST,&&AND_INST,&&BIC_INST,&&LDM_INST,&&EOR_INST,&&ADD_INST,&&RSB_INST,&&RSC_INST,
+ &&SBC_INST,&&ADC_INST,&&SUB_INST,&&ORR_INST,&&MVN_INST,&&MOV_INST,&&STM_INST,&&LDM_INST,&&LDRSH_INST,&&STM_INST,&&LDM_INST,&&LDRSB_INST,
+ &&STRD_INST,&&LDRH_INST,&&STRH_INST,&&LDRD_INST,&&STRT_INST,&&STRBT_INST,&&LDRBT_INST,&&LDRT_INST,&&MRC_INST,&&MCR_INST,&&MSR_INST,
+ &&LDRB_INST,&&STRB_INST,&&LDR_INST,&&LDRCOND_INST, &&STR_INST,&&CDP_INST,&&STC_INST,&&LDC_INST,&&SWI_INST,&&BBL_INST,&&LDREXD_INST,
+ &&STREXD_INST,&&LDREXH_INST,&&STREXH_INST,&&B_2_THUMB, &&B_COND_THUMB,&&BL_1_THUMB, &&BL_2_THUMB, &&BLX_1_THUMB, &&DISPATCH,
+ &&INIT_INST_LENGTH,&&END
+ };
+#endif
+ arm_inst* inst_base;
+ unsigned int addr;
+ unsigned int phys_addr;
+ unsigned int num_instrs = 0;
+
+ int ptr;
+
+ LOAD_NZCVT;
+ DISPATCH:
+ {
+#ifdef inst_debug
+ inst_deb(cpu);
+#endif
+ if (!cpu->NirqSig) {
+ if (!(cpu->Cpsr & 0x80)) {
+ goto END;
+ }
+ }
+ if (cpu->TFlag)
+ cpu->Reg[15] &= 0xfffffffe;
+ else
+ cpu->Reg[15] &= 0xfffffffc;
+
+ phys_addr = cpu->Reg[15];
+
+ if (find_bb(cpu,cpu->Reg[15], ptr) == -1)
+ if (InterpreterTranslate(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION)
+ goto END;
+
+ inst_base = (arm_inst *)&cpu->inst_buf[ptr];
+ GOTO_NEXT_INST;
+ }
+ ADC_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ adc_inst* const inst_cream = (adc_inst*)inst_base->component;
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(RN, SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow);
+
+ if (inst_cream->S && (inst_cream->Rd == 15)) {
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Spsr_copy & 0x1f);
+ LOAD_NZCVT;
+ }
+ } else if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(adc_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(adc_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ ADD_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ add_inst* const inst_cream = (add_inst*)inst_base->component;
+
+ u32 rn_val = RN;
+ if (inst_cream->Rn == 15)
+ {
+ rn_val += 2 * GET_INST_SIZE(cpu);
+ rn_val &= ~0x3;
+ }
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(rn_val, SHIFTER_OPERAND, 0, &carry, &overflow);
+
+ if (inst_cream->S && (inst_cream->Rd == 15)) {
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Cpsr & 0x1f);
+ LOAD_NZCVT;
+ }
+ } else if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(add_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(add_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ AND_INST:
+ {
+ and_inst *inst_cream = (and_inst *)inst_base->component;
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ u32 lop = RN;
+ u32 rop = SHIFTER_OPERAND;
+ RD = lop & rop;
+ if (inst_cream->S && (inst_cream->Rd == 15)) {
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Cpsr & 0x1f);
+ LOAD_NZCVT;
+ }
+ } else if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ UPDATE_CFLAG_WITH_SC;
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(and_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(and_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ BBL_INST:
+ {
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ bbl_inst *inst_cream = (bbl_inst *)inst_base->component;
+ if (inst_cream->L) {
+ LINK_RTN_ADDR;
+ }
+ SET_PC;
+ INC_PC(sizeof(bbl_inst));
+ goto DISPATCH;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(bbl_inst));
+ goto DISPATCH;
+ }
+ BIC_INST:
+ {
+ bic_inst *inst_cream = (bic_inst *)inst_base->component;
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ u32 lop = RN;
+ if (inst_cream->Rn == 15) {
+ lop += 2 * GET_INST_SIZE(cpu);
+ }
+ u32 rop = SHIFTER_OPERAND;
+ RD = lop & (~rop);
+ if ((inst_cream->S) && (inst_cream->Rd == 15)) {
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Spsr_copy & 0x1f);
+ LOAD_NZCVT;
+ }
+ } else if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ UPDATE_CFLAG_WITH_SC;
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(bic_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(bic_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ BKPT_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ bkpt_inst* const inst_cream = (bkpt_inst*)inst_base->component;
+ LOG("Breakpoint instruction hit. Immediate: 0x%08X", inst_cream->imm);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(bkpt_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ BLX_INST:
+ {
+ blx_inst *inst_cream = (blx_inst *)inst_base->component;
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ unsigned int inst = inst_cream->inst;
+ if (BITS(inst, 20, 27) == 0x12 && BITS(inst, 4, 7) == 0x3) {
+ cpu->Reg[14] = (cpu->Reg[15] + GET_INST_SIZE(cpu));
+ if(cpu->TFlag)
+ cpu->Reg[14] |= 0x1;
+ cpu->Reg[15] = cpu->Reg[inst_cream->val.Rm] & 0xfffffffe;
+ cpu->TFlag = cpu->Reg[inst_cream->val.Rm] & 0x1;
+ } else {
+ cpu->Reg[14] = (cpu->Reg[15] + GET_INST_SIZE(cpu));
+ cpu->TFlag = 0x1;
+ int signed_int = inst_cream->val.signed_immed_24;
+ signed_int = (signed_int & 0x800000) ? (0x3F000000 | signed_int) : signed_int;
+ signed_int = signed_int << 2;
+ cpu->Reg[15] = cpu->Reg[15] + 8 + signed_int + (BIT(inst, 24) << 1);
+ }
+ INC_PC(sizeof(blx_inst));
+ goto DISPATCH;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(blx_inst));
+ goto DISPATCH;
+ }
+
+ BX_INST:
+ BXJ_INST:
+ {
+ // Note that only the 'fail' case of BXJ is emulated. This is because
+ // the facilities for Jazelle emulation are not implemented.
+ //
+ // According to the ARM documentation on BXJ, if setting the J bit in the APSR
+ // fails, then BXJ functions identically like a regular BX instruction.
+ //
+ // This is sufficient for citra, as the CPU for the 3DS does not implement Jazelle.
+
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ bx_inst* const inst_cream = (bx_inst*)inst_base->component;
+
+ if (inst_cream->Rm == 15)
+ LOG("BX at pc %x: use of Rm = R15 is discouraged", cpu->Reg[15]);
+
+ cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1;
+ cpu->Reg[15] = cpu->Reg[inst_cream->Rm] & 0xfffffffe;
+ INC_PC(sizeof(bx_inst));
+ goto DISPATCH;
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(bx_inst));
+ goto DISPATCH;
+ }
+
+ CDP_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ // Undefined instruction here
+ cpu->NumInstrsToExecute = 0;
+ return num_instrs;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(cdp_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ CLREX_INST:
+ {
+ remove_exclusive(cpu, 0);
+ cpu->exclusive_state = 0;
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(clrex_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ CLZ_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ clz_inst* inst_cream = (clz_inst*)inst_base->component;
+ RD = clz(RM);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(clz_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ CMN_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ cmn_inst* const inst_cream = (cmn_inst*)inst_base->component;
+
+ bool carry;
+ bool overflow;
+ u32 result = AddWithCarry(RN, SHIFTER_OPERAND, 0, &carry, &overflow);
+
+ UPDATE_NFLAG(result);
+ UPDATE_ZFLAG(result);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(cmn_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ CMP_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ cmp_inst* const inst_cream = (cmp_inst*)inst_base->component;
+
+ u32 rn_val = RN;
+ if (inst_cream->Rn == 15)
+ rn_val += 2 * GET_INST_SIZE(cpu);
+
+ bool carry;
+ bool overflow;
+ u32 result = AddWithCarry(rn_val, ~SHIFTER_OPERAND, 1, &carry, &overflow);
+
+ UPDATE_NFLAG(result);
+ UPDATE_ZFLAG(result);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(cmp_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ CPS_INST:
+ {
+ cps_inst *inst_cream = (cps_inst *)inst_base->component;
+ uint32_t aif_val = 0;
+ uint32_t aif_mask = 0;
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(cps_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ CPY_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ mov_inst* inst_cream = (mov_inst*)inst_base->component;
+
+ RD = SHIFTER_OPERAND;
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(mov_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(mov_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ EOR_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ eor_inst* inst_cream = (eor_inst*)inst_base->component;
+
+ u32 lop = RN;
+ if (inst_cream->Rn == 15) {
+ lop += 2 * GET_INST_SIZE(cpu);
+ }
+ u32 rop = SHIFTER_OPERAND;
+ RD = lop ^ rop;
+ if (inst_cream->S && (inst_cream->Rd == 15)) {
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Spsr_copy & 0x1f);
+ LOAD_NZCVT;
+ }
+ } else if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ UPDATE_CFLAG_WITH_SC;
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(eor_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(eor_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDC_INST:
+ {
+ // Instruction not implemented
+ //LOG_CRITICAL(Core_ARM11, "unimplemented instruction");
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldc_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDM_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 1);
+
+ unsigned int inst = inst_cream->inst;
+ if (BIT(inst, 22) && !BIT(inst, 15)) {
+ for (int i = 0; i < 13; i++) {
+ if(BIT(inst, i)) {
+ cpu->Reg[i] = ARMul_LoadWordN(cpu, addr);
+ addr += 4;
+ }
+ }
+ if (BIT(inst, 13)) {
+ if (cpu->Mode == USER32MODE)
+ cpu->Reg[13] = ARMul_LoadWordN(cpu, addr);
+ else
+ cpu->Reg_usr[0] = ARMul_LoadWordN(cpu, addr);
+
+ addr += 4;
+ }
+ if (BIT(inst, 14)) {
+ if (cpu->Mode == USER32MODE)
+ cpu->Reg[14] = ARMul_LoadWordN(cpu, addr);
+ else
+ cpu->Reg_usr[1] = ARMul_LoadWordN(cpu, addr);
+
+ addr += 4;
+ }
+ } else if (!BIT(inst, 22)) {
+ for(int i = 0; i < 16; i++ ){
+ if(BIT(inst, i)){
+ unsigned int ret = ARMul_LoadWordN(cpu, addr);
+
+ // For armv5t, should enter thumb when bits[0] is non-zero.
+ if(i == 15){
+ cpu->TFlag = ret & 0x1;
+ ret &= 0xFFFFFFFE;
+ }
+
+ cpu->Reg[i] = ret;
+ addr += 4;
+ }
+ }
+ } else if (BIT(inst, 22) && BIT(inst, 15)) {
+ for(int i = 0; i < 15; i++ ){
+ if(BIT(inst, i)){
+ cpu->Reg[i] = ARMul_LoadWordN(cpu, addr);
+ addr += 4;
+ }
+ }
+
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Cpsr & 0x1f);
+ LOAD_NZCVT;
+ }
+
+ cpu->Reg[15] = ARMul_LoadWordN(cpu, addr);
+ }
+
+ if (BIT(inst, 15)) {
+ INC_PC(sizeof(ldst_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ SXTH_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ sxth_inst* inst_cream = (sxth_inst*)inst_base->component;
+
+ unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate);
+ if (BIT(operand2, 15)) {
+ operand2 |= 0xffff0000;
+ } else {
+ operand2 &= 0xffff;
+ }
+ RD = operand2;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(sxth_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDR_INST:
+ {
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 1);
+
+ unsigned int value = ARMul_LoadWordN(cpu, addr);
+ cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
+
+ if (BITS(inst_cream->inst, 12, 15) == 15) {
+ // For armv5t, should enter thumb when bits[0] is non-zero.
+ cpu->TFlag = value & 0x1;
+ cpu->Reg[15] &= 0xFFFFFFFE;
+ INC_PC(sizeof(ldst_inst));
+ goto DISPATCH;
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDRCOND_INST:
+ {
+ if (CondPassed(cpu, inst_base->cond)) {
+ ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 1);
+
+ unsigned int value = ARMul_LoadWordN(cpu, addr);
+ cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
+
+ if (BITS(inst_cream->inst, 12, 15) == 15) {
+ // For armv5t, should enter thumb when bits[0] is non-zero.
+ cpu->TFlag = value & 0x1;
+ cpu->Reg[15] &= 0xFFFFFFFE;
+ INC_PC(sizeof(ldst_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ UXTH_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ uxth_inst* inst_cream = (uxth_inst*)inst_base->component;
+ RD = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xffff;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(uxth_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ UXTAH_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ uxtah_inst* inst_cream = (uxtah_inst*)inst_base->component;
+ unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xffff;
+
+ RD = RN + operand2;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(uxtah_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDRB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 1);
+
+ cpu->Reg[BITS(inst_cream->inst, 12, 15)] = ARMul_LoadByte(cpu,addr);
+
+ if (BITS(inst_cream->inst, 12, 15) == 15) {
+ INC_PC(sizeof(ldst_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDRBT_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 1);
+
+ cpu->Reg[BITS(inst_cream->inst, 12, 15)] = ARMul_LoadByte(cpu,addr);
+
+ if (BITS(inst_cream->inst, 12, 15) == 15) {
+ INC_PC(sizeof(ldst_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDRD_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ // Should check if RD is even-numbered, Rd != 14, addr[0:1] == 0, (CP15_reg1_U == 1 || addr[2] == 0)
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 1);
+
+ // The 3DS doesn't have LPAE (Large Physical Access Extension), so it
+ // wouldn't do this as a single read.
+ cpu->Reg[BITS(inst_cream->inst, 12, 15) + 0] = ARMul_LoadWordN(cpu, addr);
+ cpu->Reg[BITS(inst_cream->inst, 12, 15) + 1] = ARMul_LoadWordN(cpu, addr + 4);
+
+ // No dispatch since this operation should not modify R15
+ }
+ cpu->Reg[15] += 4;
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ LDREX_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
+ unsigned int read_addr = RN;
+
+ add_exclusive_addr(cpu, read_addr);
+ cpu->exclusive_state = 1;
+
+ RD = ARMul_LoadWordN(cpu, read_addr);
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(generic_arm_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDREXB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
+ unsigned int read_addr = RN;
+
+ add_exclusive_addr(cpu, read_addr);
+ cpu->exclusive_state = 1;
+
+ RD = ARMul_LoadByte(cpu, read_addr);
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(generic_arm_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDREXH_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
+ unsigned int read_addr = RN;
+
+ add_exclusive_addr(cpu, read_addr);
+ cpu->exclusive_state = 1;
+
+ RD = ARMul_LoadHalfWord(cpu, read_addr);
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(generic_arm_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDREXD_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
+ unsigned int read_addr = RN;
+
+ add_exclusive_addr(cpu, read_addr);
+ cpu->exclusive_state = 1;
+
+ RD = ARMul_LoadWordN(cpu, read_addr);
+ RD2 = ARMul_LoadWordN(cpu, read_addr + 4);
+
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(generic_arm_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDRH_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 1);
+
+ cpu->Reg[BITS(inst_cream->inst, 12, 15)] = ARMul_LoadHalfWord(cpu, addr);
+ if (BITS(inst_cream->inst, 12, 15) == 15) {
+ INC_PC(sizeof(ldst_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDRSB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 1);
+ unsigned int value = ARMul_LoadByte(cpu, addr);
+ if (BIT(value, 7)) {
+ value |= 0xffffff00;
+ }
+ cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
+ if (BITS(inst_cream->inst, 12, 15) == 15) {
+ INC_PC(sizeof(ldst_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDRSH_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 1);
+
+ unsigned int value = ARMul_LoadHalfWord(cpu, addr);
+ if (BIT(value, 15)) {
+ value |= 0xffff0000;
+ }
+ cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
+ if (BITS(inst_cream->inst, 12, 15) == 15) {
+ INC_PC(sizeof(ldst_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ LDRT_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 1);
+
+ unsigned int value = ARMul_LoadWordN(cpu, addr);
+ cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
+
+ if (BITS(inst_cream->inst, 12, 15) == 15) {
+ INC_PC(sizeof(ldst_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ MCR_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ mcr_inst* inst_cream = (mcr_inst*)inst_base->component;
+
+ unsigned int inst = inst_cream->inst;
+ if (inst_cream->Rd == 15) {
+ DEBUG_MSG;
+ } else {
+ if (inst_cream->cp_num == 15)
+ WriteCP15Register(cpu, RD, CRn, OPCODE_1, CRm, OPCODE_2);
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(mcr_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ MCRR_INST:
+ MLA_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ mla_inst* inst_cream = (mla_inst*)inst_base->component;
+
+ uint64_t rm = RM;
+ uint64_t rs = RS;
+ uint64_t rn = RN;
+
+ RD = static_cast<uint32_t>((rm * rs + rn) & 0xffffffff);
+ if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(mla_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(mla_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ MOV_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ mov_inst* inst_cream = (mov_inst*)inst_base->component;
+
+ RD = SHIFTER_OPERAND;
+ if (inst_cream->S && (inst_cream->Rd == 15)) {
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Spsr_copy & 0x1f);
+ LOAD_NZCVT;
+ }
+ } else if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ UPDATE_CFLAG_WITH_SC;
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(mov_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(mov_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ MRC_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ mrc_inst* inst_cream = (mrc_inst*)inst_base->component;
+
+ unsigned int inst = inst_cream->inst;
+ if (inst_cream->Rd == 15) {
+ DEBUG_MSG;
+ }
+ if (inst_cream->inst == 0xeef04a10) {
+ // Undefined instruction fmrx
+ RD = 0x20000000;
+ CITRA_IGNORE_EXIT(-1);
+ goto END;
+ } else {
+ if (inst_cream->cp_num == 15)
+ RD = ReadCP15Register(cpu, CRn, OPCODE_1, CRm, OPCODE_2);
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(mrc_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ MRRC_INST:
+ MRS_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ mrs_inst* inst_cream = (mrs_inst*)inst_base->component;
+
+ if (inst_cream->R) {
+ RD = cpu->Spsr_copy;
+ } else {
+ SAVE_NZCVT;
+ RD = cpu->Cpsr;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(mrs_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ MSR_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ msr_inst* inst_cream = (msr_inst*)inst_base->component;
+ const uint32_t UnallocMask = 0x06f0fc00, UserMask = 0xf80f0200, PrivMask = 0x000001df, StateMask = 0x01000020;
+ unsigned int inst = inst_cream->inst;
+ unsigned int operand;
+
+ if (BIT(inst, 25)) {
+ int rot_imm = BITS(inst, 8, 11) * 2;
+ operand = ROTATE_RIGHT_32(BITS(inst, 0, 7), rot_imm);
+ } else {
+ operand = cpu->Reg[BITS(inst, 0, 3)];
+ }
+ uint32_t byte_mask = (BIT(inst, 16) ? 0xff : 0) | (BIT(inst, 17) ? 0xff00 : 0)
+ | (BIT(inst, 18) ? 0xff0000 : 0) | (BIT(inst, 19) ? 0xff000000 : 0);
+ uint32_t mask = 0;
+ if (!inst_cream->R) {
+ mask = byte_mask & UserMask;
+ SAVE_NZCVT;
+
+ cpu->Cpsr = (cpu->Cpsr & ~mask) | (operand & mask);
+ switch_mode(cpu, cpu->Cpsr & 0x1f);
+ LOAD_NZCVT;
+ } else {
+ if (CurrentModeHasSPSR) {
+ mask = byte_mask & (UserMask | PrivMask | StateMask);
+ cpu->Spsr_copy = (cpu->Spsr_copy & ~mask) | (operand & mask);
+ }
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(msr_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ MUL_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ mul_inst* inst_cream = (mul_inst*)inst_base->component;
+
+ uint64_t rm = RM;
+ uint64_t rs = RS;
+ RD = static_cast<uint32_t>((rm * rs) & 0xffffffff);
+ if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(mul_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(mul_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ MVN_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ mvn_inst* const inst_cream = (mvn_inst*)inst_base->component;
+
+ RD = ~SHIFTER_OPERAND;
+
+ if (inst_cream->S && (inst_cream->Rd == 15)) {
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Spsr_copy & 0x1f);
+ LOAD_NZCVT;
+ }
+ } else if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ UPDATE_CFLAG_WITH_SC;
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(mvn_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(mvn_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ ORR_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ orr_inst* const inst_cream = (orr_inst*)inst_base->component;
+
+ u32 lop = RN;
+ u32 rop = SHIFTER_OPERAND;
+ RD = lop | rop;
+
+ if (inst_cream->S && (inst_cream->Rd == 15)) {
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Spsr_copy & 0x1f);
+ LOAD_NZCVT;
+ }
+ } else if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ UPDATE_CFLAG_WITH_SC;
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(orr_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(orr_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ PKHBT_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ pkh_inst *inst_cream = (pkh_inst *)inst_base->component;
+ RD = (RN & 0xFFFF) | ((RM << inst_cream->imm) & 0xFFFF0000);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(pkh_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ PKHTB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ pkh_inst *inst_cream = (pkh_inst *)inst_base->component;
+ int shift_imm = inst_cream->imm ? inst_cream->imm : 31;
+ RD = ((static_cast<s32>(RM) >> shift_imm) & 0xFFFF) | (RN & 0xFFFF0000);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(pkh_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ PLD_INST:
+ {
+ // Not implemented. PLD is a hint instruction, so it's optional.
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(pld_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ QADD_INST:
+ QDADD_INST:
+ QDSUB_INST:
+ QSUB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+ const u8 op1 = inst_cream->op1;
+ const u32 rm_val = RM;
+ const u32 rn_val = RN;
+
+ u32 result = 0;
+
+ // QADD
+ if (op1 == 0x00) {
+ result = rm_val + rn_val;
+
+ if (AddOverflow(rm_val, rn_val, result)) {
+ result = POS(result) ? 0x80000000 : 0x7FFFFFFF;
+ cpu->Cpsr |= (1 << 27);
+ }
+ }
+ // QSUB
+ else if (op1 == 0x01) {
+ result = rm_val - rn_val;
+
+ if (SubOverflow(rm_val, rn_val, result)) {
+ result = POS(result) ? 0x80000000 : 0x7FFFFFFF;
+ cpu->Cpsr |= (1 << 27);
+ }
+ }
+ // QDADD
+ else if (op1 == 0x02) {
+ u32 mul = (rn_val * 2);
+
+ if (AddOverflow(rn_val, rn_val, rn_val * 2)) {
+ mul = POS(mul) ? 0x80000000 : 0x7FFFFFFF;
+ cpu->Cpsr |= (1 << 27);
+ }
+
+ result = mul + rm_val;
+
+ if (AddOverflow(rm_val, mul, result)) {
+ result = POS(result) ? 0x80000000 : 0x7FFFFFFF;
+ cpu->Cpsr |= (1 << 27);
+ }
+ }
+ // QDSUB
+ else if (op1 == 0x03) {
+ u32 mul = (rn_val * 2);
+
+ if (AddOverflow(rn_val, rn_val, mul)) {
+ mul = POS(mul) ? 0x80000000 : 0x7FFFFFFF;
+ cpu->Cpsr |= (1 << 27);
+ }
+
+ result = rm_val - mul;
+
+ if (SubOverflow(rm_val, mul, result)) {
+ result = POS(result) ? 0x80000000 : 0x7FFFFFFF;
+ cpu->Cpsr |= (1 << 27);
+ }
+ }
+
+ RD = result;
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ QADD8_INST:
+ QADD16_INST:
+ QADDSUBX_INST:
+ QSUB8_INST:
+ QSUB16_INST:
+ QSUBADDX_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+ const u16 rm_lo = (RM & 0xFFFF);
+ const u16 rm_hi = ((RM >> 16) & 0xFFFF);
+ const u16 rn_lo = (RN & 0xFFFF);
+ const u16 rn_hi = ((RN >> 16) & 0xFFFF);
+ const u8 op2 = inst_cream->op2;
+
+ u16 lo_result = 0;
+ u16 hi_result = 0;
+
+ // QADD16
+ if (op2 == 0x00) {
+ lo_result = ARMul_SignedSaturatedAdd16(rn_lo, rm_lo);
+ hi_result = ARMul_SignedSaturatedAdd16(rn_hi, rm_hi);
+ }
+ // QASX
+ else if (op2 == 0x01) {
+ lo_result = ARMul_SignedSaturatedSub16(rn_lo, rm_hi);
+ hi_result = ARMul_SignedSaturatedAdd16(rn_hi, rm_lo);
+ }
+ // QSAX
+ else if (op2 == 0x02) {
+ lo_result = ARMul_SignedSaturatedAdd16(rn_lo, rm_hi);
+ hi_result = ARMul_SignedSaturatedSub16(rn_hi, rm_lo);
+ }
+ // QSUB16
+ else if (op2 == 0x03) {
+ lo_result = ARMul_SignedSaturatedSub16(rn_lo, rm_lo);
+ hi_result = ARMul_SignedSaturatedSub16(rn_hi, rm_hi);
+ }
+ // QADD8
+ else if (op2 == 0x04) {
+ lo_result = ARMul_SignedSaturatedAdd8(rn_lo & 0xFF, rm_lo & 0xFF) |
+ ARMul_SignedSaturatedAdd8(rn_lo >> 8, rm_lo >> 8) << 8;
+ hi_result = ARMul_SignedSaturatedAdd8(rn_hi & 0xFF, rm_hi & 0xFF) |
+ ARMul_SignedSaturatedAdd8(rn_hi >> 8, rm_hi >> 8) << 8;
+ }
+ // QSUB8
+ else if (op2 == 0x07) {
+ lo_result = ARMul_SignedSaturatedSub8(rn_lo & 0xFF, rm_lo & 0xFF) |
+ ARMul_SignedSaturatedSub8(rn_lo >> 8, rm_lo >> 8) << 8;
+ hi_result = ARMul_SignedSaturatedSub8(rn_hi & 0xFF, rm_hi & 0xFF) |
+ ARMul_SignedSaturatedSub8(rn_hi >> 8, rm_hi >> 8) << 8;
+ }
+
+ RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ REV_INST:
+ REV16_INST:
+ REVSH_INST:
+ {
+
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ rev_inst* const inst_cream = (rev_inst*)inst_base->component;
+
+ const u8 op1 = inst_cream->op1;
+ const u8 op2 = inst_cream->op2;
+
+ // REV
+ if (op1 == 0x03 && op2 == 0x01) {
+ RD = ((RM & 0xFF) << 24) | (((RM >> 8) & 0xFF) << 16) | (((RM >> 16) & 0xFF) << 8) | ((RM >> 24) & 0xFF);
+ }
+ // REV16
+ else if (op1 == 0x03 && op2 == 0x05) {
+ RD = ((RM & 0xFF) << 8) | ((RM & 0xFF00) >> 8) | ((RM & 0xFF0000) << 8) | ((RM & 0xFF000000) >> 8);
+ }
+ // REVSH
+ else if (op1 == 0x07 && op2 == 0x05) {
+ RD = ((RM & 0xFF) << 8) | ((RM & 0xFF00) >> 8);
+ if (RD & 0x8000)
+ RD |= 0xffff0000;
+ }
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(rev_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ RFE_INST:
+ {
+ // RFE is unconditional
+ ldst_inst* const inst_cream = (ldst_inst*)inst_base->component;
+
+ u32 address = 0;
+ inst_cream->get_addr(cpu, inst_cream->inst, address, 1);
+
+ cpu->Cpsr = ARMul_LoadWordN(cpu, address);
+ cpu->Reg[15] = ARMul_LoadWordN(cpu, address + 4);
+
+ INC_PC(sizeof(ldst_inst));
+ goto DISPATCH;
+ }
+
+ RSB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ rsb_inst* const inst_cream = (rsb_inst*)inst_base->component;
+
+ u32 rn_val = RN;
+ if (inst_cream->Rn == 15)
+ rn_val += 2 * GET_INST_SIZE(cpu);
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(~rn_val, SHIFTER_OPERAND, 1, &carry, &overflow);
+
+ if (inst_cream->S && (inst_cream->Rd == 15)) {
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Spsr_copy & 0x1f);
+ LOAD_NZCVT;
+ }
+ } else if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(rsb_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(rsb_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ RSC_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ rsc_inst* const inst_cream = (rsc_inst*)inst_base->component;
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(~RN, SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow);
+
+ if (inst_cream->S && (inst_cream->Rd == 15)) {
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Spsr_copy & 0x1f);
+ LOAD_NZCVT;
+ }
+ } else if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(rsc_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(rsc_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SADD8_INST:
+ SSUB8_INST:
+ SADD16_INST:
+ SADDSUBX_INST:
+ SSUBADDX_INST:
+ SSUB16_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+ const u8 op2 = inst_cream->op2;
+
+ if (op2 == 0x00 || op2 == 0x01 || op2 == 0x02 || op2 == 0x03) {
+ const s16 rn_lo = (RN & 0xFFFF);
+ const s16 rn_hi = ((RN >> 16) & 0xFFFF);
+ const s16 rm_lo = (RM & 0xFFFF);
+ const s16 rm_hi = ((RM >> 16) & 0xFFFF);
+
+ s32 lo_result = 0;
+ s32 hi_result = 0;
+
+ // SADD16
+ if (inst_cream->op2 == 0x00) {
+ lo_result = (rn_lo + rm_lo);
+ hi_result = (rn_hi + rm_hi);
+ }
+ // SASX
+ else if (op2 == 0x01) {
+ lo_result = (rn_lo - rm_hi);
+ hi_result = (rn_hi + rm_lo);
+ }
+ // SSAX
+ else if (op2 == 0x02) {
+ lo_result = (rn_lo + rm_hi);
+ hi_result = (rn_hi - rm_lo);
+ }
+ // SSUB16
+ else if (op2 == 0x03) {
+ lo_result = (rn_lo - rm_lo);
+ hi_result = (rn_hi - rm_hi);
+ }
+
+ RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
+
+ if (lo_result >= 0) {
+ cpu->Cpsr |= (1 << 16);
+ cpu->Cpsr |= (1 << 17);
+ } else {
+ cpu->Cpsr &= ~(1 << 16);
+ cpu->Cpsr &= ~(1 << 17);
+ }
+
+ if (hi_result >= 0) {
+ cpu->Cpsr |= (1 << 18);
+ cpu->Cpsr |= (1 << 19);
+ } else {
+ cpu->Cpsr &= ~(1 << 18);
+ cpu->Cpsr &= ~(1 << 19);
+ }
+ }
+ else if (op2 == 0x04 || op2 == 0x07) {
+ s32 lo_val1, lo_val2;
+ s32 hi_val1, hi_val2;
+
+ // SADD8
+ if (op2 == 0x04) {
+ lo_val1 = (s32)(s8)(RN & 0xFF) + (s32)(s8)(RM & 0xFF);
+ lo_val2 = (s32)(s8)((RN >> 8) & 0xFF) + (s32)(s8)((RM >> 8) & 0xFF);
+ hi_val1 = (s32)(s8)((RN >> 16) & 0xFF) + (s32)(s8)((RM >> 16) & 0xFF);
+ hi_val2 = (s32)(s8)((RN >> 24) & 0xFF) + (s32)(s8)((RM >> 24) & 0xFF);
+ }
+ // SSUB8
+ else {
+ lo_val1 = (s32)(s8)(RN & 0xFF) - (s32)(s8)(RM & 0xFF);
+ lo_val2 = (s32)(s8)((RN >> 8) & 0xFF) - (s32)(s8)((RM >> 8) & 0xFF);
+ hi_val1 = (s32)(s8)((RN >> 16) & 0xFF) - (s32)(s8)((RM >> 16) & 0xFF);
+ hi_val2 = (s32)(s8)((RN >> 24) & 0xFF) - (s32)(s8)((RM >> 24) & 0xFF);
+ }
+
+ RD = ((lo_val1 & 0xFF) | ((lo_val2 & 0xFF) << 8) | ((hi_val1 & 0xFF) << 16) | ((hi_val2 & 0xFF) << 24));
+
+ if (lo_val1 >= 0)
+ cpu->Cpsr |= (1 << 16);
+ else
+ cpu->Cpsr &= ~(1 << 16);
+
+ if (lo_val2 >= 0)
+ cpu->Cpsr |= (1 << 17);
+ else
+ cpu->Cpsr &= ~(1 << 17);
+
+ if (hi_val1 >= 0)
+ cpu->Cpsr |= (1 << 18);
+ else
+ cpu->Cpsr &= ~(1 << 18);
+
+ if (hi_val2 >= 0)
+ cpu->Cpsr |= (1 << 19);
+ else
+ cpu->Cpsr &= ~(1 << 19);
+ }
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SBC_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ sbc_inst* const inst_cream = (sbc_inst*)inst_base->component;
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(RN, ~SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow);
+
+ if (inst_cream->S && (inst_cream->Rd == 15)) {
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Spsr_copy & 0x1f);
+ LOAD_NZCVT;
+ }
+ } else if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(sbc_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(sbc_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SEL_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ const u32 to = RM;
+ const u32 from = RN;
+ const u32 cpsr = cpu->Cpsr;
+
+ u32 result;
+ if (cpsr & (1 << 16))
+ result = from & 0xff;
+ else
+ result = to & 0xff;
+
+ if (cpsr & (1 << 17))
+ result |= from & 0x0000ff00;
+ else
+ result |= to & 0x0000ff00;
+
+ if (cpsr & (1 << 18))
+ result |= from & 0x00ff0000;
+ else
+ result |= to & 0x00ff0000;
+
+ if (cpsr & (1 << 19))
+ result |= from & 0xff000000;
+ else
+ result |= to & 0xff000000;
+
+ RD = result;
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SETEND_INST:
+ {
+ // SETEND is unconditional
+ setend_inst* const inst_cream = (setend_inst*)inst_base->component;
+ const bool big_endian = (inst_cream->set_bigend == 1);
+
+ if (big_endian)
+ cpu->Cpsr |= (1 << 9);
+ else
+ cpu->Cpsr &= ~(1 << 9);
+
+ LOG("SETEND %s executed", big_endian ? "BE" : "LE");
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(setend_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SHADD8_INST:
+ SHADD16_INST:
+ SHADDSUBX_INST:
+ SHSUB8_INST:
+ SHSUB16_INST:
+ SHSUBADDX_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ const u8 op2 = inst_cream->op2;
+ const u32 rm_val = RM;
+ const u32 rn_val = RN;
+
+ if (op2 == 0x00 || op2 == 0x01 || op2 == 0x02 || op2 == 0x03) {
+ s32 lo_result = 0;
+ s32 hi_result = 0;
+
+ // SHADD16
+ if (op2 == 0x00) {
+ lo_result = ((s16)(rn_val & 0xFFFF) + (s16)(rm_val & 0xFFFF)) >> 1;
+ hi_result = ((s16)((rn_val >> 16) & 0xFFFF) + (s16)((rm_val >> 16) & 0xFFFF)) >> 1;
+ }
+ // SHASX
+ else if (op2 == 0x01) {
+ lo_result = ((s16)(rn_val & 0xFFFF) - (s16)((rm_val >> 16) & 0xFFFF)) >> 1;
+ hi_result = ((s16)((rn_val >> 16) & 0xFFFF) + (s16)(rm_val & 0xFFFF)) >> 1;
+ }
+ // SHSAX
+ else if (op2 == 0x02) {
+ lo_result = ((s16)(rn_val & 0xFFFF) + (s16)((rm_val >> 16) & 0xFFFF)) >> 1;
+ hi_result = ((s16)((rn_val >> 16) & 0xFFFF) - (s16)(rm_val & 0xFFFF)) >> 1;
+ }
+ // SHSUB16
+ else if (op2 == 0x03) {
+ lo_result = ((s16)(rn_val & 0xFFFF) - (s16)(rm_val & 0xFFFF)) >> 1;
+ hi_result = ((s16)((rn_val >> 16) & 0xFFFF) - (s16)((rm_val >> 16) & 0xFFFF)) >> 1;
+ }
+
+ RD = ((lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16));
+ }
+ else if (op2 == 0x04 || op2 == 0x07) {
+ s16 lo_val1, lo_val2;
+ s16 hi_val1, hi_val2;
+
+ // SHADD8
+ if (op2 == 0x04) {
+ lo_val1 = ((s8)(rn_val & 0xFF) + (s8)(rm_val & 0xFF)) >> 1;
+ lo_val2 = ((s8)((rn_val >> 8) & 0xFF) + (s8)((rm_val >> 8) & 0xFF)) >> 1;
+
+ hi_val1 = ((s8)((rn_val >> 16) & 0xFF) + (s8)((rm_val >> 16) & 0xFF)) >> 1;
+ hi_val2 = ((s8)((rn_val >> 24) & 0xFF) + (s8)((rm_val >> 24) & 0xFF)) >> 1;
+ }
+ // SHSUB8
+ else {
+ lo_val1 = ((s8)(rn_val & 0xFF) - (s8)(rm_val & 0xFF)) >> 1;
+ lo_val2 = ((s8)((rn_val >> 8) & 0xFF) - (s8)((rm_val >> 8) & 0xFF)) >> 1;
+
+ hi_val1 = ((s8)((rn_val >> 16) & 0xFF) - (s8)((rm_val >> 16) & 0xFF)) >> 1;
+ hi_val2 = ((s8)((rn_val >> 24) & 0xFF) - (s8)((rm_val >> 24) & 0xFF)) >> 1;
+ }
+
+ RD = (lo_val1 & 0xFF) | ((lo_val2 & 0xFF) << 8) | ((hi_val1 & 0xFF) << 16) | ((hi_val2 & 0xFF) << 24);
+ }
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SMLA_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smla_inst* inst_cream = (smla_inst*)inst_base->component;
+ int32_t operand1, operand2;
+ if (inst_cream->x == 0)
+ operand1 = (BIT(RM, 15)) ? (BITS(RM, 0, 15) | 0xffff0000) : BITS(RM, 0, 15);
+ else
+ operand1 = (BIT(RM, 31)) ? (BITS(RM, 16, 31) | 0xffff0000) : BITS(RM, 16, 31);
+
+ if (inst_cream->y == 0)
+ operand2 = (BIT(RS, 15)) ? (BITS(RS, 0, 15) | 0xffff0000) : BITS(RS, 0, 15);
+ else
+ operand2 = (BIT(RS, 31)) ? (BITS(RS, 16, 31) | 0xffff0000) : BITS(RS, 16, 31);
+ RD = operand1 * operand2 + RN;
+
+ if (AddOverflow(operand1 * operand2, RN, RD))
+ cpu->Cpsr |= (1 << 27);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(smla_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SMLAD_INST:
+ SMLSD_INST:
+ SMUAD_INST:
+ SMUSD_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
+ const u8 op2 = inst_cream->op2;
+
+ u32 rm_val = cpu->Reg[inst_cream->Rm];
+ const u32 rn_val = cpu->Reg[inst_cream->Rn];
+
+ if (inst_cream->m)
+ rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16));
+
+ const s16 rm_lo = (rm_val & 0xFFFF);
+ const s16 rm_hi = ((rm_val >> 16) & 0xFFFF);
+ const s16 rn_lo = (rn_val & 0xFFFF);
+ const s16 rn_hi = ((rn_val >> 16) & 0xFFFF);
+
+ const u32 product1 = (rn_lo * rm_lo);
+ const u32 product2 = (rn_hi * rm_hi);
+
+ // SMUAD and SMLAD
+ if (BIT(op2, 1) == 0) {
+ RD = (product1 + product2);
+
+ if (inst_cream->Ra != 15) {
+ RD += cpu->Reg[inst_cream->Ra];
+
+ if (ARMul_AddOverflowQ(product1 + product2, cpu->Reg[inst_cream->Ra]))
+ cpu->Cpsr |= (1 << 27);
+ }
+
+ if (ARMul_AddOverflowQ(product1, product2))
+ cpu->Cpsr |= (1 << 27);
+ }
+ // SMUSD and SMLSD
+ else {
+ RD = (product1 - product2);
+
+ if (inst_cream->Ra != 15) {
+ RD += cpu->Reg[inst_cream->Ra];
+
+ if (ARMul_AddOverflowQ(product1 - product2, cpu->Reg[inst_cream->Ra]))
+ cpu->Cpsr |= (1 << 27);
+ }
+ }
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(smlad_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SMLAL_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ umlal_inst* inst_cream = (umlal_inst*)inst_base->component;
+ long long int rm = RM;
+ long long int rs = RS;
+ if (BIT(rm, 31)) {
+ rm |= 0xffffffff00000000LL;
+ }
+ if (BIT(rs, 31)) {
+ rs |= 0xffffffff00000000LL;
+ }
+ long long int rst = rm * rs;
+ long long int rdhi32 = RDHI;
+ long long int hilo = (rdhi32 << 32) + RDLO;
+ rst += hilo;
+ RDLO = BITS(rst, 0, 31);
+ RDHI = BITS(rst, 32, 63);
+ if (inst_cream->S) {
+ cpu->NFlag = BIT(RDHI, 31);
+ cpu->ZFlag = (RDHI == 0 && RDLO == 0);
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(umlal_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SMLALXY_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component;
+
+ u64 operand1 = RN;
+ u64 operand2 = RM;
+
+ if (inst_cream->x != 0)
+ operand1 >>= 16;
+ if (inst_cream->y != 0)
+ operand2 >>= 16;
+ operand1 &= 0xFFFF;
+ if (operand1 & 0x8000)
+ operand1 -= 65536;
+ operand2 &= 0xFFFF;
+ if (operand2 & 0x8000)
+ operand2 -= 65536;
+
+ u64 dest = ((u64)RDHI << 32 | RDLO) + (operand1 * operand2);
+ RDLO = (dest & 0xFFFFFFFF);
+ RDHI = ((dest >> 32) & 0xFFFFFFFF);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(smlalxy_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SMLAW_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
+
+ const u32 rm_val = RM;
+ const u32 rn_val = RN;
+ const u32 ra_val = cpu->Reg[inst_cream->Ra];
+ const bool high = (inst_cream->m == 1);
+
+ const s16 operand2 = (high) ? ((rm_val >> 16) & 0xFFFF) : (rm_val & 0xFFFF);
+ const s64 result = (s64)(s32)rn_val * (s64)(s32)operand2 + ((s64)(s32)ra_val << 16);
+
+ RD = (result & (0xFFFFFFFFFFFFFFFFLL >> 15)) >> 16;
+
+ if ((result >> 16) != (s32)RD)
+ cpu->Cpsr |= (1 << 27);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(smlad_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SMLALD_INST:
+ SMLSLD_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlald_inst* const inst_cream = (smlald_inst*)inst_base->component;
+
+ const bool do_swap = (inst_cream->swap == 1);
+ const u32 rdlo_val = RDLO;
+ const u32 rdhi_val = RDHI;
+ const u32 rn_val = RN;
+ u32 rm_val = RM;
+
+ if (do_swap)
+ rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16));
+
+ const s32 product1 = (s16)(rn_val & 0xFFFF) * (s16)(rm_val & 0xFFFF);
+ const s32 product2 = (s16)((rn_val >> 16) & 0xFFFF) * (s16)((rm_val >> 16) & 0xFFFF);
+ s64 result;
+
+ // SMLALD
+ if (BIT(inst_cream->op2, 1) == 0) {
+ result = (product1 + product2) + (s64)(rdlo_val | ((s64)rdhi_val << 32));
+ }
+ // SMLSLD
+ else {
+ result = (product1 - product2) + (s64)(rdlo_val | ((s64)rdhi_val << 32));
+ }
+
+ RDLO = (result & 0xFFFFFFFF);
+ RDHI = ((result >> 32) & 0xFFFFFFFF);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(smlald_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SMMLA_INST:
+ SMMLS_INST:
+ SMMUL_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
+
+ const u32 rm_val = RM;
+ const u32 rn_val = RN;
+ const bool do_round = (inst_cream->m == 1);
+
+ // Assume SMMUL by default.
+ s64 result = (s64)(s32)rn_val * (s64)(s32)rm_val;
+
+ if (inst_cream->Ra != 15) {
+ const u32 ra_val = cpu->Reg[inst_cream->Ra];
+
+ // SMMLA, otherwise SMMLS
+ if (BIT(inst_cream->op2, 1) == 0)
+ result += ((s64)ra_val << 32);
+ else
+ result = ((s64)ra_val << 32) - result;
+ }
+
+ if (do_round)
+ result += 0x80000000;
+
+ RD = ((result >> 32) & 0xFFFFFFFF);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(smlad_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SMUL_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smul_inst* inst_cream = (smul_inst*)inst_base->component;
+ uint32_t operand1, operand2;
+ if (inst_cream->x == 0)
+ operand1 = (BIT(RM, 15)) ? (BITS(RM, 0, 15) | 0xffff0000) : BITS(RM, 0, 15);
+ else
+ operand1 = (BIT(RM, 31)) ? (BITS(RM, 16, 31) | 0xffff0000) : BITS(RM, 16, 31);
+
+ if (inst_cream->y == 0)
+ operand2 = (BIT(RS, 15)) ? (BITS(RS, 0, 15) | 0xffff0000) : BITS(RS, 0, 15);
+ else
+ operand2 = (BIT(RS, 31)) ? (BITS(RS, 16, 31) | 0xffff0000) : BITS(RS, 16, 31);
+ RD = operand1 * operand2;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(smul_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ SMULL_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ umull_inst* inst_cream = (umull_inst*)inst_base->component;
+ int64_t rm = RM;
+ int64_t rs = RS;
+ if (BIT(rm, 31)) {
+ rm |= 0xffffffff00000000LL;
+ }
+ if (BIT(rs, 31)) {
+ rs |= 0xffffffff00000000LL;
+ }
+ int64_t rst = rm * rs;
+ RDHI = BITS(rst, 32, 63);
+ RDLO = BITS(rst, 0, 31);
+
+ if (inst_cream->S) {
+ cpu->NFlag = BIT(RDHI, 31);
+ cpu->ZFlag = (RDHI == 0 && RDLO == 0);
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(umull_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SMULW_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
+
+ s16 rm = (inst_cream->m == 1) ? ((RM >> 16) & 0xFFFF) : (RM & 0xFFFF);
+
+ s64 result = (s64)rm * (s64)(s32)RN;
+ RD = BITS(result, 16, 47);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(smlad_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SRS_INST:
+ {
+ // SRS is unconditional
+ ldst_inst* const inst_cream = (ldst_inst*)inst_base->component;
+
+ u32 address = 0;
+ inst_cream->get_addr(cpu, inst_cream->inst, address, 1);
+
+ ARMul_StoreWordN(cpu, address + 0, cpu->Reg[14]);
+ ARMul_StoreWordN(cpu, address + 4, cpu->Spsr_copy);
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SSAT_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ssat_inst* const inst_cream = (ssat_inst*)inst_base->component;
+
+ u8 shift_type = inst_cream->shift_type;
+ u8 shift_amount = inst_cream->imm5;
+ u32 rn_val = RN;
+
+ // 32-bit ASR is encoded as an amount of 0.
+ if (shift_type == 1 && shift_amount == 0)
+ shift_amount = 31;
+
+ if (shift_type == 0)
+ rn_val <<= shift_amount;
+ else if (shift_type == 1)
+ rn_val = ((s32)rn_val >> shift_amount);
+
+ bool saturated = false;
+ rn_val = ARMul_SignedSatQ(rn_val, inst_cream->sat_imm, &saturated);
+
+ if (saturated)
+ cpu->Cpsr |= (1 << 27);
+
+ RD = rn_val;
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ssat_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SSAT16_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ssat_inst* const inst_cream = (ssat_inst*)inst_base->component;
+ const u8 saturate_to = inst_cream->sat_imm;
+
+ bool sat1 = false;
+ bool sat2 = false;
+
+ RD = (ARMul_SignedSatQ((s16)RN, saturate_to, &sat1) & 0xFFFF) |
+ ARMul_SignedSatQ((s32)RN >> 16, saturate_to, &sat2) << 16;
+
+ if (sat1 || sat2)
+ cpu->Cpsr |= (1 << 27);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ssat_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ STC_INST:
+ {
+ // Instruction not implemented
+ //LOG_CRITICAL(Core_ARM11, "unimplemented instruction");
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(stc_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ STM_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ unsigned int inst = inst_cream->inst;
+
+ unsigned int Rn = BITS(inst, 16, 19);
+ unsigned int old_RN = cpu->Reg[Rn];
+
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 0);
+ if (BIT(inst_cream->inst, 22) == 1) {
+ for (int i = 0; i < 13; i++) {
+ if (BIT(inst_cream->inst, i)) {
+ ARMul_StoreWordN(cpu, addr, cpu->Reg[i]);
+ addr += 4;
+ }
+ }
+ if (BIT(inst_cream->inst, 13)) {
+ if (cpu->Mode == USER32MODE)
+ ARMul_StoreWordN(cpu, addr, cpu->Reg[13]);
+ else
+ ARMul_StoreWordN(cpu, addr, cpu->Reg_usr[0]);
+
+ addr += 4;
+ }
+ if (BIT(inst_cream->inst, 14)) {
+ if (cpu->Mode == USER32MODE)
+ ARMul_StoreWordN(cpu, addr, cpu->Reg[14]);
+ else
+ ARMul_StoreWordN(cpu, addr, cpu->Reg_usr[1]);
+
+ addr += 4;
+ }
+ if (BIT(inst_cream->inst, 15)) {
+ ARMul_StoreWordN(cpu, addr, cpu->Reg_usr[1] + 8);
+ }
+ } else {
+ for (int i = 0; i < 15; i++) {
+ if (BIT(inst_cream->inst, i)) {
+ if (i == Rn)
+ ARMul_StoreWordN(cpu, addr, old_RN);
+ else
+ ARMul_StoreWordN(cpu, addr, cpu->Reg[i]);
+
+ addr += 4;
+ }
+ }
+
+ // Check PC reg
+ if (BIT(inst_cream->inst, 15))
+ ARMul_StoreWordN(cpu, addr, cpu->Reg_usr[1] + 8);
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ SXTB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ sxtb_inst* inst_cream = (sxtb_inst*)inst_base->component;
+
+ unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate);
+ if (BIT(operand2, 7)) {
+ operand2 |= 0xffffff00;
+ } else {
+ operand2 &= 0xff;
+ }
+ RD = operand2;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(sxtb_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ STR_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 0);
+
+ unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)];
+ ARMul_StoreWordN(cpu, addr, value);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ UXTB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ uxtb_inst* inst_cream = (uxtb_inst*)inst_base->component;
+ RD = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xff;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(uxtb_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ UXTAB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ uxtab_inst* inst_cream = (uxtab_inst*)inst_base->component;
+
+ unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xff;
+ RD = RN + operand2;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(uxtab_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ STRB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 0);
+ unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff;
+ ARMul_StoreByte(cpu,addr, value);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ STRBT_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 0);
+ unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff;
+ ARMul_StoreByte(cpu, addr, value);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ STRD_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 0);
+
+ // The 3DS doesn't have the Large Physical Access Extension (LPAE)
+ // so STRD wouldn't store these as a single write.
+ ARMul_StoreWordN(cpu, addr + 0, cpu->Reg[BITS(inst_cream->inst, 12, 15)]);
+ ARMul_StoreWordN(cpu, addr + 4, cpu->Reg[BITS(inst_cream->inst, 12, 15) + 1]);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ STREX_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
+ unsigned int write_addr = cpu->Reg[inst_cream->Rn];
+
+ if ((exclusive_detect(cpu, write_addr) == 0) && (cpu->exclusive_state == 1)) {
+ remove_exclusive(cpu, write_addr);
+ cpu->exclusive_state = 0;
+
+ ARMul_StoreWordN(cpu, write_addr, RM);
+ RD = 0;
+ } else {
+ // Failed to write due to mutex access
+ RD = 1;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ STREXB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
+ unsigned int write_addr = cpu->Reg[inst_cream->Rn];
+
+ if ((exclusive_detect(cpu, write_addr) == 0) && (cpu->exclusive_state == 1)) {
+ remove_exclusive(cpu, write_addr);
+ cpu->exclusive_state = 0;
+
+ ARMul_StoreByte(cpu, write_addr, cpu->Reg[inst_cream->Rm]);
+ RD = 0;
+ } else {
+ // Failed to write due to mutex access
+ RD = 1;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ STREXD_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
+ unsigned int write_addr = cpu->Reg[inst_cream->Rn];
+
+ if ((exclusive_detect(cpu, write_addr) == 0) && (cpu->exclusive_state == 1)) {
+ remove_exclusive(cpu, write_addr);
+ cpu->exclusive_state = 0;
+
+ const u32 rt = cpu->Reg[inst_cream->Rm + 0];
+ const u32 rt2 = cpu->Reg[inst_cream->Rm + 1];
+ u64 value;
+
+ value = (((u64)rt2 << 32) | rt);
+
+ ARMul_WriteDouble(cpu, write_addr, value);
+ RD = 0;
+ }
+ else {
+ // Failed to write due to mutex access
+ RD = 1;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ STREXH_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
+ unsigned int write_addr = cpu->Reg[inst_cream->Rn];
+
+ if ((exclusive_detect(cpu, write_addr) == 0) && (cpu->exclusive_state == 1)) {
+ remove_exclusive(cpu, write_addr);
+ cpu->exclusive_state = 0;
+
+ ARMul_StoreHalfWord(cpu, write_addr, RM);
+ RD = 0;
+ } else {
+ // Failed to write due to mutex access
+ RD = 1;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ STRH_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 0);
+
+ unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xffff;
+ ARMul_StoreHalfWord(cpu, addr, value);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ STRT_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
+ inst_cream->get_addr(cpu, inst_cream->inst, addr, 0);
+
+ unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)];
+ ARMul_StoreWordN(cpu, addr, value);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ldst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ SUB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ sub_inst* const inst_cream = (sub_inst*)inst_base->component;
+
+ u32 rn_val = RN;
+ if (inst_cream->Rn == 15)
+ rn_val += 8;
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(rn_val, ~SHIFTER_OPERAND, 1, &carry, &overflow);
+
+ if (inst_cream->S && (inst_cream->Rd == 15)) {
+ if (CurrentModeHasSPSR) {
+ cpu->Cpsr = cpu->Spsr_copy;
+ switch_mode(cpu, cpu->Spsr_copy & 0x1f);
+ LOAD_NZCVT;
+ }
+ } else if (inst_cream->S) {
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
+ }
+ if (inst_cream->Rd == 15) {
+ INC_PC(sizeof(sub_inst));
+ goto DISPATCH;
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(sub_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ SWI_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+
+ ProcessSwi(BITS(ARMul_LoadInstrS(cpu, cpu->Reg[15],4), 0, 24), state->Reg, state->m_currentThread);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(swi_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ SWP_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ swp_inst* inst_cream = (swp_inst*)inst_base->component;
+
+ addr = RN;
+ unsigned int value = ARMul_LoadWordN(cpu, addr);
+ ARMul_StoreWordN(cpu, addr, RM);
+
+ RD = value;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(swp_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ SWPB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ swp_inst* inst_cream = (swp_inst*)inst_base->component;
+ addr = RN;
+ unsigned int value = ARMul_LoadByte(cpu,addr);
+ ARMul_StoreByte(cpu,addr, (RM & 0xFF));
+ RD = value;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(swp_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ SXTAB_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ sxtab_inst* inst_cream = (sxtab_inst*)inst_base->component;
+
+ unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xff;
+
+ // Sign extend for byte
+ operand2 = (0x80 & operand2)? (0xFFFFFF00 | operand2):operand2;
+ RD = RN + operand2;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(uxtab_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SXTAB16_INST:
+ SXTB16_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ sxtab_inst* const inst_cream = (sxtab_inst*)inst_base->component;
+
+ const u8 rotation = inst_cream->rotate * 8;
+ u32 rm_val = RM;
+ u32 rn_val = RN;
+
+ if (rotation)
+ rm_val = ((rm_val << (32 - rotation)) | (rm_val >> rotation));
+
+ // SXTB16
+ if (inst_cream->Rn == 15) {
+ u32 lo = (u32)(s8)rm_val;
+ u32 hi = (u32)(s8)(rm_val >> 16);
+ RD = (lo | (hi << 16));
+ }
+ // SXTAB16
+ else {
+ u32 lo = (rn_val & 0xFFFF) + (u32)(s8)(rm_val & 0xFF);
+ u32 hi = ((rn_val >> 16) & 0xFFFF) + (u32)(s8)((rm_val >> 16) & 0xFF);
+ RD = (lo | (hi << 16));
+ }
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(sxtab_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ SXTAH_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ sxtah_inst* inst_cream = (sxtah_inst*)inst_base->component;
+
+ unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xffff;
+ // Sign extend for half
+ operand2 = (0x8000 & operand2) ? (0xFFFF0000 | operand2) : operand2;
+ RD = RN + operand2;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(sxtah_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ TEQ_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ teq_inst* const inst_cream = (teq_inst*)inst_base->component;
+
+ u32 lop = RN;
+ u32 rop = SHIFTER_OPERAND;
+
+ if (inst_cream->Rn == 15)
+ lop += GET_INST_SIZE(cpu) * 2;
+
+ u32 result = lop ^ rop;
+
+ UPDATE_NFLAG(result);
+ UPDATE_ZFLAG(result);
+ UPDATE_CFLAG_WITH_SC;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(teq_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ TST_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ tst_inst* const inst_cream = (tst_inst*)inst_base->component;
+
+ u32 lop = RN;
+ u32 rop = SHIFTER_OPERAND;
+
+ if (inst_cream->Rn == 15)
+ lop += GET_INST_SIZE(cpu) * 2;
+
+ u32 result = lop & rop;
+
+ UPDATE_NFLAG(result);
+ UPDATE_ZFLAG(result);
+ UPDATE_CFLAG_WITH_SC;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(tst_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ UADD8_INST:
+ UADD16_INST:
+ UADDSUBX_INST:
+ USUB8_INST:
+ USUB16_INST:
+ USUBADDX_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ const u8 op2 = inst_cream->op2;
+ const u32 rm_val = RM;
+ const u32 rn_val = RN;
+
+ s32 lo_result = 0;
+ s32 hi_result = 0;
+
+ // UADD16
+ if (op2 == 0x00) {
+ lo_result = (rn_val & 0xFFFF) + (rm_val & 0xFFFF);
+ hi_result = ((rn_val >> 16) & 0xFFFF) + ((rm_val >> 16) & 0xFFFF);
+
+ if (lo_result & 0xFFFF0000) {
+ cpu->Cpsr |= (1 << 16);
+ cpu->Cpsr |= (1 << 17);
+ } else {
+ cpu->Cpsr &= ~(1 << 16);
+ cpu->Cpsr &= ~(1 << 17);
+ }
+
+ if (hi_result & 0xFFFF0000) {
+ cpu->Cpsr |= (1 << 18);
+ cpu->Cpsr |= (1 << 19);
+ } else {
+ cpu->Cpsr &= ~(1 << 18);
+ cpu->Cpsr &= ~(1 << 19);
+ }
+ }
+ // UASX
+ else if (op2 == 0x01) {
+ lo_result = (rn_val & 0xFFFF) - ((rm_val >> 16) & 0xFFFF);
+ hi_result = ((rn_val >> 16) & 0xFFFF) + (rm_val & 0xFFFF);
+
+ if (lo_result >= 0) {
+ cpu->Cpsr |= (1 << 16);
+ cpu->Cpsr |= (1 << 17);
+ } else {
+ cpu->Cpsr &= ~(1 << 16);
+ cpu->Cpsr &= ~(1 << 17);
+ }
+
+ if (hi_result >= 0x10000) {
+ cpu->Cpsr |= (1 << 18);
+ cpu->Cpsr |= (1 << 19);
+ } else {
+ cpu->Cpsr &= ~(1 << 18);
+ cpu->Cpsr &= ~(1 << 19);
+ }
+ }
+ // USAX
+ else if (op2 == 0x02) {
+ lo_result = (rn_val & 0xFFFF) + ((rm_val >> 16) & 0xFFFF);
+ hi_result = ((rn_val >> 16) & 0xFFFF) - (rm_val & 0xFFFF);
+
+ if (lo_result >= 0x10000) {
+ cpu->Cpsr |= (1 << 16);
+ cpu->Cpsr |= (1 << 17);
+ } else {
+ cpu->Cpsr &= ~(1 << 16);
+ cpu->Cpsr &= ~(1 << 17);
+ }
+
+ if (hi_result >= 0) {
+ cpu->Cpsr |= (1 << 18);
+ cpu->Cpsr |= (1 << 19);
+ } else {
+ cpu->Cpsr &= ~(1 << 18);
+ cpu->Cpsr &= ~(1 << 19);
+ }
+ }
+ // USUB16
+ else if (op2 == 0x03) {
+ lo_result = (rn_val & 0xFFFF) - (rm_val & 0xFFFF);
+ hi_result = ((rn_val >> 16) & 0xFFFF) - ((rm_val >> 16) & 0xFFFF);
+
+ if ((lo_result & 0xFFFF0000) == 0) {
+ cpu->Cpsr |= (1 << 16);
+ cpu->Cpsr |= (1 << 17);
+ } else {
+ cpu->Cpsr &= ~(1 << 16);
+ cpu->Cpsr &= ~(1 << 17);
+ }
+
+ if ((hi_result & 0xFFFF0000) == 0) {
+ cpu->Cpsr |= (1 << 18);
+ cpu->Cpsr |= (1 << 19);
+ } else {
+ cpu->Cpsr &= ~(1 << 18);
+ cpu->Cpsr &= ~(1 << 19);
+ }
+ }
+ // UADD8
+ else if (op2 == 0x04) {
+ s16 sum1 = (rn_val & 0xFF) + (rm_val & 0xFF);
+ s16 sum2 = ((rn_val >> 8) & 0xFF) + ((rm_val >> 8) & 0xFF);
+ s16 sum3 = ((rn_val >> 16) & 0xFF) + ((rm_val >> 16) & 0xFF);
+ s16 sum4 = ((rn_val >> 24) & 0xFF) + ((rm_val >> 24) & 0xFF);
+
+ if (sum1 >= 0x100)
+ state->Cpsr |= (1 << 16);
+ else
+ state->Cpsr &= ~(1 << 16);
+
+ if (sum2 >= 0x100)
+ state->Cpsr |= (1 << 17);
+ else
+ state->Cpsr &= ~(1 << 17);
+
+ if (sum3 >= 0x100)
+ state->Cpsr |= (1 << 18);
+ else
+ state->Cpsr &= ~(1 << 18);
+
+ if (sum4 >= 0x100)
+ state->Cpsr |= (1 << 19);
+ else
+ state->Cpsr &= ~(1 << 19);
+
+ lo_result = ((sum1 & 0xFF) | (sum2 & 0xFF) << 8);
+ hi_result = ((sum3 & 0xFF) | (sum4 & 0xFF) << 8);
+ }
+ // USUB8
+ else if (op2 == 0x07) {
+ s16 diff1 = (rn_val & 0xFF) - (rm_val & 0xFF);
+ s16 diff2 = ((rn_val >> 8) & 0xFF) - ((rm_val >> 8) & 0xFF);
+ s16 diff3 = ((rn_val >> 16) & 0xFF) - ((rm_val >> 16) & 0xFF);
+ s16 diff4 = ((rn_val >> 24) & 0xFF) - ((rm_val >> 24) & 0xFF);
+
+ if (diff1 >= 0)
+ state->Cpsr |= (1 << 16);
+ else
+ state->Cpsr &= ~(1 << 16);
+
+ if (diff2 >= 0)
+ state->Cpsr |= (1 << 17);
+ else
+ state->Cpsr &= ~(1 << 17);
+
+ if (diff3 >= 0)
+ state->Cpsr |= (1 << 18);
+ else
+ state->Cpsr &= ~(1 << 18);
+
+ if (diff4 >= 0)
+ state->Cpsr |= (1 << 19);
+ else
+ state->Cpsr &= ~(1 << 19);
+
+ lo_result = (diff1 & 0xFF) | ((diff2 & 0xFF) << 8);
+ hi_result = (diff3 & 0xFF) | ((diff4 & 0xFF) << 8);
+ }
+
+ RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ UHADD8_INST:
+ UHADD16_INST:
+ UHADDSUBX_INST:
+ UHSUBADDX_INST:
+ UHSUB8_INST:
+ UHSUB16_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+ const u32 rm_val = RM;
+ const u32 rn_val = RN;
+ const u8 op2 = inst_cream->op2;
+
+ if (op2 == 0x00 || op2 == 0x01 || op2 == 0x02 || op2 == 0x03)
+ {
+ u32 lo_val = 0;
+ u32 hi_val = 0;
+
+ // UHADD16
+ if (op2 == 0x00) {
+ lo_val = (rn_val & 0xFFFF) + (rm_val & 0xFFFF);
+ hi_val = ((rn_val >> 16) & 0xFFFF) + ((rm_val >> 16) & 0xFFFF);
+ }
+ // UHASX
+ else if (op2 == 0x01) {
+ lo_val = (rn_val & 0xFFFF) - ((rm_val >> 16) & 0xFFFF);
+ hi_val = ((rn_val >> 16) & 0xFFFF) + (rm_val & 0xFFFF);
+ }
+ // UHSAX
+ else if (op2 == 0x02) {
+ lo_val = (rn_val & 0xFFFF) + ((rm_val >> 16) & 0xFFFF);
+ hi_val = ((rn_val >> 16) & 0xFFFF) - (rm_val & 0xFFFF);
+ }
+ // UHSUB16
+ else if (op2 == 0x03) {
+ lo_val = (rn_val & 0xFFFF) - (rm_val & 0xFFFF);
+ hi_val = ((rn_val >> 16) & 0xFFFF) - ((rm_val >> 16) & 0xFFFF);
+ }
+
+ lo_val >>= 1;
+ hi_val >>= 1;
+
+ RD = (lo_val & 0xFFFF) | ((hi_val & 0xFFFF) << 16);
+ }
+ else if (op2 == 0x04 || op2 == 0x07) {
+ u32 sum1;
+ u32 sum2;
+ u32 sum3;
+ u32 sum4;
+
+ // UHADD8
+ if (op2 == 0x04) {
+ sum1 = (rn_val & 0xFF) + (rm_val & 0xFF);
+ sum2 = ((rn_val >> 8) & 0xFF) + ((rm_val >> 8) & 0xFF);
+ sum3 = ((rn_val >> 16) & 0xFF) + ((rm_val >> 16) & 0xFF);
+ sum4 = ((rn_val >> 24) & 0xFF) + ((rm_val >> 24) & 0xFF);
+ }
+ // UHSUB8
+ else {
+ sum1 = (rn_val & 0xFF) - (rm_val & 0xFF);
+ sum2 = ((rn_val >> 8) & 0xFF) - ((rm_val >> 8) & 0xFF);
+ sum3 = ((rn_val >> 16) & 0xFF) - ((rm_val >> 16) & 0xFF);
+ sum4 = ((rn_val >> 24) & 0xFF) - ((rm_val >> 24) & 0xFF);
+ }
+
+ sum1 >>= 1;
+ sum2 >>= 1;
+ sum3 >>= 1;
+ sum4 >>= 1;
+
+ RD = (sum1 & 0xFF) | ((sum2 & 0xFF) << 8) | ((sum3 & 0xFF) << 16) | ((sum4 & 0xFF) << 24);
+ }
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ UMAAL_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ umaal_inst* const inst_cream = (umaal_inst*)inst_base->component;
+ const u64 rm = RM;
+ const u64 rn = RN;
+ const u64 rd_lo = RDLO;
+ const u64 rd_hi = RDHI;
+ const u64 result = (rm * rn) + rd_lo + rd_hi;
+
+ RDLO = (result & 0xFFFFFFFF);
+ RDHI = ((result >> 32) & 0xFFFFFFFF);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(umaal_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ UMLAL_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ umlal_inst* inst_cream = (umlal_inst*)inst_base->component;
+ unsigned long long int rm = RM;
+ unsigned long long int rs = RS;
+ unsigned long long int rst = rm * rs;
+ unsigned long long int add = ((unsigned long long) RDHI)<<32;
+ add += RDLO;
+ rst += add;
+ RDLO = BITS(rst, 0, 31);
+ RDHI = BITS(rst, 32, 63);
+
+ if (inst_cream->S) {
+ cpu->NFlag = BIT(RDHI, 31);
+ cpu->ZFlag = (RDHI == 0 && RDLO == 0);
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(umlal_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ UMULL_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ umull_inst* inst_cream = (umull_inst*)inst_base->component;
+ unsigned long long int rm = RM;
+ unsigned long long int rs = RS;
+ unsigned long long int rst = rm * rs;
+ RDHI = BITS(rst, 32, 63);
+ RDLO = BITS(rst, 0, 31);
+
+ if (inst_cream->S) {
+ cpu->NFlag = BIT(RDHI, 31);
+ cpu->ZFlag = (RDHI == 0 && RDLO == 0);
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(umull_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ B_2_THUMB:
+ {
+ b_2_thumb* inst_cream = (b_2_thumb*)inst_base->component;
+ cpu->Reg[15] = cpu->Reg[15] + 4 + inst_cream->imm;
+ INC_PC(sizeof(b_2_thumb));
+ goto DISPATCH;
+ }
+ B_COND_THUMB:
+ {
+ b_cond_thumb* inst_cream = (b_cond_thumb*)inst_base->component;
+
+ if(CondPassed(cpu, inst_cream->cond))
+ cpu->Reg[15] = cpu->Reg[15] + 4 + inst_cream->imm;
+ else
+ cpu->Reg[15] += 2;
+
+ INC_PC(sizeof(b_cond_thumb));
+ goto DISPATCH;
+ }
+ BL_1_THUMB:
+ {
+ bl_1_thumb* inst_cream = (bl_1_thumb*)inst_base->component;
+ cpu->Reg[14] = cpu->Reg[15] + 4 + inst_cream->imm;
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(bl_1_thumb));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+ BL_2_THUMB:
+ {
+ bl_2_thumb* inst_cream = (bl_2_thumb*)inst_base->component;
+ int tmp = ((cpu->Reg[15] + 2) | 1);
+ cpu->Reg[15] = (cpu->Reg[14] + inst_cream->imm);
+ cpu->Reg[14] = tmp;
+ INC_PC(sizeof(bl_2_thumb));
+ goto DISPATCH;
+ }
+ BLX_1_THUMB:
+ {
+ // BLX 1 for armv5t and above
+ u32 tmp = cpu->Reg[15];
+ blx_1_thumb* inst_cream = (blx_1_thumb*)inst_base->component;
+ cpu->Reg[15] = (cpu->Reg[14] + inst_cream->imm) & 0xFFFFFFFC;
+ cpu->Reg[14] = ((tmp + 2) | 1);
+ cpu->TFlag = 0;
+ INC_PC(sizeof(blx_1_thumb));
+ goto DISPATCH;
+ }
+
+ UQADD8_INST:
+ UQADD16_INST:
+ UQADDSUBX_INST:
+ UQSUB8_INST:
+ UQSUB16_INST:
+ UQSUBADDX_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ const u8 op2 = inst_cream->op2;
+ const u32 rm_val = RM;
+ const u32 rn_val = RN;
+
+ u16 lo_val = 0;
+ u16 hi_val = 0;
+
+ // UQADD16
+ if (op2 == 0x00) {
+ lo_val = ARMul_UnsignedSaturatedAdd16(rn_val & 0xFFFF, rm_val & 0xFFFF);
+ hi_val = ARMul_UnsignedSaturatedAdd16((rn_val >> 16) & 0xFFFF, (rm_val >> 16) & 0xFFFF);
+ }
+ // UQASX
+ else if (op2 == 0x01) {
+ lo_val = ARMul_UnsignedSaturatedSub16(rn_val & 0xFFFF, (rm_val >> 16) & 0xFFFF);
+ hi_val = ARMul_UnsignedSaturatedAdd16((rn_val >> 16) & 0xFFFF, rm_val & 0xFFFF);
+ }
+ // UQSAX
+ else if (op2 == 0x02) {
+ lo_val = ARMul_UnsignedSaturatedAdd16(rn_val & 0xFFFF, (rm_val >> 16) & 0xFFFF);
+ hi_val = ARMul_UnsignedSaturatedSub16((rn_val >> 16) & 0xFFFF, rm_val & 0xFFFF);
+ }
+ // UQSUB16
+ else if (op2 == 0x03) {
+ lo_val = ARMul_UnsignedSaturatedSub16(rn_val & 0xFFFF, rm_val & 0xFFFF);
+ hi_val = ARMul_UnsignedSaturatedSub16((rn_val >> 16) & 0xFFFF, (rm_val >> 16) & 0xFFFF);
+ }
+ // UQADD8
+ else if (op2 == 0x04) {
+ lo_val = ARMul_UnsignedSaturatedAdd8(rn_val, rm_val) |
+ ARMul_UnsignedSaturatedAdd8(rn_val >> 8, rm_val >> 8) << 8;
+ hi_val = ARMul_UnsignedSaturatedAdd8(rn_val >> 16, rm_val >> 16) |
+ ARMul_UnsignedSaturatedAdd8(rn_val >> 24, rm_val >> 24) << 8;
+ }
+ // UQSUB8
+ else {
+ lo_val = ARMul_UnsignedSaturatedSub8(rn_val, rm_val) |
+ ARMul_UnsignedSaturatedSub8(rn_val >> 8, rm_val >> 8) << 8;
+ hi_val = ARMul_UnsignedSaturatedSub8(rn_val >> 16, rm_val >> 16) |
+ ARMul_UnsignedSaturatedSub8(rn_val >> 24, rm_val >> 24) << 8;
+ }
+
+ RD = ((lo_val & 0xFFFF) | hi_val << 16);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ USAD8_INST:
+ USADA8_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
+
+ const u8 ra_idx = inst_cream->Ra;
+ const u32 rm_val = RM;
+ const u32 rn_val = RN;
+
+ const u8 diff1 = ARMul_UnsignedAbsoluteDifference(rn_val & 0xFF, rm_val & 0xFF);
+ const u8 diff2 = ARMul_UnsignedAbsoluteDifference((rn_val >> 8) & 0xFF, (rm_val >> 8) & 0xFF);
+ const u8 diff3 = ARMul_UnsignedAbsoluteDifference((rn_val >> 16) & 0xFF, (rm_val >> 16) & 0xFF);
+ const u8 diff4 = ARMul_UnsignedAbsoluteDifference((rn_val >> 24) & 0xFF, (rm_val >> 24) & 0xFF);
+
+ u32 finalDif = (diff1 + diff2 + diff3 + diff4);
+
+ // Op is USADA8 if true.
+ if (ra_idx != 15)
+ finalDif += cpu->Reg[ra_idx];
+
+ RD = finalDif;
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ USAT_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ssat_inst* const inst_cream = (ssat_inst*)inst_base->component;
+
+ u8 shift_type = inst_cream->shift_type;
+ u8 shift_amount = inst_cream->imm5;
+ u32 rn_val = RN;
+
+ // 32-bit ASR is encoded as an amount of 0.
+ if (shift_type == 1 && shift_amount == 0)
+ shift_amount = 31;
+
+ if (shift_type == 0)
+ rn_val <<= shift_amount;
+ else if (shift_type == 1)
+ rn_val = ((s32)rn_val >> shift_amount);
+
+ bool saturated = false;
+ rn_val = ARMul_UnsignedSatQ(rn_val, inst_cream->sat_imm, &saturated);
+
+ if (saturated)
+ cpu->Cpsr |= (1 << 27);
+
+ RD = rn_val;
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ssat_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ USAT16_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ ssat_inst* const inst_cream = (ssat_inst*)inst_base->component;
+ const u8 saturate_to = inst_cream->sat_imm;
+
+ bool sat1 = false;
+ bool sat2 = false;
+
+ RD = (ARMul_UnsignedSatQ((s16)RN, saturate_to, &sat1) & 0xFFFF) |
+ ARMul_UnsignedSatQ((s32)RN >> 16, saturate_to, &sat2) << 16;
+
+ if (sat1 || sat2)
+ cpu->Cpsr |= (1 << 27);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(ssat_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ UXTAB16_INST:
+ UXTB16_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ uxtab_inst* const inst_cream = (uxtab_inst*)inst_base->component;
+
+ const u8 rn_idx = inst_cream->Rn;
+ const u32 rm_val = RM;
+ const u32 rotation = inst_cream->rotate * 8;
+ const u32 rotated_rm = ((rm_val << (32 - rotation)) | (rm_val >> rotation));
+
+ // UXTB16, otherwise UXTAB16
+ if (rn_idx == 15) {
+ RD = rotated_rm & 0x00FF00FF;
+ } else {
+ const u32 rn_val = RN;
+ const u8 lo_rotated = (rotated_rm & 0xFF);
+ const u16 lo_result = (rn_val & 0xFFFF) + (u16)lo_rotated;
+ const u8 hi_rotated = (rotated_rm >> 16) & 0xFF;
+ const u16 hi_result = (rn_val >> 16) + (u16)hi_rotated;
+
+ RD = ((hi_result << 16) | (lo_result & 0xFFFF));
+ }
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(uxtab_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
+ #define VFP_INTERPRETER_IMPL
+ #include "arm/skyeye_common/vfp/vfpinstr.cpp"
+ #undef VFP_INTERPRETER_IMPL
+
+ END:
+ {
+ SAVE_NZCVT;
+ cpu->NumInstrsToExecute = 0;
+ return num_instrs;
+ }
+ INIT_INST_LENGTH:
+ {
+ cpu->NumInstrsToExecute = 0;
+ return num_instrs;
+ }
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+unsigned InterpreterMainLoop(ARMul_State* state);
--- /dev/null
+// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+//massive fixed by ichfly XDS/3dmoo team
+
+#include <assert.h>
+#include "Kernel.h"
+
+#include "arm/dyncom/arm_dyncom_run.h"
+#include "arm/skyeye_common/armdefs.h"
+
+void switch_mode(ARMul_State* core, uint32_t mode) {
+ if (core->Mode == mode)
+ return;
+
+ if (mode != USERBANK) {
+ switch (core->Mode) {
+ case SYSTEM32MODE: // Shares registers with user mode
+ case USER32MODE:
+ core->Reg_usr[0] = core->Reg[13];
+ core->Reg_usr[1] = core->Reg[14];
+ break;
+ case IRQ32MODE:
+ core->Reg_irq[0] = core->Reg[13];
+ core->Reg_irq[1] = core->Reg[14];
+ core->Spsr[IRQBANK] = core->Spsr_copy;
+ break;
+ case SVC32MODE:
+ core->Reg_svc[0] = core->Reg[13];
+ core->Reg_svc[1] = core->Reg[14];
+ core->Spsr[SVCBANK] = core->Spsr_copy;
+ break;
+ case ABORT32MODE:
+ core->Reg_abort[0] = core->Reg[13];
+ core->Reg_abort[1] = core->Reg[14];
+ core->Spsr[ABORTBANK] = core->Spsr_copy;
+ break;
+ case UNDEF32MODE:
+ core->Reg_undef[0] = core->Reg[13];
+ core->Reg_undef[1] = core->Reg[14];
+ core->Spsr[UNDEFBANK] = core->Spsr_copy;
+ break;
+ case FIQ32MODE:
+ core->Reg_firq[0] = core->Reg[13];
+ core->Reg_firq[1] = core->Reg[14];
+ core->Spsr[FIQBANK] = core->Spsr_copy;
+ break;
+ }
+
+ switch (mode) {
+ case USER32MODE:
+ core->Reg[13] = core->Reg_usr[0];
+ core->Reg[14] = core->Reg_usr[1];
+ core->Bank = USERBANK;
+ break;
+ case IRQ32MODE:
+ core->Reg[13] = core->Reg_irq[0];
+ core->Reg[14] = core->Reg_irq[1];
+ core->Spsr_copy = core->Spsr[IRQBANK];
+ core->Bank = IRQBANK;
+ break;
+ case SVC32MODE:
+ core->Reg[13] = core->Reg_svc[0];
+ core->Reg[14] = core->Reg_svc[1];
+ core->Spsr_copy = core->Spsr[SVCBANK];
+ core->Bank = SVCBANK;
+ break;
+ case ABORT32MODE:
+ core->Reg[13] = core->Reg_abort[0];
+ core->Reg[14] = core->Reg_abort[1];
+ core->Spsr_copy = core->Spsr[ABORTBANK];
+ core->Bank = ABORTBANK;
+ break;
+ case UNDEF32MODE:
+ core->Reg[13] = core->Reg_undef[0];
+ core->Reg[14] = core->Reg_undef[1];
+ core->Spsr_copy = core->Spsr[UNDEFBANK];
+ core->Bank = UNDEFBANK;
+ break;
+ case FIQ32MODE:
+ core->Reg[13] = core->Reg_firq[0];
+ core->Reg[14] = core->Reg_firq[1];
+ core->Spsr_copy = core->Spsr[FIQBANK];
+ core->Bank = FIQBANK;
+ break;
+ case SYSTEM32MODE: // Shares registers with user mode.
+ core->Reg[13] = core->Reg_usr[0];
+ core->Reg[14] = core->Reg_usr[1];
+ core->Bank = SYSTEMBANK;
+ break;
+ }
+
+ // Set the mode bits in the APSR
+ core->Cpsr = (core->Cpsr & ~core->Mode) | mode;
+ core->Mode = mode;
+ }
+}
--- /dev/null
+/* Copyright (C)
+* 2011 - Michael.Kang blackfin.kang@gmail.com
+* 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
+* of the License, 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 __ARM_DYNCOM_RUN__
+#define __ARM_DYNCOM_RUN__
+
+#include "arm/skyeye_common/skyeye_types.h"
+
+void switch_mode(arm_core_t *core, uint32_t mode);
+
+/* FIXME, we temporarily think thumb instruction is always 16 bit */
+static inline u32 GET_INST_SIZE(arm_core_t* core) {
+ return core->TFlag? 2 : 4;
+}
+
+/**
+* @brief Read R15 and forced R15 to wold align, used address calculation
+*
+* @param core
+* @param Rn
+*
+* @return
+*/
+static inline addr_t CHECK_READ_REG15_WA(arm_core_t* core, int Rn) {
+ return (Rn == 15)? ((core->Reg[15] & ~0x3) + GET_INST_SIZE(core) * 2) : core->Reg[Rn];
+}
+
+/**
+* @brief Read R15, used to data processing with pc
+*
+* @param core
+* @param Rn
+*
+* @return
+*/
+static inline u32 CHECK_READ_REG15(arm_core_t* core, int Rn) {
+ return (Rn == 15)? ((core->Reg[15] & ~0x1) + GET_INST_SIZE(core) * 2) : core->Reg[Rn];
+}
+
+#endif
--- /dev/null
+#include "Kernel.h"
+#include "arm/skyeye_common/armdefs.h"
+#include "arm/skyeye_common/arm_regformat.h"
+
+u32 ReadCP15Register(ARMul_State* cpu, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2)
+{
+ // Unprivileged registers
+ if (crn == 13 && opcode_1 == 0 && crm == 0)
+ {
+ if (opcode_2 == 2)
+ return cpu->CP15[CP15(CP15_THREAD_UPRW)];
+
+ // TODO: Whenever TLS is implemented, this should return
+ // "cpu->CP15[CP15(CP15_THREAD_URO)];"
+ // which contains the address of the 0x200-byte TLS
+ if (opcode_2 == 3)
+ return cpu->m_currentThread->m_TSL3DS;
+ }
+
+ LOG("MRC CRn=%u, CRm=%u, OP1=%u OP2=%u is not implemented. Returning zero.", crn, crm, opcode_1, opcode_2);
+ return 0;
+}
+
+// Write to the CP15 registers. Used with implementation of the MCR instruction.
+// Note that since the 3DS does not have the hypervisor extensions, these registers
+// are not implemented.
+void WriteCP15Register(ARMul_State* cpu, u32 value, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2)
+{
+ // Unprivileged registers
+ if (crn == 7 && opcode_1 == 0 && crm == 5 && opcode_2 == 4)
+ {
+ cpu->CP15[CP15(CP15_FLUSH_PREFETCH_BUFFER)] = value;
+ }
+ else if (crn == 7 && opcode_1 == 0 && crm == 10)
+ {
+ if (opcode_2 == 4)
+ cpu->CP15[CP15(CP15_DATA_SYNC_BARRIER)] = value;
+ else if (opcode_2 == 5)
+ cpu->CP15[CP15(CP15_DATA_MEMORY_BARRIER)] = value;
+
+ }
+ else if (crn == 13 && opcode_1 == 0 && crm == 0 && opcode_2 == 2)
+ {
+ cpu->CP15[CP15(CP15_THREAD_UPRW)] = value;
+ }
+}
--- /dev/null
+// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+//massive fixed by ichfly XDS/3dmoo team
+
+// We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding
+// ARM instruction, and using the existing ARM simulator.
+
+#include "Kernel.h"
+
+#include "arm/skyeye_common/skyeye_defs.h"
+
+#ifndef MODET // Required for the Thumb instruction support
+#if 1
+#error "MODET needs to be defined for the Thumb world to work"
+#else
+#define MODET (1)
+#endif
+#endif
+
+//#include "Kernel.h"
+
+#include "arm/skyeye_common/armos.h"
+#include "arm/dyncom/arm_dyncom_thumb.h"
+
+// Decode a 16bit Thumb instruction. The instruction is in the low 16-bits of the tinstr field,
+// with the following Thumb instruction held in the high 16-bits. Passing in two Thumb instructions
+// allows easier simulation of the special dual BL instruction.
+
+//they are different in here
+#define BIT(data,n) ( (ARMword)(data>>(n))&1) /* bit n of instruction */
+#define BITS(data,m,n) ( (ARMword)(data<<(31-(n))) >> ((31-(n))+(m)) ) /* bits m to n of instr */
+
+
+tdstate thumb_translate(addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t* inst_size) {
+ tdstate valid = t_uninitialized;
+ ARMword tinstr;
+ tinstr = instr;
+
+ // The endian should be judge here
+ if ((addr & 0x3) != 0)
+ tinstr = instr >> 16;
+ else
+ tinstr &= 0xFFFF;
+
+ *ainstr = 0xDEADC0DE; // Debugging to catch non updates
+
+ switch ((tinstr & 0xF800) >> 11) {
+ case 0: // LSL
+ case 1: // LSR
+ case 2: // ASR
+ *ainstr = 0xE1B00000 // base opcode
+ | ((tinstr & 0x1800) >> (11 - 5)) // shift type
+ | ((tinstr & 0x07C0) << (7 - 6)) // imm5
+ | ((tinstr & 0x0038) >> 3) // Rs
+ | ((tinstr & 0x0007) << 12); // Rd
+ break;
+
+ case 3: // ADD/SUB
+ {
+ ARMword subset[4] = {
+ 0xE0900000, // ADDS Rd,Rs,Rn
+ 0xE0500000, // SUBS Rd,Rs,Rn
+ 0xE2900000, // ADDS Rd,Rs,#imm3
+ 0xE2500000 // SUBS Rd,Rs,#imm3
+ };
+ // It is quicker indexing into a table, than performing switch or conditionals:
+ *ainstr = subset[(tinstr & 0x0600) >> 9] // base opcode
+ | ((tinstr & 0x01C0) >> 6) // Rn or imm3
+ | ((tinstr & 0x0038) << (16 - 3)) // Rs
+ | ((tinstr & 0x0007) << (12 - 0)); // Rd
+ }
+ break;
+
+ case 4: // MOV
+ case 5: // CMP
+ case 6: // ADD
+ case 7: // SUB
+ {
+ ARMword subset[4] = {
+ 0xE3B00000, // MOVS Rd,#imm8
+ 0xE3500000, // CMP Rd,#imm8
+ 0xE2900000, // ADDS Rd,Rd,#imm8
+ 0xE2500000, // SUBS Rd,Rd,#imm8
+ };
+
+ *ainstr = subset[(tinstr & 0x1800) >> 11] // base opcode
+ | ((tinstr & 0x00FF) >> 0) // imm8
+ | ((tinstr & 0x0700) << (16 - 8)) // Rn
+ | ((tinstr & 0x0700) << (12 - 8)); // Rd
+ }
+ break;
+
+ case 8: // Arithmetic and high register transfers
+
+ // TODO: Since the subsets for both Format 4 and Format 5 instructions are made up of
+ // different ARM encodings, we could save the following conditional, and just have one
+ // large subset
+
+ if ((tinstr & (1 << 10)) == 0) {
+ enum otype {
+ t_norm,
+ t_shift,
+ t_neg,
+ t_mul
+ };
+
+ struct {
+ ARMword opcode;
+ otype type;
+ } subset[16] = {
+ { 0xE0100000, t_norm }, // ANDS Rd,Rd,Rs
+ { 0xE0300000, t_norm }, // EORS Rd,Rd,Rs
+ { 0xE1B00010, t_shift }, // MOVS Rd,Rd,LSL Rs
+ { 0xE1B00030, t_shift }, // MOVS Rd,Rd,LSR Rs
+ { 0xE1B00050, t_shift }, // MOVS Rd,Rd,ASR Rs
+ { 0xE0B00000, t_norm }, // ADCS Rd,Rd,Rs
+ { 0xE0D00000, t_norm }, // SBCS Rd,Rd,Rs
+ { 0xE1B00070, t_shift }, // MOVS Rd,Rd,ROR Rs
+ { 0xE1100000, t_norm }, // TST Rd,Rs
+ { 0xE2700000, t_neg }, // RSBS Rd,Rs,#0
+ { 0xE1500000, t_norm }, // CMP Rd,Rs
+ { 0xE1700000, t_norm }, // CMN Rd,Rs
+ { 0xE1900000, t_norm }, // ORRS Rd,Rd,Rs
+ { 0xE0100090, t_mul }, // MULS Rd,Rd,Rs
+ { 0xE1D00000, t_norm }, // BICS Rd,Rd,Rs
+ { 0xE1F00000, t_norm } // MVNS Rd,Rs
+ };
+
+ *ainstr = subset[(tinstr & 0x03C0) >> 6].opcode; // base
+
+ switch (subset[(tinstr & 0x03C0) >> 6].type) {
+ case t_norm:
+ *ainstr |= ((tinstr & 0x0007) << 16) // Rn
+ | ((tinstr & 0x0007) << 12) // Rd
+ | ((tinstr & 0x0038) >> 3); // Rs
+ break;
+ case t_shift:
+ *ainstr |= ((tinstr & 0x0007) << 12) // Rd
+ | ((tinstr & 0x0007) >> 0) // Rm
+ | ((tinstr & 0x0038) << (8 - 3)); // Rs
+ break;
+ case t_neg:
+ *ainstr |= ((tinstr & 0x0007) << 12) // Rd
+ | ((tinstr & 0x0038) << (16 - 3)); // Rn
+ break;
+ case t_mul:
+ *ainstr |= ((tinstr & 0x0007) << 16) // Rd
+ | ((tinstr & 0x0007) << 8) // Rs
+ | ((tinstr & 0x0038) >> 3); // Rm
+ break;
+ }
+ }
+ else {
+ ARMword Rd = ((tinstr & 0x0007) >> 0);
+ ARMword Rs = ((tinstr & 0x0038) >> 3);
+
+ if (tinstr & (1 << 7))
+ Rd += 8;
+ if (tinstr & (1 << 6))
+ Rs += 8;
+
+ switch ((tinstr & 0x03C0) >> 6) {
+ case 0x0: /* ADD Rd,Rd,Hs */
+ case 0x1: /* ADD Rd,Rd,Hs */
+ case 0x2: /* ADD Rd,Rd,Hs */
+ case 0x3: /* ADD Rd,Rd,Hs */
+ *ainstr = 0xE0800000 /* base */
+ | (Rd << 16) /* Rn */
+ | (Rd << 12) /* Rd */
+ | (Rs << 0); /* Rm */
+ break;
+
+ case 0x4: /* CMP Rd,Hs */
+ case 0x5: /* CMP Rd,Hs */
+ case 0x6: /* CMP Hd,Rs */
+ case 0x7: /* CMP Hd,Hs */
+ *ainstr = 0xE1500000 /* base */
+ | (Rd << 16) /* Rn */
+ | (Rd << 12) /* Rd */
+ | (Rs << 0); /* Rm */
+ break;
+ case 0x8: /* MOV Rd,Hs */
+ case 0x9: /* MOV Rd,Hs */
+ case 0xA: /* MOV Hd,Rs */
+ case 0xB: /* MOV Hd,Hs */
+ *ainstr = 0xE1A00000 /* base */
+ | (Rd << 16) /* Rn */
+ | (Rd << 12) /* Rd */
+ | (Rs << 0); /* Rm */
+ break;
+ case 0xC: /* BX Rs */
+ case 0xD: /* BX Hs */
+ *ainstr = 0xE12FFF10 /* base */
+ | ((tinstr & 0x0078) >> 3); /* Rd */
+ break;
+ case 0xE: /* BLX */
+ case 0xF: /* BLX */
+ *ainstr = 0xE1200030 /* base */
+ | (Rs << 0); /* Rm */
+ break;
+ }
+ }
+ break;
+
+ case 9: // LDR Rd,[PC,#imm8]
+ *ainstr = 0xE59F0000 // base
+ | ((tinstr & 0x0700) << (12 - 8)) // Rd
+ | ((tinstr & 0x00FF) << (2 - 0)); // off8
+ break;
+
+ case 10:
+ case 11:
+ {
+ static const ARMword subset[8] = {
+ 0xE7800000, // STR Rd,[Rb,Ro]
+ 0xE18000B0, // STRH Rd,[Rb,Ro]
+ 0xE7C00000, // STRB Rd,[Rb,Ro]
+ 0xE19000D0, // LDRSB Rd,[Rb,Ro]
+ 0xE7900000, // LDR Rd,[Rb,Ro]
+ 0xE19000B0, // LDRH Rd,[Rb,Ro]
+ 0xE7D00000, // LDRB Rd,[Rb,Ro]
+ 0xE19000F0 // LDRSH Rd,[Rb,Ro]
+ };
+
+ *ainstr = subset[(tinstr & 0xE00) >> 9] // base
+ | ((tinstr & 0x0007) << (12 - 0)) // Rd
+ | ((tinstr & 0x0038) << (16 - 3)) // Rb
+ | ((tinstr & 0x01C0) >> 6); // Ro
+ break;
+ }
+
+ case 12: // STR Rd,[Rb,#imm5]
+ case 13: // LDR Rd,[Rb,#imm5]
+ case 14: // STRB Rd,[Rb,#imm5]
+ case 15: // LDRB Rd,[Rb,#imm5]
+ {
+ ARMword subset[4] = {
+ 0xE5800000, // STR Rd,[Rb,#imm5]
+ 0xE5900000, // LDR Rd,[Rb,#imm5]
+ 0xE5C00000, // STRB Rd,[Rb,#imm5]
+ 0xE5D00000 // LDRB Rd,[Rb,#imm5]
+ };
+ // The offset range defends on whether we are transferring a byte or word value:
+ *ainstr = subset[(tinstr & 0x1800) >> 11] // base
+ | ((tinstr & 0x0007) << (12 - 0)) // Rd
+ | ((tinstr & 0x0038) << (16 - 3)) // Rb
+ | ((tinstr & 0x07C0) >> (6 - ((tinstr & (1 << 12)) ? 0 : 2))); // off5
+ }
+ break;
+
+ case 16: // STRH Rd,[Rb,#imm5]
+ case 17: // LDRH Rd,[Rb,#imm5]
+ *ainstr = ((tinstr & (1 << 11)) // base
+ ? 0xE1D000B0 // LDRH
+ : 0xE1C000B0) // STRH
+ | ((tinstr & 0x0007) << (12 - 0)) // Rd
+ | ((tinstr & 0x0038) << (16 - 3)) // Rb
+ | ((tinstr & 0x01C0) >> (6 - 1)) // off5, low nibble
+ | ((tinstr & 0x0600) >> (9 - 8)); // off5, high nibble
+ break;
+
+ case 18: // STR Rd,[SP,#imm8]
+ case 19: // LDR Rd,[SP,#imm8]
+ *ainstr = ((tinstr & (1 << 11)) // base
+ ? 0xE59D0000 // LDR
+ : 0xE58D0000) // STR
+ | ((tinstr & 0x0700) << (12 - 8)) // Rd
+ | ((tinstr & 0x00FF) << 2); // off8
+ break;
+
+ case 20: // ADD Rd,PC,#imm8
+ case 21: // ADD Rd,SP,#imm8
+
+ if ((tinstr & (1 << 11)) == 0) {
+
+ // NOTE: The PC value used here should by word aligned. We encode shift-left-by-2 in the
+ // rotate immediate field, so no shift of off8 is needed.
+
+ *ainstr = 0xE28F0F00 // base
+ | ((tinstr & 0x0700) << (12 - 8)) // Rd
+ | (tinstr & 0x00FF); // off8
+ }
+ else {
+ // We encode shift-left-by-2 in the rotate immediate field, so no shift of off8 is needed.
+ *ainstr = 0xE28D0F00 // base
+ | ((tinstr & 0x0700) << (12 - 8)) // Rd
+ | (tinstr & 0x00FF); // off8
+ }
+ break;
+
+ case 22:
+ case 23:
+ if ((tinstr & 0x0F00) == 0x0000) {
+ // NOTE: The instruction contains a shift left of 2 equivalent (implemented as ROR #30):
+ *ainstr = ((tinstr & (1 << 7)) // base
+ ? 0xE24DDF00 // SUB
+ : 0xE28DDF00) // ADD
+ | (tinstr & 0x007F); // off7
+ }
+ else if ((tinstr & 0x0F00) == 0x0E00) {
+ // BKPT T1
+ *ainstr = 0xEF000000 // base
+ | BITS(tinstr, 0, 3) // imm4 field;
+ | (BITS(tinstr, 4, 7) << 8); // beginning 4 bits of imm12
+ }
+ else if ((tinstr & 0x0F00) == 0x600) {
+ if (BIT(tinstr, 5) == 0) {
+ // SETEND T1
+ *ainstr = 0xF1010000 // base
+ | (BIT(tinstr, 3) << 9); // endian specifier
+ }
+ else {
+ // CPS T1
+ *ainstr = 0xF1080000 // base
+ | (BIT(tinstr, 0) << 6) // fiq bit
+ | (BIT(tinstr, 1) << 7) // irq bit
+ | (BIT(tinstr, 2) << 8) // abort bit
+ | (BIT(tinstr, 4) << 18); // enable bit
+ }
+ }
+ else if ((tinstr & 0x0F00) == 0x0a00) {
+ static const ARMword subset[3] = {
+ 0xE6BF0F30, // REV T1
+ 0xE6BF0FB0, // REV16 T1
+ 0xE6FF0FB0, // REVSH T1
+ };
+
+ *ainstr = subset[BITS(tinstr, 6, 7)] // base
+ | (BITS(tinstr, 0, 2) << 12) // Rd
+ | BITS(tinstr, 3, 5); // Rm
+ }
+ else if ((tinstr & 0x600) == 0x400)
+ {
+ /* Format 14 */
+ u32 subset[4] = {
+ 0xE92D0000, /* STMDB sp!,{rlist} */
+ 0xE92D4000, /* STMDB sp!,{rlist,lr} */
+ 0xE8BD0000, /* LDMIA sp!,{rlist} */
+ 0xE8BD8000 /* LDMIA sp!,{rlist,pc} */
+ };
+ *ainstr = subset[((tinstr & (1 << 11)) >> 10) | ((tinstr & (1 << 8)) >> 8)] /* base */
+ | (tinstr & 0x00FF); /* mask8 */
+ }
+ else
+ {
+ //e6bf1071 sxth r1, r1
+ //e6af1071 sxtb r1, r1
+ //e6ff1078 uxth r1, r8
+ //e6ef1078 uxtb r1, r8
+
+ u32 subset[4] = { //Bit 12 - 15 dest Bit 0 - 3 src
+ 0xe6ff0070, /* uxth */
+ 0xe6ef0070, /* uxtb */
+ 0xe6bf0070, /* sxth */
+ 0xe6af0070 /* sxtb */
+ };
+
+ if ((tinstr & 0xF00) == 0x200) //Bit(7) unsigned (set = sxt. cleared = uxt) Bit(6) byte (set = .xtb cleared = .xth) Bit 5-3 Rb src Bit 2-0 Rd dest
+ {
+ *ainstr = subset[((tinstr & (0x3 << 6)) >> 6)] |
+ (tinstr & 0x7) << 12 |
+ (tinstr & 0x38) >> 3;
+ }
+ else if ((tinstr & 0x0FC0) == 0x0A00){
+ u32 Destr = (tinstr & 0x7);
+ u32 srcr = ((tinstr >> 3) & 0x7);
+ *ainstr = 0xE6BF0F30 | srcr | (Destr << 12);
+
+ }
+ else
+ {
+ valid = t_undefined;
+ XDSERROR("unk thumb instr %04x", tinstr);
+ }
+
+ }
+ break;
+
+ case 24: // STMIA
+ case 25: // LDMIA
+ {
+ u32 Rb = (tinstr & 0x0700) >> 8;
+ if ((1 << Rb)&tinstr) //no write back if the register is in the list
+ {
+ *ainstr = ((tinstr & (1 << 11)) /* base */
+ ? 0xE8900000 /* LDMIA */
+ : 0xE8800000) /* STMIA */
+ | ((tinstr & 0x0700) << (16 - 8)) /* Rb */
+ | (tinstr & 0x00FF); /* mask8 */
+ break;
+ }
+ else
+ {
+ *ainstr = ((tinstr & (1 << 11)) /* base */
+ ? 0xE8B00000 /* LDMIA */
+ : 0xE8A00000) /* STMIA */
+ | ((tinstr & 0x0700) << (16 - 8)) /* Rb */
+ | (tinstr & 0x00FF); /* mask8 */
+ break;
+ }
+ }
+
+ case 26: // Bcc
+ case 27: // Bcc/SWI
+ if ((tinstr & 0x0F00) == 0x0F00) {
+ // Format 17 : SWI
+ *ainstr = 0xEF000000;
+ // Breakpoint must be handled specially.
+ if ((tinstr & 0x00FF) == 0x18)
+ *ainstr |= ((tinstr & 0x00FF) << 16);
+ // New breakpoint value. See gdb/arm-tdep.c
+ else if ((tinstr & 0x00FF) == 0xFE)
+ *ainstr |= SWI_Breakpoint;
+ else
+ *ainstr |= (tinstr & 0x00FF);
+ }
+ else if ((tinstr & 0x0F00) != 0x0E00)
+ valid = t_branch;
+ else // UNDEFINED : cc=1110(AL) uses different format
+ valid = t_undefined;
+
+ break;
+
+ case 28: // B
+ valid = t_branch;
+ break;
+
+ case 29:
+ if (tinstr & 0x1)
+ valid = t_undefined;
+ else
+ valid = t_branch;
+ break;
+
+ case 30: // BL instruction 1
+
+ // There is no single ARM instruction equivalent for this Thumb instruction. To keep the
+ // simulation simple (from the user perspective) we check if the following instruction is
+ // the second half of this BL, and if it is we simulate it immediately
+
+ valid = t_branch;
+ break;
+
+ case 31: // BL instruction 2
+
+ // There is no single ARM instruction equivalent for this instruction. Also, it should only
+ // ever be matched with the fmt19 "BL instruction 1" instruction. However, we do allow the
+ // simulation of it on its own, with undefined results if r14 is not suitably initialised.
+
+ valid = t_branch;
+ break;
+ }
+
+ *inst_size = 2;
+
+ return valid;
+}
\ No newline at end of file
--- /dev/null
+/* Copyright (C)
+* 2011 - Michael.Kang blackfin.kang@gmail.com
+* 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
+* of the License, 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.
+*
+*/
+
+/**
+* @file arm_dyncom_thumb.h
+* @brief The thumb dyncom
+* @author Michael.Kang blackfin.kang@gmail.com
+* @version 78.77
+* @date 2011-11-07
+*/
+
+#ifndef __ARM_DYNCOM_THUMB_H__
+#define __ARM_DYNCOM_THUMB_H__
+
+#include "arm/skyeye_common/armdefs.h"
+#include "arm/skyeye_common/skyeye_types.h"
+
+#ifndef _ARM_INTERPRETER_H
+enum tdstate {
+ t_undefined, // Undefined Thumb instruction
+ t_decoded, // Instruction decoded to ARM equivalent
+ t_branch, // Thumb branch (already processed)
+ t_uninitialized,
+};
+#endif
+
+tdstate thumb_translate(addr_t addr, u32 instr, u32* ainstr, u32* inst_size);
+
+static inline u32 get_thumb_instr(u32 instr, addr_t pc) {
+ u32 tinstr;
+ if ((pc & 0x3) != 0)
+ tinstr = instr >> 16;
+ else
+ tinstr = instr & 0xFFFF;
+ return tinstr;
+}
+
+#endif
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "Kernel.h"
+
+//#include "core.h"
+
+const static cpu_config_t arm11_cpu_info = {
+ "armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE
+};
+
+ARM_Interpreter::ARM_Interpreter() {
+
+ state = new ARMul_State;
+ state->m_MemoryMap = NULL;
+
+
+ ARMul_EmulateInit();
+ memset(state, 0, sizeof(ARMul_State));
+
+ ARMul_NewState(state);
+
+ state->abort_model = 0;
+ state->cpu = (cpu_config_t*)&arm11_cpu_info;
+ state->bigendSig = LOW;
+
+ ARMul_SelectProcessor(state, ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop);
+ state->lateabtSig = LOW;
+
+ // Reset the core to initial state
+ ARMul_CoProInit(state);
+ ARMul_Reset(state);
+ state->NextInstr = RESUME; // NOTE: This will be overwritten by LoadContext
+ state->Emulate = 3;
+
+ state->pc = state->Reg[15] = 0x00000000;
+ state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack
+ state->servaddr = 0xFFFF0000;
+}
+
+ARM_Interpreter::~ARM_Interpreter() {
+ delete state;
+}
+
+void ARM_Interpreter::SetPC(u32 pc) {
+ state->pc = state->Reg[15] = pc;
+}
+
+u32 ARM_Interpreter::GetPC() const {
+ return state->pc;
+}
+
+u32 ARM_Interpreter::GetReg(int index) const {
+ return state->Reg[index];
+}
+
+void ARM_Interpreter::SetReg(int index, u32 value) {
+ state->Reg[index] = value;
+}
+
+u32 ARM_Interpreter::GetCPSR() const {
+ return state->Cpsr;
+}
+
+void ARM_Interpreter::SetCPSR(u32 cpsr) {
+ state->Cpsr = cpsr;
+}
+
+u64 ARM_Interpreter::GetTicks() const {
+ return state->NumInstrs;
+}
+
+void ARM_Interpreter::AddTicks(u64 ticks) {
+ state->NumInstrs += ticks;
+}
+
+u64 ARM_Interpreter::ExecuteInstructions(int num_instructions) {
+ state->NumInstrsToExecute = num_instructions - 1;
+ return ARMul_Emulate32(state);
+}
+
+void ARM_Interpreter::SaveContext(ThreadContext& ctx) {
+ memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers));
+ memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers));
+
+ ctx.sp = state->Reg[13];
+ ctx.lr = state->Reg[14];
+ ctx.pc = state->pc;
+ ctx.cpsr = state->Cpsr;
+
+ ctx.fpscr = state->VFP[1];
+ ctx.fpexc = state->VFP[2];
+
+ ctx.reg_15 = state->Reg[15];
+ ctx.mode = state->NextInstr;
+}
+
+void ARM_Interpreter::LoadContext(const ThreadContext& ctx) {
+ memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers));
+ memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers));
+
+ state->Reg[13] = ctx.sp;
+ state->Reg[14] = ctx.lr;
+ state->pc = ctx.pc;
+ state->Cpsr = ctx.cpsr;
+
+ state->VFP[1] = ctx.fpscr;
+ state->VFP[2] = ctx.fpexc;
+
+ state->Reg[15] = ctx.reg_15;
+ state->NextInstr = ctx.mode;
+}
+
+void ARM_Interpreter::PrepareReschedule() {
+ state->NumInstrsToExecute = 0;
+}
--- /dev/null
+
+
+#ifndef _ARM_INTERPRETER_H
+#define _ARM_INTERPRETER_H
+
+#include "Common.h"
+
+#include "arm/arm_interface.h"
+#include "arm/skyeye_common/armdefs.h"
+#include "arm/skyeye_common/armemu.h"
+
+class ARM_Interpreter final : virtual public ARM_Interface {
+public:
+
+ ARM_Interpreter();
+ ~ARM_Interpreter();
+
+ /**
+ * Set the Program Counter to an address
+ * @param pc Address to set PC to
+ */
+ void SetPC(u32 pc) override;
+
+ /*
+ * Get the current Program Counter
+ * @return Returns current PC
+ */
+ u32 GetPC() const override;
+
+ /**
+ * Get an ARM register
+ * @param index Register index (0-15)
+ * @return Returns the value in the register
+ */
+ u32 GetReg(int index) const override;
+
+ /**
+ * Set an ARM register
+ * @param index Register index (0-15)
+ * @param value Value to set register to
+ */
+ void SetReg(int index, u32 value) override;
+
+ /**
+ * Get the current CPSR register
+ * @return Returns the value of the CPSR register
+ */
+ u32 GetCPSR() const override;
+
+ /**
+ * Set the current CPSR register
+ * @param cpsr Value to set CPSR to
+ */
+ void SetCPSR(u32 cpsr) override;
+
+ /**
+ * Returns the number of clock ticks since the last reset
+ * @return Returns number of clock ticks
+ */
+ u64 GetTicks() const override;
+
+ /**
+ * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
+ * @param ticks Number of ticks to advance the CPU core
+ */
+ void AddTicks(u64 ticks) override;
+
+ /**
+ * Saves the current CPU context
+ * @param ctx Thread context to save
+ */
+ void SaveContext(ThreadContext& ctx) override;
+
+ /**
+ * Loads a CPU context
+ * @param ctx Thread context to load
+ */
+ void LoadContext(const ThreadContext& ctx) override;
+
+ /// Prepare core for thread reschedule (if needed to correctly handle state)
+ void PrepareReschedule() override;
+ ARMul_State* state;
+
+protected:
+
+ /**
+ * Executes the given number of instructions
+ * @param num_instructions Number of instructions to executes
+ */
+ u64 ExecuteInstructions(int num_instructions) override;
+
+
+};
+#endif
--- /dev/null
+/* armcopro.c -- co-processor interface: ARM6 Instruction Emulator.
+ Copyright (C) 1994, 2000 Advanced RISC Machines Ltd.
+
+ 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 of the License, 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. */
+
+#include "Kernel.h"
+
+#include "arm/skyeye_common/armdefs.h"
+#include "arm/skyeye_common/armemu.h"
+#include "arm/skyeye_common/vfp/vfp.h"
+
+//chy 2005-07-08
+//#include "ansidecl.h"
+//chy -------
+//#include "iwmmxt.h"
+
+/* Dummy Co-processors. */
+
+static unsigned
+NoCoPro3R(ARMul_State * state,
+unsigned a, ARMword b)
+{
+ return ARMul_CANT;
+}
+
+static unsigned
+NoCoPro4R(ARMul_State * state,
+unsigned a,
+ARMword b, ARMword c)
+{
+ return ARMul_CANT;
+}
+
+static unsigned
+NoCoPro4W(ARMul_State * state,
+unsigned a,
+ARMword b, ARMword * c)
+{
+ return ARMul_CANT;
+}
+
+static unsigned
+NoCoPro5R(ARMul_State * state,
+unsigned a,
+ARMword b,
+ARMword c, ARMword d)
+{
+ return ARMul_CANT;
+}
+
+static unsigned
+NoCoPro5W(ARMul_State * state,
+unsigned a,
+ARMword b,
+ARMword * c, ARMword * d)
+{
+ return ARMul_CANT;
+}
+
+/* The XScale Co-processors. */
+
+/* Coprocessor 15: System Control. */
+static void write_cp14_reg(unsigned, ARMword);
+static ARMword read_cp14_reg(unsigned);
+
+/* Check an access to a register. */
+
+static unsigned
+check_cp15_access(ARMul_State * state,
+unsigned reg,
+unsigned CRm, unsigned opcode_1, unsigned opcode_2)
+{
+ /* Do not allow access to these register in USER mode. */
+ //chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
+ if (state->Mode == USER26MODE || state->Mode == USER32MODE)
+ return ARMul_CANT;
+
+ /* Opcode_1should be zero. */
+ if (opcode_1 != 0)
+ return ARMul_CANT;
+
+ /* Different register have different access requirements. */
+ switch (reg) {
+ case 0:
+ case 1:
+ /* CRm must be 0. Opcode_2 can be anything. */
+ if (CRm != 0)
+ return ARMul_CANT;
+ break;
+ case 2:
+ case 3:
+ /* CRm must be 0. Opcode_2 must be zero. */
+ if ((CRm != 0) || (opcode_2 != 0))
+ return ARMul_CANT;
+ break;
+ case 4:
+ /* Access not allowed. */
+ return ARMul_CANT;
+ case 5:
+ case 6:
+ /* Opcode_2 must be zero. CRm must be 0. */
+ if ((CRm != 0) || (opcode_2 != 0))
+ return ARMul_CANT;
+ break;
+ case 7:
+ /* Permissable combinations:
+ Opcode_2 CRm
+ 0 5
+ 0 6
+ 0 7
+ 1 5
+ 1 6
+ 1 10
+ 4 10
+ 5 2
+ 6 5 */
+ switch (opcode_2) {
+ default:
+ return ARMul_CANT;
+ case 6:
+ if (CRm != 5)
+ return ARMul_CANT;
+ break;
+ case 5:
+ if (CRm != 2)
+ return ARMul_CANT;
+ break;
+ case 4:
+ if (CRm != 10)
+ return ARMul_CANT;
+ break;
+ case 1:
+ if ((CRm != 5) && (CRm != 6) && (CRm != 10))
+ return ARMul_CANT;
+ break;
+ case 0:
+ if ((CRm < 5) || (CRm > 7))
+ return ARMul_CANT;
+ break;
+ }
+ break;
+
+ case 8:
+ /* Permissable combinations:
+ Opcode_2 CRm
+ 0 5
+ 0 6
+ 0 7
+ 1 5
+ 1 6 */
+ if (opcode_2 > 1)
+ return ARMul_CANT;
+ if ((CRm < 5) || (CRm > 7))
+ return ARMul_CANT;
+ if (opcode_2 == 1 && CRm == 7)
+ return ARMul_CANT;
+ break;
+ case 9:
+ /* Opcode_2 must be zero or one. CRm must be 1 or 2. */
+ if (((CRm != 0) && (CRm != 1))
+ || ((opcode_2 != 1) && (opcode_2 != 2)))
+ return ARMul_CANT;
+ break;
+ case 10:
+ /* Opcode_2 must be zero or one. CRm must be 4 or 8. */
+ if (((CRm != 0) && (CRm != 1))
+ || ((opcode_2 != 4) && (opcode_2 != 8)))
+ return ARMul_CANT;
+ break;
+ case 11:
+ /* Access not allowed. */
+ return ARMul_CANT;
+ case 12:
+ /* Access not allowed. */
+ return ARMul_CANT;
+ case 13:
+ /* Opcode_2 must be zero. CRm must be 0. */
+ if ((CRm != 0) || (opcode_2 != 0))
+ return ARMul_CANT;
+ break;
+ case 14:
+ /* Opcode_2 must be 0. CRm must be 0, 3, 4, 8 or 9. */
+ if (opcode_2 != 0)
+ return ARMul_CANT;
+
+ if ((CRm != 0) && (CRm != 3) && (CRm != 4) && (CRm != 8)
+ && (CRm != 9))
+ return ARMul_CANT;
+ break;
+ case 15:
+ /* Opcode_2 must be zero. CRm must be 1. */
+ if ((CRm != 1) || (opcode_2 != 0))
+ return ARMul_CANT;
+ break;
+ default:
+ /* Should never happen. */
+ return ARMul_CANT;
+ }
+
+ return ARMul_DONE;
+}
+
+/* Install co-processor instruction handlers in this routine. */
+
+unsigned
+ARMul_CoProInit(ARMul_State * state)
+{
+ unsigned int i;
+
+ /* Initialise tham all first. */
+ for (i = 0; i < 16; i++)
+ ARMul_CoProDetach(state, i);
+
+ /* Install CoPro Instruction handlers here.
+ The format is:
+ ARMul_CoProAttach (state, CP Number, Init routine, Exit routine
+ LDC routine, STC routine, MRC routine, MCR routine,
+ CDP routine, Read Reg routine, Write Reg routine). */
+ if (state->is_v6) {
+ ARMul_CoProAttach(state, 10, VFPInit, NULL, VFPLDC, VFPSTC,
+ VFPMRC, VFPMCR, VFPMRRC, VFPMCRR, VFPCDP, NULL, NULL);
+ ARMul_CoProAttach(state, 11, VFPInit, NULL, VFPLDC, VFPSTC,
+ VFPMRC, VFPMCR, VFPMRRC, VFPMCRR, VFPCDP, NULL, NULL);
+
+ /*ARMul_CoProAttach (state, 15, MMUInit, NULL, NULL, NULL,
+ MMUMRC, MMUMCR, NULL, NULL, NULL, NULL, NULL);*/
+ }
+ //chy 2003-09-03 do it in future!!!!????
+#if 0
+ if (state->is_iWMMXt) {
+ ARMul_CoProAttach(state, 0, NULL, NULL, IwmmxtLDC, IwmmxtSTC,
+ NULL, NULL, IwmmxtCDP, NULL, NULL);
+
+ ARMul_CoProAttach(state, 1, NULL, NULL, NULL, NULL,
+ IwmmxtMRC, IwmmxtMCR, IwmmxtCDP, NULL,
+ NULL);
+ }
+#endif
+ /* No handlers below here. */
+
+ /* Call all the initialisation routines. */
+ for (i = 0; i < 16; i++)
+ if (state->CPInit[i])
+ (state->CPInit[i]) (state);
+
+ return TRUE;
+}
+
+/* Install co-processor finalisation routines in this routine. */
+
+void
+ARMul_CoProExit(ARMul_State * state)
+{
+ register unsigned i;
+
+ for (i = 0; i < 16; i++)
+ if (state->CPExit[i])
+ (state->CPExit[i]) (state);
+
+ for (i = 0; i < 16; i++) /* Detach all handlers. */
+ ARMul_CoProDetach(state, i);
+}
+
+/* Routines to hook Co-processors into ARMulator. */
+
+void
+ARMul_CoProAttach(ARMul_State * state,
+unsigned number,
+ARMul_CPInits * init,
+ARMul_CPExits * exit,
+ARMul_LDCs * ldc,
+ARMul_STCs * stc,
+ARMul_MRCs * mrc,
+ARMul_MCRs * mcr,
+ARMul_MRRCs * mrrc,
+ARMul_MCRRs * mcrr,
+ARMul_CDPs * cdp,
+ARMul_CPReads * read, ARMul_CPWrites * write)
+{
+ if (init != NULL)
+ state->CPInit[number] = init;
+ if (exit != NULL)
+ state->CPExit[number] = exit;
+ if (ldc != NULL)
+ state->LDC[number] = ldc;
+ if (stc != NULL)
+ state->STC[number] = stc;
+ if (mrc != NULL)
+ state->MRC[number] = mrc;
+ if (mcr != NULL)
+ state->MCR[number] = mcr;
+ if (mrrc != NULL)
+ state->MRRC[number] = mrrc;
+ if (mcrr != NULL)
+ state->MCRR[number] = mcrr;
+ if (cdp != NULL)
+ state->CDP[number] = cdp;
+ if (read != NULL)
+ state->CPRead[number] = read;
+ if (write != NULL)
+ state->CPWrite[number] = write;
+}
+
+void
+ARMul_CoProDetach(ARMul_State * state, unsigned number)
+{
+ ARMul_CoProAttach(state, number, NULL, NULL,
+ NoCoPro4R, NoCoPro4W, NoCoPro4W, NoCoPro4R,
+ NoCoPro5W, NoCoPro5R, NoCoPro3R, NULL, NULL);
+
+ state->CPInit[number] = NULL;
+ state->CPExit[number] = NULL;
+ state->CPRead[number] = NULL;
+ state->CPWrite[number] = NULL;
+}
--- /dev/null
+/* armemu.c -- Main instruction emulation: ARM7 Instruction Emulator.
+ Copyright (C) 1994 Advanced RISC Machines Ltd.
+ Modifications to add arch. v4 support by <jsmith@cygnus.com>.
+
+ 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 of the License, 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. */
+
+//#include <util.h> // DEBUG()
+
+#include "Kernel.h"
+
+#include "arm/skyeye_common/arm_regformat.h"
+#include "arm/skyeye_common/armdefs.h"
+#include "arm/skyeye_common/armemu.h"
+
+#include "arm/memory.h"
+
+//#include "hle/hle.h"
+//#include "svc.h"
+
+//ichfly
+//#define callstacker 1
+
+//#include "skyeye_callback.h"
+//#include "skyeye_bus.h"
+//#include "sim_control.h"
+//#include "skyeye_pref.h"
+//#include "skyeye.h"
+//#include "skyeye2gdb.h"
+//#include "code_cov.h"
+
+//#include "iwmmxt.h"
+//chy 2003-07-11: for debug instrs
+//extern int skyeye_instr_debug;
+extern FILE *skyeye_logfd;
+
+static ARMword GetDPRegRHS (ARMul_State *, ARMword);
+static ARMword GetDPSRegRHS (ARMul_State *, ARMword);
+static void WriteR15 (ARMul_State *, ARMword);
+static void WriteSR15 (ARMul_State *, ARMword);
+static void WriteR15Branch (ARMul_State *, ARMword);
+static ARMword GetLSRegRHS (ARMul_State *, ARMword);
+static ARMword GetLS7RHS (ARMul_State *, ARMword);
+static unsigned LoadWord (ARMul_State *, ARMword, ARMword);
+static unsigned LoadHalfWord (ARMul_State *, ARMword, ARMword, int);
+static unsigned LoadByte (ARMul_State *, ARMword, ARMword, int);
+static unsigned StoreWord (ARMul_State *, ARMword, ARMword);
+static unsigned StoreHalfWord (ARMul_State *, ARMword, ARMword);
+static unsigned StoreByte (ARMul_State *, ARMword, ARMword);
+static void LoadMult (ARMul_State *, ARMword, ARMword, ARMword);
+static void StoreMult (ARMul_State *, ARMword, ARMword, ARMword);
+static void LoadSMult (ARMul_State *, ARMword, ARMword, ARMword);
+static void StoreSMult (ARMul_State *, ARMword, ARMword, ARMword);
+static unsigned Multiply64 (ARMul_State *, ARMword, int, int);
+static unsigned MultiplyAdd64 (ARMul_State *, ARMword, int, int);
+static void Handle_Load_Double (ARMul_State *, ARMword);
+static void Handle_Store_Double (ARMul_State *, ARMword);
+
+static int
+handle_v6_insn (ARMul_State * state, ARMword instr);
+
+#define LUNSIGNED (0) /* unsigned operation */
+#define LSIGNED (1) /* signed operation */
+#define LDEFAULT (0) /* default : do nothing */
+#define LSCC (1) /* set condition codes on result */
+
+#ifdef NEED_UI_LOOP_HOOK
+/* How often to run the ui_loop update, when in use. */
+#define UI_LOOP_POLL_INTERVAL 0x32000
+
+/* Counter for the ui_loop_hook update. */
+static int ui_loop_hook_counter = UI_LOOP_POLL_INTERVAL;
+
+/* Actual hook to call to run through gdb's gui event loop. */
+extern int (*ui_loop_hook) (int);
+#endif /* NEED_UI_LOOP_HOOK */
+
+/* Short-hand macros for LDR/STR. */
+
+/* Store post decrement writeback. */
+#define SHDOWNWB() \
+ lhs = LHS ; \
+ if (StoreHalfWord (state, instr, lhs)) \
+ LSBase = lhs - GetLS7RHS (state, instr);
+
+/* Store post increment writeback. */
+#define SHUPWB() \
+ lhs = LHS ; \
+ if (StoreHalfWord (state, instr, lhs)) \
+ LSBase = lhs + GetLS7RHS (state, instr);
+
+/* Store pre decrement. */
+#define SHPREDOWN() \
+ (void)StoreHalfWord (state, instr, LHS - GetLS7RHS (state, instr));
+
+/* Store pre decrement writeback. */
+#define SHPREDOWNWB() \
+ temp = LHS - GetLS7RHS (state, instr); \
+ if (StoreHalfWord (state, instr, temp)) \
+ LSBase = temp;
+
+/* Store pre increment. */
+#define SHPREUP() \
+ (void)StoreHalfWord (state, instr, LHS + GetLS7RHS (state, instr));
+
+/* Store pre increment writeback. */
+#define SHPREUPWB() \
+ temp = LHS + GetLS7RHS (state, instr); \
+ if (StoreHalfWord (state, instr, temp)) \
+ LSBase = temp;
+
+/* Load post decrement writeback. */
+#define LHPOSTDOWN() \
+{ \
+ int done = 1; \
+ lhs = LHS; \
+ temp = lhs - GetLS7RHS (state, instr); \
+ \
+ switch (BITS (5, 6)) \
+ { \
+ case 1: /* H */ \
+ if (LoadHalfWord (state, instr, lhs, LUNSIGNED)) \
+ LSBase = temp; \
+ break; \
+ case 2: /* SB */ \
+ if (LoadByte (state, instr, lhs, LSIGNED)) \
+ LSBase = temp; \
+ break; \
+ case 3: /* SH */ \
+ if (LoadHalfWord (state, instr, lhs, LSIGNED)) \
+ LSBase = temp; \
+ break; \
+ case 0: /* SWP handled elsewhere. */ \
+ default: \
+ done = 0; \
+ break; \
+ } \
+ if (done) \
+ break; \
+}
+
+/* Load post increment writeback. */
+#define LHPOSTUP() \
+{ \
+ int done = 1; \
+ lhs = LHS; \
+ temp = lhs + GetLS7RHS (state, instr); \
+ \
+ switch (BITS (5, 6)) \
+ { \
+ case 1: /* H */ \
+ if (LoadHalfWord (state, instr, lhs, LUNSIGNED)) \
+ LSBase = temp; \
+ break; \
+ case 2: /* SB */ \
+ if (LoadByte (state, instr, lhs, LSIGNED)) \
+ LSBase = temp; \
+ break; \
+ case 3: /* SH */ \
+ if (LoadHalfWord (state, instr, lhs, LSIGNED)) \
+ LSBase = temp; \
+ break; \
+ case 0: /* SWP handled elsewhere. */ \
+ default: \
+ done = 0; \
+ break; \
+ } \
+ if (done) \
+ break; \
+}
+
+/* Load pre decrement. */
+#define LHPREDOWN() \
+{ \
+ int done = 1; \
+ \
+ temp = LHS - GetLS7RHS (state, instr); \
+ switch (BITS (5, 6)) \
+ { \
+ case 1: /* H */ \
+ (void) LoadHalfWord (state, instr, temp, LUNSIGNED); \
+ break; \
+ case 2: /* SB */ \
+ (void) LoadByte (state, instr, temp, LSIGNED); \
+ break; \
+ case 3: /* SH */ \
+ (void) LoadHalfWord (state, instr, temp, LSIGNED); \
+ break; \
+ case 0: \
+ /* SWP handled elsewhere. */ \
+ default: \
+ done = 0; \
+ break; \
+ } \
+ if (done) \
+ break; \
+}
+
+/* Load pre decrement writeback. */
+#define LHPREDOWNWB() \
+{ \
+ int done = 1; \
+ \
+ temp = LHS - GetLS7RHS (state, instr); \
+ switch (BITS (5, 6)) \
+ { \
+ case 1: /* H */ \
+ if (LoadHalfWord (state, instr, temp, LUNSIGNED)) \
+ LSBase = temp; \
+ break; \
+ case 2: /* SB */ \
+ if (LoadByte (state, instr, temp, LSIGNED)) \
+ LSBase = temp; \
+ break; \
+ case 3: /* SH */ \
+ if (LoadHalfWord (state, instr, temp, LSIGNED)) \
+ LSBase = temp; \
+ break; \
+ case 0: \
+ /* SWP handled elsewhere. */ \
+ default: \
+ done = 0; \
+ break; \
+ } \
+ if (done) \
+ break; \
+}
+
+/* Load pre increment. */
+#define LHPREUP() \
+{ \
+ int done = 1; \
+ \
+ temp = LHS + GetLS7RHS (state, instr); \
+ switch (BITS (5, 6)) \
+ { \
+ case 1: /* H */ \
+ (void) LoadHalfWord (state, instr, temp, LUNSIGNED); \
+ break; \
+ case 2: /* SB */ \
+ (void) LoadByte (state, instr, temp, LSIGNED); \
+ break; \
+ case 3: /* SH */ \
+ (void) LoadHalfWord (state, instr, temp, LSIGNED); \
+ break; \
+ case 0: \
+ /* SWP handled elsewhere. */ \
+ default: \
+ done = 0; \
+ break; \
+ } \
+ if (done) \
+ break; \
+}
+
+/* Load pre increment writeback. */
+#define LHPREUPWB() \
+{ \
+ int done = 1; \
+ \
+ temp = LHS + GetLS7RHS (state, instr); \
+ switch (BITS (5, 6)) \
+ { \
+ case 1: /* H */ \
+ if (LoadHalfWord (state, instr, temp, LUNSIGNED)) \
+ LSBase = temp; \
+ break; \
+ case 2: /* SB */ \
+ if (LoadByte (state, instr, temp, LSIGNED)) \
+ LSBase = temp; \
+ break; \
+ case 3: /* SH */ \
+ if (LoadHalfWord (state, instr, temp, LSIGNED)) \
+ LSBase = temp; \
+ break; \
+ case 0: \
+ /* SWP handled elsewhere. */ \
+ default: \
+ done = 0; \
+ break; \
+ } \
+ if (done) \
+ break; \
+}
+
+/*ywc 2005-03-31*/
+//teawater add for arm2x86 2005.02.17-------------------------------------------
+#ifdef DBCT
+#include "dbct/tb.h"
+#include "dbct/arm2x86_self.h"
+#endif
+//AJ2D--------------------------------------------------------------------------
+
+//Diff register
+unsigned int mirror_register_file[39];
+
+/* EMULATION of ARM6. */
+
+extern int debugmode;
+int ARMul_ICE_debug(ARMul_State *state,ARMword instr,ARMword addr);
+#ifdef MODE32
+//chy 2006-04-12, for ICE debug
+int ARMul_ICE_debug(ARMul_State *state,ARMword instr,ARMword addr)
+{
+ return 0;
+}
+
+static int dump = 0;
+ARMword ARMul_Debug(ARMul_State * state, ARMword pc, ARMword instr)
+{
+ /*printf("[%08x] ", pc);
+ arm11_Disasm32(pc);*/
+
+ /*if (pc >= 0x0010303C && pc <= 0x00103050)
+ {
+ printf("[%08x] = %08X = ", pc, instr);
+ arm11_Disasm32(pc);
+ arm11_Dump();
+ }*/
+
+ //fprintf(stderr,"[%08x]\n", pc);
+
+ //if (pc == 0x00240C88)
+ // arm11_Dump();
+
+ /*if (pc == 0x188e04)
+ {
+ DEBUG("read %08X %08X %016X %08X %08X from %08X", state->Reg[0], state->Reg[1], state->Reg[2] | state->Reg[3] << 32, mem_Read32(state->Reg[13]), mem_Read32(state->Reg[13] + 4), state->Reg[14]);
+ }
+ if (pc == 0x21222c)
+ {
+ arm11_Dump();
+ mem_Dbugdump();
+ }*/
+
+ /*if (pc == 0x0022D168)
+ {
+ int j = 0;
+ }*/
+
+ /*if (state->Reg[4] == 0x00105734)
+ {
+ printf("[%08x] ", pc);
+ arm11_Disasm32(pc);
+ }*/
+
+ return 0;
+}
+
+/*
+void chy_debug()
+{
+ printf("SkyEye chy_deubeg begin\n");
+}
+*/
+ARMword
+ARMul_Emulate32 (ARMul_State * state)
+#else
+ARMword
+ARMul_Emulate26 (ARMul_State * state)
+#endif
+{
+ /* The PC pipeline value depends on whether ARM
+ or Thumb instructions are being
+ d. */
+ ARMword isize;
+ ARMword instr; /* The current instruction. */
+ ARMword dest = 0; /* Almost the DestBus. */
+ ARMword temp; /* Ubiquitous third hand. */
+ ARMword pc = 0; /* The address of the current instruction. */
+ ARMword lhs; /* Almost the ABus and BBus. */
+ ARMword rhs;
+ ARMword decoded = 0; /* Instruction pipeline. */
+ ARMword loaded = 0;
+ ARMword decoded_addr=0;
+ ARMword loaded_addr=0;
+ ARMword have_bp=0;
+
+#ifdef callstacker
+ char a[256];
+#endif
+ /* shenoubang */
+ static int instr_sum = 0;
+ int reg_index = 0;
+#if DIFF_STATE
+//initialize all mirror register for follow mode
+ for (reg_index = 0; reg_index < 16; reg_index ++) {
+ mirror_register_file[reg_index] = state->Reg[reg_index];
+ }
+ mirror_register_file[CPSR_REG] = state->Cpsr;
+ mirror_register_file[R13_SVC] = state->RegBank[SVCBANK][13];
+ mirror_register_file[R14_SVC] = state->RegBank[SVCBANK][14];
+ mirror_register_file[R13_ABORT] = state->RegBank[ABORTBANK][13];
+ mirror_register_file[R14_ABORT] = state->RegBank[ABORTBANK][14];
+ mirror_register_file[R13_UNDEF] = state->RegBank[UNDEFBANK][13];
+ mirror_register_file[R14_UNDEF] = state->RegBank[UNDEFBANK][14];
+ mirror_register_file[R13_IRQ] = state->RegBank[IRQBANK][13];
+ mirror_register_file[R14_IRQ] = state->RegBank[IRQBANK][14];
+ mirror_register_file[R8_FIRQ] = state->RegBank[FIQBANK][8];
+ mirror_register_file[R9_FIRQ] = state->RegBank[FIQBANK][9];
+ mirror_register_file[R10_FIRQ] = state->RegBank[FIQBANK][10];
+ mirror_register_file[R11_FIRQ] = state->RegBank[FIQBANK][11];
+ mirror_register_file[R12_FIRQ] = state->RegBank[FIQBANK][12];
+ mirror_register_file[R13_FIRQ] = state->RegBank[FIQBANK][13];
+ mirror_register_file[R14_FIRQ] = state->RegBank[FIQBANK][14];
+ mirror_register_file[SPSR_SVC] = state->Spsr[SVCBANK];
+ mirror_register_file[SPSR_ABORT] = state->Spsr[ABORTBANK];
+ mirror_register_file[SPSR_UNDEF] = state->Spsr[UNDEFBANK];
+ mirror_register_file[SPSR_IRQ] = state->Spsr[IRQBANK];
+ mirror_register_file[SPSR_FIRQ] = state->Spsr[FIQBANK];
+#endif
+ /* Execute the next instruction. */
+ if (state->NextInstr < PRIMEPIPE) {
+ decoded = state->decoded;
+ loaded = state->loaded;
+ pc = state->pc;
+ //chy 2006-04-12, for ICE debug
+ decoded_addr=state->decoded_addr;
+ loaded_addr=state->loaded_addr;
+ }
+
+ do {
+ //print_func_name(state->pc);
+ /* Just keep going. */
+ isize = INSN_SIZE;
+
+ switch (state->NextInstr) {
+ case SEQ:
+ /* Advance the pipeline, and an S cycle. */
+ state->Reg[15] += isize;
+ pc += isize;
+ instr = decoded;
+ //chy 2006-04-12, for ICE debug
+ have_bp = ARMul_ICE_debug(state,instr,decoded_addr);
+ decoded = loaded;
+ decoded_addr=loaded_addr;
+ //loaded = ARMul_LoadInstrS (state, pc + (isize * 2),
+ // isize);
+ loaded_addr=pc + (isize * 2);
+ if (have_bp) goto TEST_EMULATE;
+ break;
+
+ case NONSEQ:
+ /* Advance the pipeline, and an N cycle. */
+ state->Reg[15] += isize;
+ pc += isize;
+ instr = decoded;
+ //chy 2006-04-12, for ICE debug
+ have_bp=ARMul_ICE_debug(state,instr,decoded_addr);
+ decoded = loaded;
+ decoded_addr=loaded_addr;
+ //loaded = ARMul_LoadInstrN (state, pc + (isize * 2),
+ // isize);
+ loaded_addr=pc + (isize * 2);
+ NORMALCYCLE;
+ if (have_bp) goto TEST_EMULATE;
+ break;
+
+ case PCINCEDSEQ:
+ /* Program counter advanced, and an S cycle. */
+ pc += isize;
+ instr = decoded;
+ //chy 2006-04-12, for ICE debug
+ have_bp=ARMul_ICE_debug(state,instr,decoded_addr);
+ decoded = loaded;
+ decoded_addr=loaded_addr;
+ //loaded = ARMul_LoadInstrS (state, pc + (isize * 2),
+ // isize);
+ loaded_addr=pc + (isize * 2);
+ NORMALCYCLE;
+ if (have_bp) goto TEST_EMULATE;
+ break;
+
+ case PCINCEDNONSEQ:
+ /* Program counter advanced, and an N cycle. */
+ pc += isize;
+ instr = decoded;
+ //chy 2006-04-12, for ICE debug
+ have_bp=ARMul_ICE_debug(state,instr,decoded_addr);
+ decoded = loaded;
+ decoded_addr=loaded_addr;
+ //loaded = ARMul_LoadInstrN (state, pc + (isize * 2),
+ // isize);
+ loaded_addr=pc + (isize * 2);
+ NORMALCYCLE;
+ if (have_bp) goto TEST_EMULATE;
+ break;
+
+ case RESUME:
+ /* The program counter has been changed. */
+ pc = state->Reg[15];
+#ifndef MODE32
+ pc = pc & R15PCBITS;
+#endif
+ state->Reg[15] = pc + (isize * 2);
+ state->Aborted = 0;
+ //chy 2004-05-25, fix bug provided by Carl van Schaik<cvansch@cse.unsw.EDU.AU>
+ state->AbortAddr = 1;
+
+ instr = ARMul_LoadInstrN (state, pc, isize);
+ //instr = ARMul_ReLoadInstr (state, pc, isize);
+ //chy 2006-04-12, for ICE debug
+ have_bp=ARMul_ICE_debug(state,instr,pc);
+ //decoded =
+ // ARMul_ReLoadInstr (state, pc + isize, isize);
+ decoded_addr=pc+isize;
+ //loaded = ARMul_ReLoadInstr (state, pc + isize * 2,
+ // isize);
+ loaded_addr=pc + isize * 2;
+ NORMALCYCLE;
+ if (have_bp) goto TEST_EMULATE;
+ break;
+
+ default:
+ /* The program counter has been changed. */
+ pc = state->Reg[15];
+#ifndef MODE32
+ pc = pc & R15PCBITS;
+#endif
+ state->Reg[15] = pc + (isize * 2);
+ state->Aborted = 0;
+ //chy 2004-05-25, fix bug provided by Carl van Schaik<cvansch@cse.unsw.EDU.AU>
+ state->AbortAddr = 1;
+
+ instr = ARMul_LoadInstrN (state, pc, isize);
+
+ //chy 2006-04-12, for ICE debug
+ have_bp=ARMul_ICE_debug(state,instr,pc);
+#if 0
+ decoded =
+ ARMul_LoadInstrS (state, pc + (isize), isize);
+#endif
+ decoded_addr=pc+isize;
+#if 0
+ loaded = ARMul_LoadInstrS (state, pc + (isize * 2),
+ isize);
+#endif
+ loaded_addr=pc + isize * 2;
+ NORMALCYCLE;
+ if (have_bp) goto TEST_EMULATE;
+ break;
+ }
+#if 0
+ int idx = 0;
+ printf("pc:%x\n", pc);
+ for (; idx < 17; idx ++) {
+ printf("R%d:%x\t", idx, state->Reg[idx]);
+ }
+ printf("\n");
+#endif
+
+ instr = ARMul_LoadInstrN (state, pc, isize);
+ state->last_instr = state->CurrInstr;
+ state->CurrInstr = instr;
+ ARMul_Debug(state, pc, instr);
+#if 0
+ if((state->NumInstrs % 10000000) == 0)
+ printf("---|%p|--- %lld\n", pc, state->NumInstrs);
+ if(state->NumInstrs > (3000000000)) {
+ static int flag = 0;
+ if(pc == 0x8032ccc4) {
+ flag = 300;
+ }
+ if(flag) {
+ int idx = 0;
+ printf("------------------------------------\n");
+ printf("pc:%x\n", pc);
+ for (; idx < 17; idx ++) {
+ printf("R%d:%x\t", idx, state->Reg[idx]);
+ }
+ printf("\nN:%d\t Z:%d\t C:%d\t V:%d\n", state->NFlag, state->ZFlag, state->CFlag, state->VFlag);
+ printf("\n");
+ printf("------------------------------------\n");
+ flag--;
+ }
+ }
+#endif
+#if DIFF_STATE
+ fprintf(state->state_log, "PC:0x%x\n", pc);
+ if (pc && (pc + 8) != state->Reg[15]) {
+ printf("lucky dog\n");
+ printf("pc is %x, R15 is %x\n", pc, state->Reg[15]);
+ //exit(-1);
+ }
+ for (reg_index = 0; reg_index < 16; reg_index ++) {
+ if (state->Reg[reg_index] != mirror_register_file[reg_index]) {
+ fprintf(state->state_log, "R%d:0x%x\n", reg_index, state->Reg[reg_index]);
+ mirror_register_file[reg_index] = state->Reg[reg_index];
+ }
+ }
+ if (state->Cpsr != mirror_register_file[CPSR_REG]) {
+ fprintf(state->state_log, "Cpsr:0x%x\n", state->Cpsr);
+ mirror_register_file[CPSR_REG] = state->Cpsr;
+ }
+ if (state->RegBank[SVCBANK][13] != mirror_register_file[R13_SVC]) {
+ fprintf(state->state_log, "R13_SVC:0x%x\n", state->RegBank[SVCBANK][13]);
+ mirror_register_file[R13_SVC] = state->RegBank[SVCBANK][13];
+ }
+ if (state->RegBank[SVCBANK][14] != mirror_register_file[R14_SVC]) {
+ fprintf(state->state_log, "R14_SVC:0x%x\n", state->RegBank[SVCBANK][14]);
+ mirror_register_file[R14_SVC] = state->RegBank[SVCBANK][14];
+ }
+ if (state->RegBank[ABORTBANK][13] != mirror_register_file[R13_ABORT]) {
+ fprintf(state->state_log, "R13_ABORT:0x%x\n", state->RegBank[ABORTBANK][13]);
+ mirror_register_file[R13_ABORT] = state->RegBank[ABORTBANK][13];
+ }
+ if (state->RegBank[ABORTBANK][14] != mirror_register_file[R14_ABORT]) {
+ fprintf(state->state_log, "R14_ABORT:0x%x\n", state->RegBank[ABORTBANK][14]);
+ mirror_register_file[R14_ABORT] = state->RegBank[ABORTBANK][14];
+ }
+ if (state->RegBank[UNDEFBANK][13] != mirror_register_file[R13_UNDEF]) {
+ fprintf(state->state_log, "R13_UNDEF:0x%x\n", state->RegBank[UNDEFBANK][13]);
+ mirror_register_file[R13_UNDEF] = state->RegBank[UNDEFBANK][13];
+ }
+ if (state->RegBank[UNDEFBANK][14] != mirror_register_file[R14_UNDEF]) {
+ fprintf(state->state_log, "R14_UNDEF:0x%x\n", state->RegBank[UNDEFBANK][14]);
+ mirror_register_file[R14_UNDEF] = state->RegBank[UNDEFBANK][14];
+ }
+ if (state->RegBank[IRQBANK][13] != mirror_register_file[R13_IRQ]) {
+ fprintf(state->state_log, "R13_IRQ:0x%x\n", state->RegBank[IRQBANK][13]);
+ mirror_register_file[R13_IRQ] = state->RegBank[IRQBANK][13];
+ }
+ if (state->RegBank[IRQBANK][14] != mirror_register_file[R14_IRQ]) {
+ fprintf(state->state_log, "R14_IRQ:0x%x\n", state->RegBank[IRQBANK][14]);
+ mirror_register_file[R14_IRQ] = state->RegBank[IRQBANK][14];
+ }
+ if (state->RegBank[FIQBANK][8] != mirror_register_file[R8_FIRQ]) {
+ fprintf(state->state_log, "R8_FIRQ:0x%x\n", state->RegBank[FIQBANK][8]);
+ mirror_register_file[R8_FIRQ] = state->RegBank[FIQBANK][8];
+ }
+ if (state->RegBank[FIQBANK][9] != mirror_register_file[R9_FIRQ]) {
+ fprintf(state->state_log, "R9_FIRQ:0x%x\n", state->RegBank[FIQBANK][9]);
+ mirror_register_file[R9_FIRQ] = state->RegBank[FIQBANK][9];
+ }
+ if (state->RegBank[FIQBANK][10] != mirror_register_file[R10_FIRQ]) {
+ fprintf(state->state_log, "R10_FIRQ:0x%x\n", state->RegBank[FIQBANK][10]);
+ mirror_register_file[R10_FIRQ] = state->RegBank[FIQBANK][10];
+ }
+ if (state->RegBank[FIQBANK][11] != mirror_register_file[R11_FIRQ]) {
+ fprintf(state->state_log, "R11_FIRQ:0x%x\n", state->RegBank[FIQBANK][11]);
+ mirror_register_file[R11_FIRQ] = state->RegBank[FIQBANK][11];
+ }
+ if (state->RegBank[FIQBANK][12] != mirror_register_file[R12_FIRQ]) {
+ fprintf(state->state_log, "R12_FIRQ:0x%x\n", state->RegBank[FIQBANK][12]);
+ mirror_register_file[R12_FIRQ] = state->RegBank[FIQBANK][12];
+ }
+ if (state->RegBank[FIQBANK][13] != mirror_register_file[R13_FIRQ]) {
+ fprintf(state->state_log, "R13_FIRQ:0x%x\n", state->RegBank[FIQBANK][13]);
+ mirror_register_file[R13_FIRQ] = state->RegBank[FIQBANK][13];
+ }
+ if (state->RegBank[FIQBANK][14] != mirror_register_file[R14_FIRQ]) {
+ fprintf(state->state_log, "R14_FIRQ:0x%x\n", state->RegBank[FIQBANK][14]);
+ mirror_register_file[R14_FIRQ] = state->RegBank[FIQBANK][14];
+ }
+ if (state->Spsr[SVCBANK] != mirror_register_file[SPSR_SVC]) {
+ fprintf(state->state_log, "SPSR_SVC:0x%x\n", state->Spsr[SVCBANK]);
+ mirror_register_file[SPSR_SVC] = state->RegBank[SVCBANK];
+ }
+ if (state->Spsr[ABORTBANK] != mirror_register_file[SPSR_ABORT]) {
+ fprintf(state->state_log, "SPSR_ABORT:0x%x\n", state->Spsr[ABORTBANK]);
+ mirror_register_file[SPSR_ABORT] = state->RegBank[ABORTBANK];
+ }
+ if (state->Spsr[UNDEFBANK] != mirror_register_file[SPSR_UNDEF]) {
+ fprintf(state->state_log, "SPSR_UNDEF:0x%x\n", state->Spsr[UNDEFBANK]);
+ mirror_register_file[SPSR_UNDEF] = state->RegBank[UNDEFBANK];
+ }
+ if (state->Spsr[IRQBANK] != mirror_register_file[SPSR_IRQ]) {
+ fprintf(state->state_log, "SPSR_IRQ:0x%x\n", state->Spsr[IRQBANK]);
+ mirror_register_file[SPSR_IRQ] = state->RegBank[IRQBANK];
+ }
+ if (state->Spsr[FIQBANK] != mirror_register_file[SPSR_FIRQ]) {
+ fprintf(state->state_log, "SPSR_FIRQ:0x%x\n", state->Spsr[FIQBANK]);
+ mirror_register_file[SPSR_FIRQ] = state->RegBank[FIQBANK];
+ }
+#endif
+
+#if 0
+ uint32_t alex = 0;
+ static int flagged = 0;
+ if ((flagged == 0) && (pc == 0xb224)) {
+ flagged++;
+ }
+ if ((flagged == 1) && (pc == 0x1a800)) {
+ flagged++;
+ }
+ if (flagged == 3) {
+ printf("---|%p|--- %x\n", pc, state->NumInstrs);
+ for (alex = 0; alex < 15; alex++) {
+ printf("R%02d % 8x\n", alex, state->Reg[alex]);
+ }
+ printf("R%02d % 8x\n", alex, state->Reg[alex] - 8);
+ printf("CPS %x%07x\n", (state->NFlag<<3 | state->ZFlag<<2 | state->CFlag<<1 | state->VFlag), state->Cpsr & 0xfffffff);
+ } else {
+ if (state->NumInstrs < 0x400000) {
+ //exit(-1);
+ }
+ }
+#endif
+
+ /*if (state->EventSet)
+ ARMul_EnvokeEvent (state);*/
+
+#if 0
+ /* do profiling for code coverage */
+ if (skyeye_config.code_cov.prof_on)
+ cov_prof(EXEC_FLAG, pc);
+#endif
+//2003-07-11 chy: for test
+#if 0
+ if (skyeye_config.log.logon >= 1) {
+ if (state->NumInstrs >= skyeye_config.log.start &&
+ state->NumInstrs <= skyeye_config.log.end) {
+ static int mybegin = 0;
+ static int myinstrnum = 0;
+ if (mybegin == 0)
+ mybegin = 1;
+#if 0
+ if (state->NumInstrs == 3695) {
+ printf ("***********SKYEYE: numinstr = 3695\n");
+ }
+ static int mybeg2 = 0;
+ static int mybeg3 = 0;
+ static int mybeg4 = 0;
+ static int mybeg5 = 0;
+
+ if (pc == 0xa0008000) {
+ //mybegin=1;
+ printf ("************SKYEYE: real vmlinux begin now numinstr is %llu ****************\n", state->NumInstrs);
+ }
+
+ //chy 2003-09-02 test fiq
+ if (state->NumInstrs == 67347000) {
+ printf ("***********SKYEYE: numinstr = 67347000, begin log\n");
+ mybegin = 1;
+ }
+ if (pc == 0xc00087b4) { //numinstr=67348714
+ mybegin = 1;
+ printf ("************SKYEYE: test irq now numinstr is %llu ****************\n", state->NumInstrs);
+ }
+ if (pc == 0xc00087b8) { //in start_kernel::sti()
+ mybeg4 = 1;
+ printf ("************SKYEYE: startkerenl: sti now numinstr is %llu ********\n", state->NumInstrs);
+ }
+ /*if (pc==0xc001e4f4||pc==0xc001e4f8||pc==0xc001e4fc||pc==0xc001e500||pc==0xffff0004) { //MRA instr */
+ if (pc == 0xc001e500) { //MRA instr
+ mybeg5 = 1;
+ printf ("************SKYEYE: MRA instr now numinstr is %llu ********\n", state->NumInstrs);
+ }
+ if (pc >= 0xc0000000 && mybeg2 == 0) {
+ mybeg2 = 1;
+ printf ("************SKYEYE: enable mmu&cache, now numinstr is %llu **************\n", state->NumInstrs);
+ SKYEYE_OUTREGS (stderr);
+ printf ("************************************************************************\n");
+ }
+ //chy 2003-09-01 test after tlb-flush
+ if (pc == 0xc00261ac) {
+ //sleep(2);
+ mybeg3 = 1;
+ printf ("************SKYEYE: after tlb-flush numinstr is %llu ****************\n", state->NumInstrs);
+ }
+ if (mybeg3 == 1) {
+ SKYEYE_OUTREGS (skyeye_logfd);
+ SKYEYE_OUTMOREREGS (skyeye_logfd);
+ fprintf (skyeye_logfd, "\n");
+ }
+#endif
+ if (mybegin == 1) {
+ //fprintf(skyeye_logfd,"p %x,i %x,d %x,l %x,",pc,instr,decoded,loaded);
+ //chy for test 20050729
+ /*if (state->NumInstrs>=3302294) {
+ if (pc==0x100c9d4 && instr==0xe1b0f00e){
+ chy_debug();
+ printf("*********************************************\n");
+ printf("******SKYEYE N %llx :p %x,i %x\n SKYEYE******\n",state->NumInstrs,pc,instr);
+ printf("*********************************************\n");
+ }
+ */
+ if (skyeye_config.log.logon >= 1)
+ /*
+ fprintf (skyeye_logfd,
+ "N %llx :p %x,i %x,",
+ state->NumInstrs, pc,
+ #ifdef MODET
+ TFLAG ? instr & 0xffff : instr
+ #else
+ instr
+ #endif
+ );
+ */
+ fprintf(skyeye_logfd, "pc=0x%x,r3=0x%x\n", pc, state->Reg[3]);
+ if (skyeye_config.log.logon >= 2)
+ SKYEYE_OUTREGS (skyeye_logfd);
+ if (skyeye_config.log.logon >= 3)
+ SKYEYE_OUTMOREREGS
+ (skyeye_logfd);
+ //fprintf (skyeye_logfd, "\n");
+ if (skyeye_config.log.length > 0) {
+ myinstrnum++;
+ if (myinstrnum >=
+ skyeye_config.log.
+ length) {
+ myinstrnum = 0;
+ fflush (skyeye_logfd);
+ fseek (skyeye_logfd,
+ 0L, SEEK_SET);
+ }
+ }
+ }
+ //SKYEYE_OUTREGS(skyeye_logfd);
+ //SKYEYE_OUTMOREREGS(skyeye_logfd);
+ }
+ }
+#endif
+#if 0 /* Enable this for a helpful bit of debugging when tracing is needed. */
+ fprintf (stderr, "pc: %x, instr: %x\n", pc & ~1, instr);
+ if (instr == 0)
+ abort ();
+#endif
+#if 0 /* Enable this code to help track down stack alignment bugs. */
+ {
+ static ARMword old_sp = -1;
+
+ if (old_sp != state->Reg[13]) {
+ old_sp = state->Reg[13];
+ fprintf (stderr,
+ "pc: %08x: SP set to %08x%s\n",
+ pc & ~1, old_sp,
+ (old_sp % 8) ? " [UNALIGNED!]" : "");
+ }
+ }
+#endif
+ /* Any exceptions ? */
+ if (state->NresetSig == LOW) {
+ ARMul_Abort (state, ARMul_ResetV);
+
+ /*added energy_prof statement by ksh in 2004-11-26 */
+ //chy 2005-07-28 for standalone
+ //ARMul_do_energy(state,instr,pc);
+ break;
+ } else if (!state->NfiqSig && !FFLAG) {
+ ARMul_Abort (state, ARMul_FIQV);
+ /*added energy_prof statement by ksh in 2004-11-26 */
+ //chy 2005-07-28 for standalone
+ //ARMul_do_energy(state,instr,pc);
+ break;
+ } else if (!state->NirqSig && !IFLAG) {
+ ARMul_Abort (state, ARMul_IRQV);
+ /*added energy_prof statement by ksh in 2004-11-26 */
+ //chy 2005-07-28 for standalone
+ //ARMul_do_energy(state,instr,pc);
+ break;
+ }
+
+//teawater add for arm2x86 2005.04.26-------------------------------------------
+#if 0
+// if (state->pc == 0xc011a868 || state->pc == 0xc011a86c) {
+ if (state->NumInstrs == 1671574 || state->NumInstrs == 1671573 || state->NumInstrs == 1671572
+ || state->NumInstrs == 1671575) {
+ for (reg_index = 0; reg_index < 16; reg_index ++) {
+ printf("R%d:%x\t", reg_index, state->Reg[reg_index]);
+ }
+ printf("\n");
+ }
+#endif
+ if (state->tea_pc) {
+ int i;
+
+ if (state->tea_reg_fd) {
+ fprintf (state->tea_reg_fd, "\n");
+ for (i = 0; i < 15; i++) {
+ fprintf (state->tea_reg_fd, "%x,",
+ state->Reg[i]);
+ }
+ fprintf (state->tea_reg_fd, "%x,", pc);
+ state->Cpsr = ARMul_GetCPSR (state);
+ fprintf (state->tea_reg_fd, "%x\n",
+ state->Cpsr);
+ } else {
+ printf ("\n");
+ for (i = 0; i < 15; i++) {
+ printf ("%x,", state->Reg[i]);
+ }
+ printf ("%x,", pc);
+ state->Cpsr = ARMul_GetCPSR (state);
+ printf ("%x\n", state->Cpsr);
+ }
+ }
+//AJ2D--------------------------------------------------------------------------
+
+ /*if (state->CallDebug > 0) {
+ instr = ARMul_Debug (state, pc, instr);
+ if (state->Emulate < ONCE) {
+ state->NextInstr = RESUME;
+ break;
+ }
+ if (state->Debug) {
+ fprintf (stderr,
+ "sim: At %08lx Instr %08lx Mode %02lx\n",
+ pc, instr, state->Mode);
+ (void) fgetc (stdin);
+ }
+ }
+ else*/
+ if (state->Emulate < ONCE) {
+ state->NextInstr = RESUME;
+ break;
+ }
+ //io_do_cycle (state);
+ state->NumInstrs++;
+#if 0
+ if (state->NumInstrs % 10000000 == 0) {
+ printf("10 MIPS instr have been executed\n");
+ }
+#endif
+
+#ifdef MODET
+ /* Provide Thumb instruction decoding. If the processor is in Thumb
+ mode, then we can simply decode the Thumb instruction, and map it
+ to the corresponding ARM instruction (by directly loading the
+ instr variable, and letting the normal ARM simulator
+ execute). There are some caveats to ensure that the correct
+ pipelined PC value is used when executing Thumb code, and also for
+ dealing with the BL instruction. */
+ if (TFLAG) {
+ ARMword armOp = 0;
+ /* Check if in Thumb mode. */
+ switch (ARMul_ThumbDecode(state, pc, instr, &armOp)) {
+ case t_undefined:
+ /* This is a Thumb instruction. */
+ ARMul_UndefInstr (state, instr);
+ goto donext;
+
+ case t_branch:
+ /* Already processed. */
+ //pc = state->Reg[15] - 2;
+ //state->pc = state->Reg[15] - 2; //ichfly why do I need that
+ goto donext;
+
+ case t_decoded:
+ /* ARM instruction available. */
+ //printf("t decode %04lx -> %08lx\n", instr & 0xffff, armOp);
+
+ if (armOp == 0xDEADC0DE) {
+ XDSERROR("Failed to decode thumb opcode %04X at %08X", instr, pc);
+ }
+
+ instr = armOp;
+
+ /* So continue instruction decoding. */
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+ /* Check the condition codes. */
+ if ((temp = TOPBITS (28)) == AL) {
+ /* Vile deed in the need for speed. */
+ goto mainswitch;
+ }
+
+ /* Check the condition code. */
+ switch ((int) TOPBITS (28)) {
+ case AL:
+ temp = TRUE;
+ break;
+ case NV:
+
+ /* shenoubang add for armv7 instr dmb 2012-3-11 */
+ if (state->is_v7) {
+ if ((instr & 0x0fffff00) == 0x057ff000) {
+ switch((instr >> 4) & 0xf) {
+ case 4: /* dsb */
+ case 5: /* dmb */
+ case 6: /* isb */
+ // TODO: do no implemented thes instr
+ goto donext;
+ }
+ }
+ }
+ /* dyf add for armv6 instruct CPS 2010.9.17 */
+ if (state->is_v6) {
+ /* clrex do nothing here temporary */
+ if (instr == 0xf57ff01f) {
+ //printf("clrex \n");
+#if 0
+ int i;
+ for(i = 0; i < 128; i++) {
+ state->exclusive_tag_array[i] = 0xffffffff;
+ }
+#endif
+ /* shenoubang 2012-3-14 refer the dyncom_interpreter */
+ state->exclusive_tag_array[0] = 0xFFFFFFFF;
+ state->exclusive_access_state = 0;
+ goto donext;
+ }
+
+ if (BITS(20, 27) == 0x10) {
+ if (BIT(19)) {
+ if (BIT(8)) {
+ if (BIT(18))
+ state->Cpsr |= 1<<8;
+ else
+ state->Cpsr &= ~(1<<8);
+ }
+ if (BIT(7)) {
+ if (BIT(18))
+ state->Cpsr |= 1<<7;
+ else
+ state->Cpsr &= ~(1<<7);
+ ASSIGNINT (state->Cpsr & INTBITS);
+ }
+ if (BIT(6)) {
+ if (BIT(18))
+ state->Cpsr |= 1<<6;
+ else
+ state->Cpsr &= ~(1<<6);
+ ASSIGNINT (state->Cpsr & INTBITS);
+ }
+ }
+ if (BIT(17)) {
+ state->Cpsr |= BITS(0, 4);
+ printf("skyeye test state->Mode\n");
+ if (state->Mode != (state->Cpsr & MODEBITS)) {
+ state->Mode = ARMul_SwitchMode (state, state->Mode, state->Cpsr & MODEBITS);
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ }
+ }
+ goto donext;
+ }
+ }
+ if (state->is_v5) {
+ if (BITS (25, 27) == 5) { /* BLX(1) */
+ ARMword dest;
+
+ state->Reg[14] = pc + 4;
+
+ /* Force entry into Thumb mode. */
+ dest = pc + 8 + 1;
+ if (BIT (23))
+ dest += (NEGBRANCH +
+ (BIT (24) << 1));
+ else
+ dest += POSBRANCH +
+ (BIT (24) << 1);
+
+ WriteR15Branch (state, dest);
+ goto donext;
+ } else if ((instr & 0xFC70F000) == 0xF450F000) {
+ /* The PLD instruction. Ignored. */
+ goto donext;
+ } else if (((instr & 0xfe500f00) == 0xfc100100)
+ || ((instr & 0xfe500f00) ==
+ 0xfc000100)) {
+ /* wldrw and wstrw are unconditional. */
+ goto mainswitch;
+ } else {
+ /* UNDEFINED in v5, UNPREDICTABLE in v3, v4, non executed in v1, v2. */
+ ARMul_UndefInstr (state, instr);
+ }
+ }
+ temp = FALSE;
+ break;
+ case EQ:
+ temp = ZFLAG;
+ break;
+ case NE:
+ temp = !ZFLAG;
+ break;
+ case VS:
+ temp = VFLAG;
+ break;
+ case VC:
+ temp = !VFLAG;
+ break;
+ case MI:
+ temp = NFLAG;
+ break;
+ case PL:
+ temp = !NFLAG;
+ break;
+ case CS:
+ temp = CFLAG;
+ break;
+ case CC:
+ temp = !CFLAG;
+ break;
+ case HI:
+ temp = (CFLAG && !ZFLAG);
+ break;
+ case LS:
+ temp = (!CFLAG || ZFLAG);
+ break;
+ case GE:
+ temp = ((!NFLAG && !VFLAG) || (NFLAG && VFLAG));
+ break;
+ case LT:
+ temp = ((NFLAG && !VFLAG) || (!NFLAG && VFLAG));
+ break;
+ case GT:
+ temp = ((!NFLAG && !VFLAG && !ZFLAG)
+ || (NFLAG && VFLAG && !ZFLAG));
+ break;
+ case LE:
+ temp = ((NFLAG && !VFLAG) || (!NFLAG && VFLAG))
+ || ZFLAG;
+ break;
+ } /* cc check */
+
+//chy 2003-08-24 now #if 0 .... #endif process cp14, cp15.reg14, I disable it...
+
+ /* Actual execution of instructions begins here. */
+ /* If the condition codes don't match, stop here. */
+ if (temp) {
+mainswitch:
+
+ /* shenoubang sbfx and ubfx instr 2012-3-16 */
+ if (state->is_v6) {
+ unsigned int m, lsb, width, Rd, Rn, data;
+ Rd = Rn = lsb = width = data = m = 0;
+
+ //printf("helloworld\n");
+ if ((((int) BITS (21, 27)) == 0x3f) && (((int) BITS (4, 6)) == 0x5)) {
+ m = (unsigned)BITS(7, 11);
+ width = (unsigned)BITS(16, 20);
+ Rd = (unsigned)BITS(12, 15);
+ Rn = (unsigned)BITS(0, 3);
+ if ((Rd == 15) || (Rn == 15)) {
+ ARMul_UndefInstr (state, instr);
+ } else if ((m + width) < 32) {
+ data = state->Reg[Rn];
+ state->Reg[Rd] ^= state->Reg[Rd];
+ state->Reg[Rd] = ((ARMword)(data << (31 -(m + width))) >> ((31 - (m + width)) + (m)));
+ //SKYEYE_LOG_IN_CLR(RED, "UBFX: In %s, line = %d, Reg_src[%d] = 0x%x, Reg_d[%d] = 0x%x, m = %d, width = %d, Rd = %d, Rn = %d\n",
+ // __FUNCTION__, __LINE__, Rn, data, Rd, state->Reg[Rd], m, width + 1, Rd, Rn);
+ goto donext;
+ }
+ } // ubfx instr
+ else if ((((int) BITS (21, 27)) == 0x3d) && (((int) BITS (4, 6)) == 0x5)) {
+ int tmp = 0;
+ Rd = BITS(12, 15);
+ Rn = BITS(0, 3);
+ lsb = BITS(7, 11);
+ width = BITS(16, 20);
+ if ((Rd == 15) || (Rn == 15)) {
+ ARMul_UndefInstr (state, instr);
+ } else if ((lsb + width) < 32) {
+ state->Reg[Rd] ^= state->Reg[Rd];
+ data = state->Reg[Rn];
+ tmp = (data << (32 - (lsb + width + 1)));
+ state->Reg[Rd] = (tmp >> (32 - (lsb + width + 1)));
+ //SKYEYE_LOG_IN_CLR(RED, "sbfx: In %s, line = %d, pc = 0x%x, instr = 0x%x,Rd = 0x%x, Rn = 0x%x, lsb = %d, width = %d, Rs[%d] = 0x%x, Rd[%d] = 0x%x\n",
+ // __func__, __LINE__, pc, instr, Rd, Rn, lsb, width + 1, Rn, state->Reg[Rn], Rd, state->Reg[Rd]);
+ goto donext;
+ }
+ } // sbfx instr
+ else if ((((int)BITS(21, 27)) == 0x3e) && ((int)BITS(4, 6) == 0x1)) {
+ //(ARMword)(instr<<(31-(n))) >> ((31-(n))+(m))
+ unsigned msb ,tmp_rn, tmp_rd, dst;
+ tmp_rd = tmp_rn = dst = 0;
+ Rd = BITS(12, 15);
+ Rn = BITS(0, 3);
+ lsb = BITS(7, 11);
+ msb = BITS(16, 20); //-V519
+ if ((Rd == 15)) {
+ ARMul_UndefInstr (state, instr);
+ } else if ((Rn == 15)) {
+ data = state->Reg[Rd];
+ tmp_rd = ((ARMword)(data << (31 - lsb)) >> (31 - lsb));
+ dst = ((data >> msb) << (msb - lsb));
+ dst = (dst << lsb) | tmp_rd;
+ goto donext;
+ } // bfc instr
+ else if (((msb >= lsb) && (msb < 32))) {
+ data = state->Reg[Rn];
+ tmp_rn = ((ARMword)(data << (31 - (msb - lsb))) >> (31 - (msb - lsb)));
+ data = state->Reg[Rd];
+ tmp_rd = ((ARMword)(data << (31 - lsb)) >> (31 - lsb));
+ dst = ((data >> msb) << (msb - lsb)) | tmp_rn;
+ dst = (dst << lsb) | tmp_rd;
+ goto donext;
+ } // bfi instr
+ }
+ }
+
+ switch ((int) BITS (20, 27)) {
+ /* Data Processing Register RHS Instructions. */
+
+ case 0x00: /* AND reg and MUL */
+#ifdef MODET
+ if (BITS (4, 11) == 0xB) {
+ /* STRH register offset, no write-back, down, post indexed. */
+ SHDOWNWB ();
+ break;
+ }
+ if (BITS (4, 7) == 0xD) {
+ Handle_Load_Double (state, instr);
+ break;
+ }
+ if (BITS (4, 7) == 0xF) {
+ Handle_Store_Double (state, instr);
+ break;
+ }
+#endif
+ if (BITS (4, 7) == 9) {
+ /* MUL */
+ rhs = state->Reg[MULRHSReg];
+ //if (MULLHSReg == MULDESTReg) {
+ if(0) { /* For armv6, the restriction is removed */
+ UNDEF_MULDestEQOp1;
+ state->Reg[MULDESTReg] = 0;
+ } else if (MULDESTReg != 15)
+ state->Reg[MULDESTReg] = state->Reg[MULLHSReg] * rhs;
+ else
+ UNDEF_MULPCDest;
+
+ for (dest = 0, temp = 0; dest < 32;
+ dest++)
+ if (rhs & (1L << dest))
+ temp = dest;
+
+ /* Mult takes this many/2 I cycles. */
+ ARMul_Icycles (state, ARMul_MultTable[temp], 0L);
+ } else {
+ /* AND reg. */
+ rhs = DPRegRHS;
+ dest = LHS & rhs;
+ WRITEDEST (dest);
+ }
+ break;
+
+ case 0x01: /* ANDS reg and MULS */
+#ifdef MODET
+ if ((BITS (4, 11) & 0xF9) == 0x9)
+ /* LDR register offset, no write-back, down, post indexed. */
+ LHPOSTDOWN ();
+ /* Fall through to rest of decoding. */
+#endif
+ if (BITS (4, 7) == 9) {
+ /* MULS */
+ rhs = state->Reg[MULRHSReg];
+
+ //if (MULLHSReg == MULDESTReg) {
+ if(0) {
+ printf("Something in %d line\n", __LINE__);
+ UNDEF_WARNING;
+ UNDEF_MULDestEQOp1;
+ state->Reg[MULDESTReg] = 0;
+ CLEARN;
+ SETZ;
+ } else if (MULDESTReg != 15) {
+ dest = state->Reg[MULLHSReg] * rhs;
+ ARMul_NegZero (state, dest);
+ state->Reg[MULDESTReg] = dest;
+ } else
+ UNDEF_MULPCDest;
+
+ for (dest = 0, temp = 0; dest < 32;
+ dest++)
+ if (rhs & (1L << dest))
+ temp = dest;
+
+ /* Mult takes this many/2 I cycles. */
+ ARMul_Icycles (state, ARMul_MultTable[temp], 0L);
+ } else {
+ /* ANDS reg. */
+ rhs = DPSRegRHS;
+ dest = LHS & rhs;
+ WRITESDEST (dest);
+ }
+ break;
+
+ case 0x02: /* EOR reg and MLA */
+#ifdef MODET
+ if (BITS (4, 11) == 0xB) {
+ /* STRH register offset, write-back, down, post indexed. */
+ SHDOWNWB ();
+ break;
+ }
+#endif
+ if (BITS (4, 7) == 9) { /* MLA */
+ rhs = state->Reg[MULRHSReg];
+#if 0
+ if (MULLHSReg == MULDESTReg) {
+ UNDEF_MULDestEQOp1;
+ state->Reg[MULDESTReg] = state->Reg[MULACCReg];
+ } else if (MULDESTReg != 15) {
+#endif
+ if (MULDESTReg != 15) {
+ state->Reg[MULDESTReg] = state->Reg[MULLHSReg] * rhs + state->Reg[MULACCReg];
+ } else
+ UNDEF_MULPCDest;
+
+ for (dest = 0, temp = 0; dest < 32;
+ dest++)
+ if (rhs & (1L << dest))
+ temp = dest;
+
+ /* Mult takes this many/2 I cycles. */
+ ARMul_Icycles (state, ARMul_MultTable[temp], 0L);
+ } else {
+ rhs = DPRegRHS;
+ dest = LHS ^ rhs;
+ WRITEDEST (dest);
+ }
+ break;
+
+ case 0x03: /* EORS reg and MLAS */
+#ifdef MODET
+ if ((BITS (4, 11) & 0xF9) == 0x9)
+ /* LDR register offset, write-back, down, post-indexed. */
+ LHPOSTDOWN ();
+ /* Fall through to rest of the decoding. */
+#endif
+ if (BITS (4, 7) == 9) {
+ /* MLAS */
+ rhs = state->Reg[MULRHSReg];
+ //if (MULLHSReg == MULDESTReg) {
+ if (0) {
+ UNDEF_MULDestEQOp1;
+ dest = state->Reg[MULACCReg];
+ ARMul_NegZero (state, dest);
+ state->Reg[MULDESTReg] = dest;
+ } else if (MULDESTReg != 15) {
+ dest = state->Reg[MULLHSReg] * rhs + state->Reg[MULACCReg];
+ ARMul_NegZero (state, dest);
+ state->Reg[MULDESTReg] = dest;
+ } else
+ UNDEF_MULPCDest;
+
+ for (dest = 0, temp = 0; dest < 32;
+ dest++)
+ if (rhs & (1L << dest))
+ temp = dest;
+
+ /* Mult takes this many/2 I cycles. */
+ ARMul_Icycles (state, ARMul_MultTable[temp], 0L);
+ } else {
+ /* EORS Reg. */
+ rhs = DPSRegRHS;
+ dest = LHS ^ rhs;
+ WRITESDEST (dest);
+ }
+ break;
+
+ case 0x04: /* SUB reg */
+ // Signifies UMAAL
+ if (state->is_v6 && BITS(4, 7) == 0x09) {
+ if (handle_v6_insn(state, instr))
+ break;
+ }
+
+#ifdef MODET
+ if (BITS (4, 7) == 0xB) {
+ /* STRH immediate offset, no write-back, down, post indexed. */
+ SHDOWNWB ();
+ break;
+ }
+ if (BITS (4, 7) == 0xD) {
+ Handle_Load_Double (state, instr);
+ break;
+ }
+ if (BITS (4, 7) == 0xF) {
+ Handle_Store_Double (state, instr);
+ break;
+ }
+#endif
+ rhs = DPRegRHS;
+ dest = LHS - rhs;
+ WRITEDEST (dest);
+ break;
+
+ case 0x05: /* SUBS reg */
+#ifdef MODET
+ if ((BITS (4, 7) & 0x9) == 0x9)
+ /* LDR immediate offset, no write-back, down, post indexed. */
+ LHPOSTDOWN ();
+ /* Fall through to the rest of the instruction decoding. */
+#endif
+ lhs = LHS;
+ rhs = DPRegRHS;
+ dest = lhs - rhs;
+
+ if ((lhs >= rhs) || ((rhs | lhs) >> 31)) {
+ ARMul_SubCarry (state, lhs, rhs, dest);
+ ARMul_SubOverflow (state, lhs, rhs, dest);
+ } else {
+ CLEARC;
+ CLEARV;
+ }
+ WRITESDEST (dest);
+ break;
+
+ case 0x06: /* RSB reg */
+#ifdef MODET
+ if (BITS (4, 7) == 0xB) {
+ /* STRH immediate offset, write-back, down, post indexed. */
+ SHDOWNWB ();
+ break;
+ }
+#endif
+ rhs = DPRegRHS;
+ dest = rhs - LHS;
+ WRITEDEST (dest);
+ break;
+
+ case 0x07: /* RSBS reg */
+#ifdef MODET
+ if ((BITS (4, 7) & 0x9) == 0x9)
+ /* LDR immediate offset, write-back, down, post indexed. */
+ LHPOSTDOWN ();
+ /* Fall through to remainder of instruction decoding. */
+#endif
+ lhs = LHS;
+ rhs = DPRegRHS;
+ dest = rhs - lhs;
+
+ if ((rhs >= lhs) || ((rhs | lhs) >> 31)) {
+ ARMul_SubCarry (state, rhs, lhs, dest);
+ ARMul_SubOverflow (state, rhs, lhs, dest);
+ } else {
+ CLEARC;
+ CLEARV;
+ }
+ WRITESDEST (dest);
+ break;
+
+ case 0x08: /* ADD reg */
+#ifdef MODET
+ if (BITS (4, 11) == 0xB) {
+ /* STRH register offset, no write-back, up, post indexed. */
+ SHUPWB ();
+ break;
+ }
+ if (BITS (4, 7) == 0xD) {
+ Handle_Load_Double (state, instr);
+ break;
+ }
+ if (BITS (4, 7) == 0xF) {
+ Handle_Store_Double (state, instr);
+ break;
+ }
+#endif
+#ifdef MODET
+ if (BITS (4, 7) == 0x9) {
+ /* MULL */
+ /* 32x32 = 64 */
+ ARMul_Icycles (state, Multiply64 (state, instr, LUNSIGNED, LDEFAULT), 0L);
+ break;
+ }
+#endif
+ rhs = DPRegRHS;
+ dest = LHS + rhs;
+ WRITEDEST (dest);
+ break;
+
+ case 0x09: /* ADDS reg */
+#ifdef MODET
+ if ((BITS (4, 11) & 0xF9) == 0x9)
+ /* LDR register offset, no write-back, up, post indexed. */
+ LHPOSTUP ();
+ /* Fall through to remaining instruction decoding. */
+#endif
+#ifdef MODET
+ if (BITS (4, 7) == 0x9) {
+ /* MULL */
+ /* 32x32=64 */
+ ARMul_Icycles (state, Multiply64 (state, instr, LUNSIGNED, LSCC), 0L);
+ break;
+ }
+#endif
+ lhs = LHS;
+ rhs = DPRegRHS;
+ dest = lhs + rhs;
+ ASSIGNZ (dest == 0);
+ if ((lhs | rhs) >> 30) {
+ /* Possible C,V,N to set. */
+ ASSIGNN (NEG (dest));
+ ARMul_AddCarry (state, lhs, rhs, dest);
+ ARMul_AddOverflow (state, lhs, rhs, dest);
+ } else {
+ CLEARN;
+ CLEARC;
+ CLEARV;
+ }
+ WRITESDEST (dest);
+ break;
+
+ case 0x0a: /* ADC reg */
+#ifdef MODET
+ if (BITS (4, 11) == 0xB) {
+ /* STRH register offset, write-back, up, post-indexed. */
+ SHUPWB ();
+ break;
+ }
+ if (BITS (4, 7) == 0x9) {
+ /* MULL */
+ /* 32x32=64 */
+ ARMul_Icycles (state, MultiplyAdd64 (state, instr, LUNSIGNED, LDEFAULT), 0L);
+ break;
+ }
+#endif
+ rhs = DPRegRHS;
+ dest = LHS + rhs + CFLAG;
+ WRITEDEST (dest);
+ break;
+
+ case 0x0b: /* ADCS reg */
+#ifdef MODET
+ if ((BITS (4, 11) & 0xF9) == 0x9)
+ /* LDR register offset, write-back, up, post indexed. */
+ LHPOSTUP ();
+ /* Fall through to remaining instruction decoding. */
+ if (BITS (4, 7) == 0x9) {
+ /* MULL */
+ /* 32x32=64 */
+ ARMul_Icycles (state, MultiplyAdd64 (state, instr, LUNSIGNED, LSCC), 0L);
+ break;
+ }
+#endif
+ lhs = LHS;
+ rhs = DPRegRHS;
+ dest = lhs + rhs + CFLAG;
+ ASSIGNZ (dest == 0);
+ if ((lhs | rhs) >> 30) {
+ /* Possible C,V,N to set. */
+ ASSIGNN (NEG (dest));
+ ARMul_AddCarry (state, lhs, rhs, dest);
+ ARMul_AddOverflow (state, lhs, rhs, dest);
+ } else {
+ CLEARN;
+ CLEARC;
+ CLEARV;
+ }
+ WRITESDEST (dest);
+ break;
+
+ case 0x0c: /* SBC reg */
+#ifdef MODET
+ if (BITS (4, 7) == 0xB) {
+ /* STRH immediate offset, no write-back, up post indexed. */
+ SHUPWB ();
+ break;
+ }
+ if (BITS (4, 7) == 0xD) {
+ Handle_Load_Double (state, instr);
+ break;
+ }
+ if (BITS (4, 7) == 0xF) {
+ Handle_Store_Double (state, instr);
+ break;
+ }
+ if (BITS (4, 7) == 0x9) {
+ /* MULL */
+ /* 32x32=64 */
+ ARMul_Icycles (state, Multiply64 (state, instr, LSIGNED, LDEFAULT), 0L);
+ break;
+ }
+#endif
+ rhs = DPRegRHS;
+ dest = LHS - rhs - !CFLAG;
+ WRITEDEST (dest);
+ break;
+
+ case 0x0d: /* SBCS reg */
+#ifdef MODET
+ if ((BITS (4, 7) & 0x9) == 0x9)
+ /* LDR immediate offset, no write-back, up, post indexed. */
+ LHPOSTUP ();
+
+ if (BITS (4, 7) == 0x9) {
+ /* MULL */
+ /* 32x32=64 */
+ ARMul_Icycles (state, Multiply64 (state, instr, LSIGNED, LSCC), 0L);
+ break;
+ }
+#endif
+ lhs = LHS;
+ rhs = DPRegRHS;
+ dest = lhs - rhs - !CFLAG;
+ if ((lhs >= rhs) || ((rhs | lhs) >> 31)) {
+ ARMul_SubCarry (state, lhs, rhs, dest);
+ ARMul_SubOverflow (state, lhs, rhs, dest);
+ } else {
+ CLEARC;
+ CLEARV;
+ }
+ WRITESDEST (dest);
+ break;
+
+ case 0x0e: /* RSC reg */
+#ifdef MODET
+ if (BITS (4, 7) == 0xB) {
+ /* STRH immediate offset, write-back, up, post indexed. */
+ SHUPWB ();
+ break;
+ }
+
+ if (BITS (4, 7) == 0x9) {
+ /* MULL */
+ /* 32x32=64 */
+ ARMul_Icycles (state, MultiplyAdd64 (state, instr, LSIGNED, LDEFAULT), 0L);
+ break;
+ }
+#endif
+ rhs = DPRegRHS;
+ dest = rhs - LHS - !CFLAG;
+ WRITEDEST (dest);
+ break;
+
+ case 0x0f: /* RSCS reg */
+#ifdef MODET
+ if ((BITS (4, 7) & 0x9) == 0x9)
+ /* LDR immediate offset, write-back, up, post indexed. */
+ LHPOSTUP ();
+ /* Fall through to remaining instruction decoding. */
+
+ if (BITS (4, 7) == 0x9) {
+ /* MULL */
+ /* 32x32=64 */
+ ARMul_Icycles (state, MultiplyAdd64 (state, instr, LSIGNED, LSCC), 0L);
+ break;
+ }
+#endif
+ lhs = LHS;
+ rhs = DPRegRHS;
+ dest = rhs - lhs - !CFLAG;
+
+ if ((rhs >= lhs) || ((rhs | lhs) >> 31)) {
+ ARMul_SubCarry (state, rhs, lhs, dest);
+ ARMul_SubOverflow (state, rhs, lhs, dest);
+ } else {
+ CLEARC;
+ CLEARV;
+ }
+ WRITESDEST (dest);
+ break;
+
+ case 0x10: /* TST reg and MRS CPSR and SWP word. */
+ if (state->is_v5e) {
+ if (BIT (4) == 0 && BIT (7) == 1) {
+ /* ElSegundo SMLAxy insn. */
+ ARMword op1 = state->Reg[BITS (0, 3)];
+ ARMword op2 = state->Reg[BITS (8, 11)];
+ ARMword Rn = state->Reg[BITS (12, 15)];
+
+ if (BIT (5))
+ op1 >>= 16;
+ if (BIT (6))
+ op2 >>= 16;
+ op1 &= 0xFFFF;
+ op2 &= 0xFFFF;
+ if (op1 & 0x8000)
+ op1 -= 65536;
+ if (op2 & 0x8000)
+ op2 -= 65536;
+ op1 *= op2;
+ //printf("SMLA_INST:BB,op1=0x%x, op2=0x%x. Rn=0x%x\n", op1, op2, Rn);
+ if (AddOverflow(op1, Rn, op1 + Rn))
+ SETQ;
+ state->Reg[BITS (16, 19)] = op1 + Rn;
+ break;
+ }
+
+ if (BITS (4, 11) == 5) {
+ /* ElSegundo QADD insn. */
+ ARMword op1 = state->Reg[BITS (0, 3)];
+ ARMword op2 = state->Reg[BITS (16, 19)];
+ ARMword result = op1 + op2;
+ if (AddOverflow(op1, op2, result)) {
+ result = POS (result) ? 0x80000000 : 0x7fffffff;
+ SETQ;
+ }
+ state->Reg[BITS (12, 15)] = result;
+ break;
+ }
+ }
+#ifdef MODET
+ if (BITS (4, 11) == 0xB) {
+ /* STRH register offset, no write-back, down, pre indexed. */
+ SHPREDOWN ();
+ break;
+ }
+ if (BITS (4, 7) == 0xD) {
+ Handle_Load_Double (state, instr);
+ break;
+ }
+ if (BITS (4, 7) == 0xF) {
+ Handle_Store_Double (state, instr);
+ break;
+ }
+#endif
+ if (BITS (4, 11) == 9) {
+ /* SWP */
+ UNDEF_SWPPC;
+ temp = LHS;
+ BUSUSEDINCPCS;
+#ifndef MODE32
+ if (VECTORACCESS (temp) || ADDREXCEPT (temp)) {
+ INTERNALABORT (temp);
+ (void) ARMul_LoadWordN (state, temp);
+ (void) ARMul_LoadWordN (state, temp);
+ } else
+#endif
+ dest = ARMul_SwapWord (state, temp, state->Reg[RHSReg]);
+ if (temp & 3)
+ DEST = ARMul_Align (state, temp, dest);
+ else
+ DEST = dest;
+ if (state->abortSig || state->Aborted)
+ TAKEABORT;
+ } else if ((BITS (0, 11) == 0) && (LHSReg == 15)) { /* MRS CPSR */
+ UNDEF_MRSPC;
+ DEST = ARMul_GetCPSR(state);
+ } else {
+ UNDEF_Test;
+ }
+ break;
+
+ case 0x11: /* TSTP reg */
+#ifdef MODET
+ if ((BITS (4, 11) & 0xF9) == 0x9)
+ /* LDR register offset, no write-back, down, pre indexed. */
+ LHPREDOWN ();
+ /* Continue with remaining instruction decode. */
+#endif
+ if (DESTReg == 15) {
+ /* TSTP reg */
+#ifdef MODE32
+ //chy 2006-02-15 if in user mode, can not set cpsr 0:23
+ //from p165 of ARMARM book
+ state->Cpsr = GETSPSR (state->Bank);
+ ARMul_CPSRAltered (state);
+#else
+ rhs = DPRegRHS;
+ temp = LHS & rhs;
+ SETR15PSR (temp);
+#endif
+ } else {
+ /* TST reg */
+ rhs = DPSRegRHS;
+ dest = LHS & rhs;
+ ARMul_NegZero (state, dest);
+ }
+ break;
+
+ case 0x12: /* TEQ reg and MSR reg to CPSR (ARM6). */
+
+ if (state->is_v5) {
+ if (BITS (4, 7) == 3) {
+ /* BLX(2) */
+ ARMword temp;
+
+ if (TFLAG)
+ temp = (pc + 2) | 1;
+ else
+ temp = pc + 4;
+
+ WriteR15Branch (state, state->Reg[RHSReg]);
+ state->Reg[14] = temp;
+ break;
+ }
+ }
+
+ if (state->is_v5e) {
+ if (BIT (4) == 0 && BIT (7) == 1 && (BIT (5) == 0 || BITS (12, 15) == 0)) {
+ /* ElSegundo SMLAWy/SMULWy insn. */
+ unsigned long long op1 = state->Reg[BITS (0, 3)];
+ unsigned long long op2 = state->Reg[BITS (8, 11)];
+ unsigned long long result;
+
+ if (BIT (6))
+ op2 >>= 16;
+ if (op1 & 0x80000000)
+ op1 -= 1ULL << 32;
+ op2 &= 0xFFFF;
+ if (op2 & 0x8000)
+ op2 -= 65536;
+ result = (op1 * op2) >> 16;
+
+ if (BIT (5) == 0) {
+ ARMword Rn = state->Reg[BITS(12, 15)];
+
+ if (AddOverflow((ARMword)result, Rn, (ARMword)(result + Rn)))
+ SETQ;
+ result += Rn;
+ }
+ state->Reg[BITS (16, 19)] = (ARMword)result;
+ break;
+ }
+
+ if (BITS (4, 11) == 5) {
+ /* ElSegundo QSUB insn. */
+ ARMword op1 = state->Reg[BITS (0, 3)];
+ ARMword op2 = state->Reg[BITS (16, 19)];
+ ARMword result = op1 - op2;
+
+ if (SubOverflow
+ (op1, op2, result)) {
+ result = POS (result) ? 0x80000000 : 0x7fffffff;
+ SETQ;
+ }
+
+ state->Reg[BITS (12, 15)] = result;
+ break;
+ }
+ }
+#ifdef MODET
+ if (BITS (4, 11) == 0xB) {
+ /* STRH register offset, write-back, down, pre indexed. */
+ SHPREDOWNWB ();
+ break;
+ }
+ if (BITS (4, 27) == 0x12FFF1) {
+ /* BX */
+ WriteR15Branch (state, state->Reg[RHSReg]);
+ break;
+ }
+ if (BITS (4, 7) == 0xD) {
+ Handle_Load_Double (state, instr);
+ break;
+ }
+ if (BITS (4, 7) == 0xF) {
+ Handle_Store_Double (state, instr);
+ break;
+ }
+#endif
+ if (state->is_v5) {
+ if (BITS (4, 7) == 0x7) {
+ //ARMword value;
+ //extern int SWI_vector_installed;
+
+ /* Hardware is allowed to optionally override this
+ instruction and treat it as a breakpoint. Since
+ this is a simulator not hardware, we take the position
+ that if a SWI vector was not installed, then an Abort
+ vector was probably not installed either, and so
+ normally this instruction would be ignored, even if an
+ Abort is generated. This is a bad thing, since GDB
+ uses this instruction for its breakpoints (at least in
+ Thumb mode it does). So intercept the instruction here
+ and generate a breakpoint SWI instead. */
+ /* Force the next instruction to be refetched. */
+ state->NextInstr = RESUME;
+ break;
+ }
+ }
+ if (DESTReg == 15) {
+ /* MSR reg to CPSR. */
+ UNDEF_MSRPC;
+ temp = DPRegRHS;
+#ifdef MODET
+ /* Don't allow TBIT to be set by MSR. */
+ temp &= ~TBIT;
+#endif
+ ARMul_FixCPSR (state, instr, temp);
+ } else
+ UNDEF_Test;
+
+ break;
+
+ case 0x13: /* TEQP reg */
+#ifdef MODET
+ if ((BITS (4, 11) & 0xF9) == 0x9)
+ /* LDR register offset, write-back, down, pre indexed. */
+ LHPREDOWNWB ();
+ /* Continue with remaining instruction decode. */
+#endif
+ if (DESTReg == 15) {
+ /* TEQP reg */
+#ifdef MODE32
+ state->Cpsr = GETSPSR (state->Bank);
+ ARMul_CPSRAltered (state);
+#else
+ rhs = DPRegRHS;
+ temp = LHS ^ rhs;
+ SETR15PSR (temp);
+#endif
+ } else {
+ /* TEQ Reg. */
+ rhs = DPSRegRHS;
+ dest = LHS ^ rhs;
+ ARMul_NegZero (state, dest);
+ }
+ break;
+
+ case 0x14: /* CMP reg and MRS SPSR and SWP byte. */
+ if (state->is_v5e) {
+ if (BIT (4) == 0 && BIT (7) == 1) {
+ /* ElSegundo SMLALxy insn. */
+ unsigned long long op1 = state->Reg[BITS (0, 3)];
+ unsigned long long op2 = state->Reg[BITS (8, 11)];
+ unsigned long long dest;
+ //unsigned long long result;
+
+ if (BIT (5))
+ op1 >>= 16;
+ if (BIT (6))
+ op2 >>= 16;
+ op1 &= 0xFFFF;
+ if (op1 & 0x8000)
+ op1 -= 65536;
+ op2 &= 0xFFFF;
+ if (op2 & 0x8000)
+ op2 -= 65536;
+
+ dest = (unsigned long long) state->Reg[BITS (16, 19)] << 32;
+ dest |= state->Reg[BITS (12, 15)];
+ dest += op1 * op2;
+ state->Reg[BITS(12, 15)] = (ARMword)dest;
+ state->Reg[BITS(16, 19)] = (ARMword)(dest >> 32);
+ break;
+ }
+
+ if (BITS (4, 11) == 5) {
+ /* ElSegundo QDADD insn. */
+ ARMword op1 = state->Reg[BITS (0, 3)];
+ ARMword op2 = state->Reg[BITS (16, 19)];
+ ARMword op2d = op2 + op2;
+ ARMword result;
+
+ if (AddOverflow
+ (op2, op2, op2d)) {
+ SETQ;
+ op2d = POS (op2d) ? 0x80000000 : 0x7fffffff;
+ }
+
+ result = op1 + op2d;
+ if (AddOverflow(op1, op2d, result)) {
+ SETQ;
+ result = POS (result) ? 0x80000000 : 0x7fffffff;
+ }
+
+ state->Reg[BITS (12, 15)] = result;
+ break;
+ }
+ }
+#ifdef MODET
+ if (BITS (4, 7) == 0xB) {
+ /* STRH immediate offset, no write-back, down, pre indexed. */
+ SHPREDOWN ();
+ break;
+ }
+ if (BITS (4, 7) == 0xD) {
+ Handle_Load_Double (state, instr);
+ break;
+ }
+ if (BITS (4, 7) == 0xF) {
+ Handle_Store_Double (state, instr);
+ break;
+ }
+#endif
+ if (BITS (4, 11) == 9) {
+ /* SWP */
+ UNDEF_SWPPC;
+ temp = LHS;
+ BUSUSEDINCPCS;
+#ifndef MODE32
+ if (VECTORACCESS (temp) || ADDREXCEPT (temp)) {
+ INTERNALABORT (temp);
+ (void) ARMul_LoadByte (state, temp);
+ (void) ARMul_LoadByte (state, temp);
+ } else
+#endif
+ DEST = ARMul_SwapByte (state, temp, state->Reg[RHSReg]);
+ if (state->abortSig || state->Aborted)
+ TAKEABORT;
+ } else if ((BITS (0, 11) == 0)
+ && (LHSReg == 15)) {
+ /* MRS SPSR */
+ UNDEF_MRSPC;
+ DEST = GETSPSR (state->Bank);
+ } else
+ UNDEF_Test;
+
+ break;
+
+ case 0x15: /* CMPP reg. */
+#ifdef MODET
+ if ((BITS (4, 7) & 0x9) == 0x9)
+ /* LDR immediate offset, no write-back, down, pre indexed. */
+ LHPREDOWN ();
+ /* Continue with remaining instruction decode. */
+#endif
+ if (DESTReg == 15) {
+ /* CMPP reg. */
+#ifdef MODE32
+ state->Cpsr = GETSPSR (state->Bank);
+ ARMul_CPSRAltered (state);
+#else
+ rhs = DPRegRHS;
+ temp = LHS - rhs;
+ SETR15PSR (temp);
+#endif
+ } else {
+ /* CMP reg. */
+ lhs = LHS;
+ rhs = DPRegRHS;
+ dest = lhs - rhs;
+ ARMul_NegZero (state, dest);
+ if ((lhs >= rhs)
+ || ((rhs | lhs) >> 31)) {
+ ARMul_SubCarry (state, lhs, rhs, dest);
+ ARMul_SubOverflow (state, lhs, rhs, dest);
+ } else {
+ CLEARC;
+ CLEARV;
+ }
+ }
+ break;
+
+ case 0x16: /* CMN reg and MSR reg to SPSR */
+ if (state->is_v5e) {
+ if (BIT (4) == 0 && BIT (7) == 1 && BITS (12, 15) == 0) {
+ /* ElSegundo SMULxy insn. */
+ ARMword op1 = state->Reg[BITS (0, 3)];
+ ARMword op2 = state->Reg[BITS (8, 11)];
+ ARMword Rn = state->Reg[BITS (12, 15)];
+
+ if (BIT (5))
+ op1 >>= 16;
+ if (BIT (6))
+ op2 >>= 16;
+ op1 &= 0xFFFF;
+ op2 &= 0xFFFF;
+ if (op1 & 0x8000)
+ op1 -= 65536;
+ if (op2 & 0x8000)
+ op2 -= 65536;
+
+ state->Reg[BITS (16, 19)] = op1 * op2;
+ break;
+ }
+
+ if (BITS (4, 11) == 5) {
+ /* ElSegundo QDSUB insn. */
+ ARMword op1 = state->Reg[BITS (0, 3)];
+ ARMword op2 = state->Reg[BITS (16, 19)];
+ ARMword op2d = op2 + op2;
+ ARMword result;
+
+ if (AddOverflow(op2, op2, op2d)) {
+ SETQ;
+ op2d = POS (op2d) ? 0x80000000 : 0x7fffffff;
+ }
+
+ result = op1 - op2d;
+ if (SubOverflow(op1, op2d, result)) {
+ SETQ;
+ result = POS (result) ? 0x80000000 : 0x7fffffff;
+ }
+
+ state->Reg[BITS (12, 15)] = result;
+ break;
+ }
+ }
+
+ if (state->is_v5) {
+ if (BITS (4, 11) == 0xF1
+ && BITS (16, 19) == 0xF) {
+ /* ARM5 CLZ insn. */
+ ARMword op1 = state->Reg[BITS (0, 3)];
+ int result = 32;
+
+ if (op1)
+ for (result = 0; (op1 & 0x80000000) == 0; op1 <<= 1)
+ result++;
+ state->Reg[BITS (12, 15)] = result;
+ break;
+ }
+ }
+
+#ifdef MODET
+ if (BITS (4, 7) == 0xB) {
+ /* STRH immediate offset, write-back, down, pre indexed. */
+ SHPREDOWNWB ();
+ break;
+ }
+ if (BITS (4, 7) == 0xD) {
+ Handle_Load_Double (state, instr);
+ break;
+ }
+ if (BITS (4, 7) == 0xF) {
+ Handle_Store_Double (state, instr);
+ break;
+ }
+#endif
+ if (DESTReg == 15) {
+ /* MSR */
+ UNDEF_MSRPC;
+ /*ARMul_FixSPSR (state, instr,
+ DPRegRHS);*/
+ } else {
+ UNDEF_Test;
+ }
+ break;
+
+ case 0x17: /* CMNP reg */
+#ifdef MODET
+ if ((BITS (4, 7) & 0x9) == 0x9)
+ /* LDR immediate offset, write-back, down, pre indexed. */
+ LHPREDOWNWB ();
+ /* Continue with remaining instruction decoding. */
+#endif
+ if (DESTReg == 15) {
+#ifdef MODE32
+ state->Cpsr = GETSPSR (state->Bank);
+ ARMul_CPSRAltered (state);
+#else
+ rhs = DPRegRHS;
+ temp = LHS + rhs;
+ SETR15PSR (temp);
+#endif
+ break;
+ } else {
+ /* CMN reg. */
+ lhs = LHS;
+ rhs = DPRegRHS;
+ dest = lhs + rhs;
+ ASSIGNZ (dest == 0);
+ if ((lhs | rhs) >> 30) {
+ /* Possible C,V,N to set. */
+ ASSIGNN (NEG (dest));
+ ARMul_AddCarry (state, lhs, rhs, dest);
+ ARMul_AddOverflow (state, lhs, rhs, dest);
+ } else {
+ CLEARN;
+ CLEARC;
+ CLEARV;
+ }
+ }
+ break;
+
+ case 0x18: /* ORR reg */
+#ifdef MODET
+ /* dyf add armv6 instr strex 2010.9.17 */
+ if (state->is_v6) {
+ if (BITS (4, 7) == 0x9)
+ if (handle_v6_insn (state, instr))
+ break;
+ }
+
+ if (BITS (4, 11) == 0xB) {
+ /* STRH register offset, no write-back, up, pre indexed. */
+ SHPREUP ();
+ break;
+ }
+ if (BITS (4, 7) == 0xD) {
+ Handle_Load_Double (state, instr);
+ break;
+ }
+ if (BITS (4, 7) == 0xF) {
+ Handle_Store_Double (state, instr);
+ break;
+ }
+#endif
+ rhs = DPRegRHS;
+ dest = LHS | rhs;
+ WRITEDEST (dest);
+ break;
+
+ case 0x19: /* ORRS reg */
+#ifdef MODET
+ /* dyf add armv6 instr ldrex */
+ if (state->is_v6) {
+ if (BITS (4, 7) == 0x9) {
+ if (handle_v6_insn (state, instr))
+ break;
+ }
+ }
+ if ((BITS (4, 11) & 0xF9) == 0x9)
+ /* LDR register offset, no write-back, up, pre indexed. */
+ LHPREUP ();
+ /* Continue with remaining instruction decoding. */
+#endif
+ rhs = DPSRegRHS;
+ dest = LHS | rhs;
+ WRITESDEST (dest);
+ break;
+
+ case 0x1a: /* MOV reg */
+#ifdef MODET
+ if (BITS (4, 11) == 0xB) {
+ /* STRH register offset, write-back, up, pre indexed. */
+ SHPREUPWB ();
+ break;
+ }
+ if (BITS (4, 7) == 0xD) {
+ Handle_Load_Double (state, instr);
+ break;
+ }
+ if (BITS (4, 7) == 0xF) {
+ Handle_Store_Double (state, instr);
+ break;
+ }
+ if (BITS(4, 11) == 0xF9) { //strexd
+ u32 l = LHSReg;
+
+ bool enter = false;
+
+ if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr)&&
+ state->currentexvald == (u32)ARMul_ReadWord(state, state->currentexaddr + 4))
+ enter = true;
+
+ //todo bug this and STREXD and LDREXD http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0360e/CHDGJGGC.html
+
+ if (enter) {
+ ARMul_StoreWordN(state, LHS, state->Reg[RHSReg]);
+ ARMul_StoreWordN(state,LHS + 4 , state->Reg[RHSReg + 1]);
+ state->Reg[DESTReg] = 0;
+ } else {
+ state->Reg[DESTReg] = 1;
+ }
+
+ break;
+ }
+#endif
+ dest = DPRegRHS;
+ WRITEDEST (dest);
+ break;
+
+ case 0x1B: /* MOVS reg */
+#ifdef MODET
+ /* ldrexd ichfly */
+ if (BITS(0, 11) == 0xF9F) { //strexd
+ lhs = LHS;
+
+ state->currentexaddr = lhs;
+ state->currentexval = (u32)ARMul_ReadWord(state, lhs);
+ state->currentexvald = (u32)ARMul_ReadWord(state, lhs + 4);
+
+ state->Reg[DESTReg] = ARMul_LoadWordN(state, lhs);
+ state->Reg[DESTReg] = ARMul_LoadWordN(state, lhs + 4);
+ break;
+ }
+
+ if ((BITS (4, 11) & 0xF9) == 0x9)
+ /* LDR register offset, write-back, up, pre indexed. */
+ LHPREUPWB ();
+ /* Continue with remaining instruction decoding. */
+
+#endif
+ dest = DPSRegRHS;
+ WRITESDEST (dest);
+ break;
+
+ case 0x1c: /* BIC reg */
+#ifdef MODET
+ /* dyf add for STREXB */
+ if (state->is_v6) {
+ if (BITS (4, 7) == 0x9) {
+ if (handle_v6_insn (state, instr))
+ break;
+ }
+ }
+ if (BITS (4, 7) == 0xB) {
+ /* STRH immediate offset, no write-back, up, pre indexed. */
+ SHPREUP ();
+ break;
+ }
+ if (BITS (4, 7) == 0xD) {
+ Handle_Load_Double (state, instr);
+ break;
+ } else if (BITS (4, 7) == 0xF) {
+ Handle_Store_Double (state, instr);
+ break;
+ }
+#endif
+ rhs = DPRegRHS;
+ dest = LHS & ~rhs;
+ WRITEDEST (dest);
+ break;
+
+ case 0x1d: /* BICS reg */
+#ifdef MODET
+ /* ladsh P=1 U=1 W=0 L=1 S=1 H=1 */
+ if (BITS(4, 7) == 0xF) {
+ temp = LHS + GetLS7RHS (state, instr);
+ LoadHalfWord (state, instr, temp, LSIGNED);
+ break;
+ }
+ if (BITS (4, 7) == 0xb) {
+ /* LDRH immediate offset, no write-back, up, pre indexed. */
+ temp = LHS + GetLS7RHS (state, instr);
+ LoadHalfWord (state, instr, temp, LUNSIGNED);
+ break;
+ }
+ if (BITS (4, 7) == 0xd) {
+ // alex-ykl fix: 2011-07-20 missing ldrsb instruction
+ temp = LHS + GetLS7RHS (state, instr);
+ LoadByte (state, instr, temp, LSIGNED);
+ break;
+ }
+
+ /* Continue with instruction decoding. */
+ /*if ((BITS (4, 7) & 0x9) == 0x9) */
+ if ((BITS (4, 7)) == 0x9) {
+ /* ldrexb */
+ if (state->is_v6) {
+ if (handle_v6_insn (state, instr))
+ break;
+ }
+ /* LDR immediate offset, no write-back, up, pre indexed. */
+ LHPREUP ();
+ }
+
+#endif
+ rhs = DPSRegRHS;
+ dest = LHS & ~rhs;
+ WRITESDEST (dest);
+ break;
+
+ case 0x1e: /* MVN reg */
+#ifdef MODET
+ if ((instr & 0x00000FF0) == 0x00000F90) { //if ((instr & 0x0FF00FF0) == 0x01e00f90) { //todo make that better ichfly
+ /* strexh ichfly */
+ u32 l = LHSReg;
+ u32 r = RHSReg;
+ lhs = LHS;
+
+ bool enter = false;
+
+ if (state->currentexval == (u32)ARMul_LoadHalfWord(state, state->currentexaddr))enter = true;
+
+ //StoreWord(state, lhs, RHS)
+ if (state->Aborted) {
+ TAKEABORT;
+ }
+ if (enter) {
+ ARMul_StoreHalfWord(state, lhs, RHS);
+ state->Reg[DESTReg] = 0;
+ } else {
+ state->Reg[DESTReg] = 1;
+ }
+ break;
+ }
+ if (BITS (4, 7) == 0xB) {
+ /* STRH immediate offset, write-back, up, pre indexed. */
+ SHPREUPWB ();
+ break;
+ }
+ if (BITS (4, 7) == 0xD) {
+ Handle_Load_Double (state, instr);
+ break;
+ }
+ if (BITS (4, 7) == 0xF) {
+ Handle_Store_Double (state, instr);
+ break;
+ }
+#endif
+ dest = ~DPRegRHS;
+ WRITEDEST (dest);
+ break;
+
+ case 0x1f: /* MVNS reg */
+#ifdef MODET
+
+ if ((instr & 0x00000FF0) == 0x00000F90) { //(instr & 0x0FF00FF0) == 0x01f00f90)//if ((instr & 0x0FF00FF0) == 0x01f00f90) {
+ /* ldrexh ichfly */
+ lhs = LHS;
+
+ state->currentexaddr = lhs;
+ state->currentexval = (u32)ARMul_LoadHalfWord(state, lhs);
+
+ LoadHalfWord(state, instr, lhs,0);
+ break;
+ }
+
+ if ((BITS (4, 7) & 0x9) == 0x9)
+ /* LDR immediate offset, write-back, up, pre indexed. */
+ LHPREUPWB ();
+ /* Continue instruction decoding. */
+#endif
+ dest = ~DPSRegRHS;
+ WRITESDEST (dest);
+ break;
+
+ /* Data Processing Immediate RHS Instructions. */
+
+ case 0x20: /* AND immed */
+ dest = LHS & DPImmRHS;
+ WRITEDEST (dest);
+ break;
+
+ case 0x21: /* ANDS immed */
+ DPSImmRHS;
+ dest = LHS & rhs;
+ WRITESDEST (dest);
+ break;
+
+ case 0x22: /* EOR immed */
+ dest = LHS ^ DPImmRHS;
+ WRITEDEST (dest);
+ break;
+
+ case 0x23: /* EORS immed */
+ DPSImmRHS;
+ dest = LHS ^ rhs;
+ WRITESDEST (dest);
+ break;
+
+ case 0x24: /* SUB immed */
+ dest = LHS - DPImmRHS;
+ WRITEDEST (dest);
+ break;
+
+ case 0x25: /* SUBS immed */
+ lhs = LHS;
+ rhs = DPImmRHS;
+ dest = lhs - rhs;
+
+ if ((lhs >= rhs) || ((rhs | lhs) >> 31)) {
+ ARMul_SubCarry (state, lhs, rhs, dest);
+ ARMul_SubOverflow (state, lhs, rhs, dest);
+ } else {
+ CLEARC;
+ CLEARV;
+ }
+ WRITESDEST (dest);
+ break;
+
+ case 0x26: /* RSB immed */
+ dest = DPImmRHS - LHS;
+ WRITEDEST (dest);
+ break;
+
+ case 0x27: /* RSBS immed */
+ lhs = LHS;
+ rhs = DPImmRHS;
+ dest = rhs - lhs;
+
+ if ((rhs >= lhs) || ((rhs | lhs) >> 31)) {
+ ARMul_SubCarry (state, rhs, lhs, dest);
+ ARMul_SubOverflow (state, rhs, lhs, dest);
+ } else {
+ CLEARC;
+ CLEARV;
+ }
+ WRITESDEST (dest);
+ break;
+
+ case 0x28: /* ADD immed */
+ dest = LHS + DPImmRHS;
+ WRITEDEST (dest);
+ break;
+
+ case 0x29: /* ADDS immed */
+ lhs = LHS;
+ rhs = DPImmRHS;
+ dest = lhs + rhs;
+ ASSIGNZ (dest == 0);
+
+ if ((lhs | rhs) >> 30) {
+ /* Possible C,V,N to set. */
+ ASSIGNN (NEG (dest));
+ ARMul_AddCarry (state, lhs, rhs, dest);
+ ARMul_AddOverflow (state, lhs, rhs, dest);
+ } else {
+ CLEARN;
+ CLEARC;
+ CLEARV;
+ }
+ WRITESDEST (dest);
+ break;
+
+ case 0x2a: /* ADC immed */
+ dest = LHS + DPImmRHS + CFLAG;
+ WRITEDEST (dest);
+ break;
+
+ case 0x2b: /* ADCS immed */
+ lhs = LHS;
+ rhs = DPImmRHS;
+ dest = lhs + rhs + CFLAG;
+ ASSIGNZ (dest == 0);
+ if ((lhs | rhs) >> 30) {
+ /* Possible C,V,N to set. */
+ ASSIGNN (NEG (dest));
+ ARMul_AddCarry (state, lhs, rhs, dest);
+ ARMul_AddOverflow (state, lhs, rhs, dest);
+ } else {
+ CLEARN;
+ CLEARC;
+ CLEARV;
+ }
+ WRITESDEST (dest);
+ break;
+
+ case 0x2c: /* SBC immed */
+ dest = LHS - DPImmRHS - !CFLAG;
+ WRITEDEST (dest);
+ break;
+
+ case 0x2d: /* SBCS immed */
+ lhs = LHS;
+ rhs = DPImmRHS;
+ dest = lhs - rhs - !CFLAG;
+ if ((lhs >= rhs) || ((rhs | lhs) >> 31)) {
+ ARMul_SubCarry (state, lhs, rhs, dest);
+ ARMul_SubOverflow (state, lhs, rhs, dest);
+ } else {
+ CLEARC;
+ CLEARV;
+ }
+ WRITESDEST (dest);
+ break;
+
+ case 0x2e: /* RSC immed */
+ dest = DPImmRHS - LHS - !CFLAG;
+ WRITEDEST (dest);
+ break;
+
+ case 0x2f: /* RSCS immed */
+ lhs = LHS;
+ rhs = DPImmRHS;
+ dest = rhs - lhs - !CFLAG;
+ if ((rhs >= lhs) || ((rhs | lhs) >> 31)) {
+ ARMul_SubCarry (state, rhs, lhs, dest);
+ ARMul_SubOverflow (state, rhs, lhs, dest);
+ } else {
+ CLEARC;
+ CLEARV;
+ }
+ WRITESDEST (dest);
+ break;
+
+ case 0x30: /* TST immed */
+ /* shenoubang 2012-3-14*/
+ if (state->is_v6) { /* movw, ARMV6, ARMv7 */
+ dest ^= dest;
+ dest = BITS(16, 19);
+ dest = ((dest<<12) | BITS(0, 11));
+ WRITEDEST(dest);
+ break;
+ } else {
+ UNDEF_Test;
+ break;
+ }
+
+ case 0x31: /* TSTP immed */
+ if (DESTReg == 15) {
+ /* TSTP immed. */
+#ifdef MODE32
+ state->Cpsr = GETSPSR (state->Bank);
+ ARMul_CPSRAltered (state);
+#else
+ temp = LHS & DPImmRHS;
+ SETR15PSR (temp);
+#endif
+ } else {
+ /* TST immed. */
+ DPSImmRHS;
+ dest = LHS & rhs;
+ ARMul_NegZero (state, dest);
+ }
+ break;
+
+ case 0x32: /* TEQ immed and MSR immed to CPSR */
+ if (DESTReg == 15)
+ /* MSR immed to CPSR. */
+ ARMul_FixCPSR (state, instr,
+ DPImmRHS);
+ else
+ UNDEF_Test;
+ break;
+
+ case 0x33: /* TEQP immed */
+ if (DESTReg == 15) {
+ /* TEQP immed. */
+#ifdef MODE32
+ state->Cpsr = GETSPSR (state->Bank);
+ ARMul_CPSRAltered (state);
+#else
+ temp = LHS ^ DPImmRHS;
+ SETR15PSR (temp);
+#endif
+ } else {
+ DPSImmRHS; /* TEQ immed */
+ dest = LHS ^ rhs;
+ ARMul_NegZero (state, dest);
+ }
+ break;
+
+ case 0x34: /* CMP immed */
+ UNDEF_Test;
+ break;
+
+ case 0x35: /* CMPP immed */
+ if (DESTReg == 15) {
+ /* CMPP immed. */
+#ifdef MODE32
+ state->Cpsr = GETSPSR (state->Bank);
+ ARMul_CPSRAltered (state);
+#else
+ temp = LHS - DPImmRHS;
+ SETR15PSR (temp);
+#endif
+ break;
+ } else {
+ /* CMP immed. */
+ lhs = LHS;
+ rhs = DPImmRHS;
+ dest = lhs - rhs;
+ ARMul_NegZero (state, dest);
+
+ if ((lhs >= rhs) || ((rhs | lhs) >> 31)) {
+ ARMul_SubCarry (state, lhs, rhs, dest);
+ ARMul_SubOverflow (state, lhs, rhs, dest);
+ } else {
+ CLEARC;
+ CLEARV;
+ }
+ }
+ break;
+
+ case 0x36: /* CMN immed and MSR immed to SPSR */
+ //if (DESTReg == 15)
+ /*ARMul0_FixSPSR (state, instr,
+ DPImmRHS);*/
+ //else
+ UNDEF_Test;
+ break;
+
+ case 0x37: /* CMNP immed. */
+ if (DESTReg == 15) {
+ /* CMNP immed. */
+#ifdef MODE32
+ state->Cpsr = GETSPSR (state->Bank);
+ ARMul_CPSRAltered (state);
+#else
+ temp = LHS + DPImmRHS;
+ SETR15PSR (temp);
+#endif
+ break;
+ } else {
+ /* CMN immed. */
+ lhs = LHS;
+ rhs = DPImmRHS;
+ dest = lhs + rhs;
+ ASSIGNZ (dest == 0);
+ if ((lhs | rhs) >> 30) {
+ /* Possible C,V,N to set. */
+ ASSIGNN (NEG (dest));
+ ARMul_AddCarry (state, lhs, rhs, dest);
+ ARMul_AddOverflow (state, lhs, rhs, dest);
+ } else {
+ CLEARN;
+ CLEARC;
+ CLEARV;
+ }
+ }
+ break;
+
+ case 0x38: /* ORR immed. */
+ dest = LHS | DPImmRHS;
+ WRITEDEST (dest);
+ break;
+
+ case 0x39: /* ORRS immed. */
+ DPSImmRHS;
+ dest = LHS | rhs;
+ WRITESDEST (dest);
+ break;
+
+ case 0x3a: /* MOV immed. */
+ dest = DPImmRHS;
+ WRITEDEST (dest);
+ break;
+
+ case 0x3b: /* MOVS immed. */
+ DPSImmRHS;
+ WRITESDEST (rhs);
+ break;
+
+ case 0x3c: /* BIC immed. */
+ dest = LHS & ~DPImmRHS;
+ WRITEDEST (dest);
+ break;
+
+ case 0x3d: /* BICS immed. */
+ DPSImmRHS;
+ dest = LHS & ~rhs;
+ WRITESDEST (dest);
+ break;
+
+ case 0x3e: /* MVN immed. */
+ dest = ~DPImmRHS;
+ WRITEDEST (dest);
+ break;
+
+ case 0x3f: /* MVNS immed. */
+ DPSImmRHS;
+ WRITESDEST (~rhs);
+ break;
+
+ /* Single Data Transfer Immediate RHS Instructions. */
+
+ case 0x40: /* Store Word, No WriteBack, Post Dec, Immed. */
+ lhs = LHS;
+ if (StoreWord (state, instr, lhs))
+ LSBase = lhs - LSImmRHS;
+ break;
+
+ case 0x41: /* Load Word, No WriteBack, Post Dec, Immed. */
+ lhs = LHS;
+ if (LoadWord (state, instr, lhs))
+ LSBase = lhs - LSImmRHS;
+ break;
+
+ case 0x42: /* Store Word, WriteBack, Post Dec, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ lhs = LHS;
+ temp = lhs - LSImmRHS;
+ state->NtransSig = LOW;
+ if (StoreWord (state, instr, lhs))
+ LSBase = temp;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x43: /* Load Word, WriteBack, Post Dec, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ lhs = LHS;
+ state->NtransSig = LOW;
+ if (LoadWord (state, instr, lhs))
+ LSBase = lhs - LSImmRHS;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x44: /* Store Byte, No WriteBack, Post Dec, Immed. */
+ lhs = LHS;
+ if (StoreByte (state, instr, lhs))
+ LSBase = lhs - LSImmRHS;
+ break;
+
+ case 0x45: /* Load Byte, No WriteBack, Post Dec, Immed. */
+ lhs = LHS;
+ if (LoadByte (state, instr, lhs, LUNSIGNED))
+ LSBase = lhs - LSImmRHS;
+ break;
+
+ case 0x46: /* Store Byte, WriteBack, Post Dec, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ lhs = LHS;
+ state->NtransSig = LOW;
+ if (StoreByte (state, instr, lhs))
+ LSBase = lhs - LSImmRHS;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x47: /* Load Byte, WriteBack, Post Dec, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ lhs = LHS;
+ state->NtransSig = LOW;
+ if (LoadByte (state, instr, lhs, LUNSIGNED))
+ LSBase = lhs - LSImmRHS;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x48: /* Store Word, No WriteBack, Post Inc, Immed. */
+ lhs = LHS;
+ if (StoreWord (state, instr, lhs))
+ LSBase = lhs + LSImmRHS;
+ break;
+
+ case 0x49: /* Load Word, No WriteBack, Post Inc, Immed. */
+ lhs = LHS;
+ if (LoadWord (state, instr, lhs))
+ LSBase = lhs + LSImmRHS;
+ break;
+
+ case 0x4a: /* Store Word, WriteBack, Post Inc, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ lhs = LHS;
+ state->NtransSig = LOW;
+ if (StoreWord (state, instr, lhs))
+ LSBase = lhs + LSImmRHS;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x4b: /* Load Word, WriteBack, Post Inc, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ lhs = LHS;
+ state->NtransSig = LOW;
+ if (LoadWord (state, instr, lhs))
+ LSBase = lhs + LSImmRHS;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x4c: /* Store Byte, No WriteBack, Post Inc, Immed. */
+ lhs = LHS;
+ if (StoreByte (state, instr, lhs))
+ LSBase = lhs + LSImmRHS;
+ break;
+
+ case 0x4d: /* Load Byte, No WriteBack, Post Inc, Immed. */
+ lhs = LHS;
+ if (LoadByte (state, instr, lhs, LUNSIGNED))
+ LSBase = lhs + LSImmRHS;
+ break;
+
+ case 0x4e: /* Store Byte, WriteBack, Post Inc, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ lhs = LHS;
+ state->NtransSig = LOW;
+ if (StoreByte (state, instr, lhs))
+ LSBase = lhs + LSImmRHS;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x4f: /* Load Byte, WriteBack, Post Inc, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ lhs = LHS;
+ state->NtransSig = LOW;
+ if (LoadByte (state, instr, lhs, LUNSIGNED))
+ LSBase = lhs + LSImmRHS;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x50: /* Store Word, No WriteBack, Pre Dec, Immed. */
+ (void) StoreWord (state, instr, LHS - LSImmRHS);
+ break;
+
+ case 0x51: /* Load Word, No WriteBack, Pre Dec, Immed. */
+ (void) LoadWord (state, instr, LHS - LSImmRHS);
+ break;
+
+ case 0x52: /* Store Word, WriteBack, Pre Dec, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ temp = LHS - LSImmRHS;
+ if (StoreWord (state, instr, temp))
+ LSBase = temp;
+ break;
+
+ case 0x53: /* Load Word, WriteBack, Pre Dec, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ temp = LHS - LSImmRHS;
+ if (LoadWord (state, instr, temp))
+ LSBase = temp;
+ break;
+
+ case 0x54: /* Store Byte, No WriteBack, Pre Dec, Immed. */
+ (void) StoreByte (state, instr, LHS - LSImmRHS);
+ break;
+
+ case 0x55: /* Load Byte, No WriteBack, Pre Dec, Immed. */
+ (void) LoadByte (state, instr, LHS - LSImmRHS, LUNSIGNED);
+ break;
+
+ case 0x56: /* Store Byte, WriteBack, Pre Dec, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ temp = LHS - LSImmRHS;
+ if (StoreByte (state, instr, temp))
+ LSBase = temp;
+ break;
+
+ case 0x57: /* Load Byte, WriteBack, Pre Dec, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ temp = LHS - LSImmRHS;
+ if (LoadByte (state, instr, temp, LUNSIGNED))
+ LSBase = temp;
+ break;
+
+ case 0x58: /* Store Word, No WriteBack, Pre Inc, Immed. */
+ (void) StoreWord (state, instr, LHS + LSImmRHS);
+ break;
+
+ case 0x59: /* Load Word, No WriteBack, Pre Inc, Immed. */
+ (void) LoadWord (state, instr, LHS + LSImmRHS);
+ break;
+
+ case 0x5a: /* Store Word, WriteBack, Pre Inc, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ temp = LHS + LSImmRHS;
+ if (StoreWord (state, instr, temp))
+ LSBase = temp;
+ break;
+
+ case 0x5b: /* Load Word, WriteBack, Pre Inc, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ temp = LHS + LSImmRHS;
+ if (LoadWord (state, instr, temp))
+ LSBase = temp;
+ break;
+
+ case 0x5c: /* Store Byte, No WriteBack, Pre Inc, Immed. */
+ (void) StoreByte (state, instr, LHS + LSImmRHS);
+ break;
+
+ case 0x5d: /* Load Byte, No WriteBack, Pre Inc, Immed. */
+ (void) LoadByte (state, instr, LHS + LSImmRHS, LUNSIGNED);
+ break;
+
+ case 0x5e: /* Store Byte, WriteBack, Pre Inc, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ temp = LHS + LSImmRHS;
+ if (StoreByte (state, instr, temp))
+ LSBase = temp;
+ break;
+
+ case 0x5f: /* Load Byte, WriteBack, Pre Inc, Immed. */
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ temp = LHS + LSImmRHS;
+ if (LoadByte (state, instr, temp, LUNSIGNED))
+ LSBase = temp;
+ break;
+
+ /* Single Data Transfer Register RHS Instructions. */
+
+ case 0x60: /* Store Word, No WriteBack, Post Dec, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ if (StoreWord (state, instr, lhs))
+ LSBase = lhs - LSRegRHS;
+ break;
+
+ case 0x61: /* Load Word, No WriteBack, Post Dec, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6 && handle_v6_insn (state, instr))
+ break;
+#endif
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ temp = lhs - LSRegRHS;
+ if (LoadWord (state, instr, lhs))
+ LSBase = temp;
+ break;
+
+ case 0x62: /* Store Word, WriteBack, Post Dec, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6 && handle_v6_insn (state, instr))
+ break;
+#endif
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ state->NtransSig = LOW;
+ if (StoreWord (state, instr, lhs))
+ LSBase = lhs - LSRegRHS;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x63: /* Load Word, WriteBack, Post Dec, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6 && handle_v6_insn (state, instr))
+ break;
+#endif
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ temp = lhs - LSRegRHS;
+ state->NtransSig = LOW;
+ if (LoadWord (state, instr, lhs))
+ LSBase = temp;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x64: /* Store Byte, No WriteBack, Post Dec, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ if (StoreByte (state, instr, lhs))
+ LSBase = lhs - LSRegRHS;
+ break;
+
+ case 0x65: /* Load Byte, No WriteBack, Post Dec, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6 && handle_v6_insn (state, instr))
+ break;
+#endif
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ temp = lhs - LSRegRHS;
+ if (LoadByte (state, instr, lhs, LUNSIGNED))
+ LSBase = temp;
+ break;
+
+ case 0x66: /* Store Byte, WriteBack, Post Dec, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6 && handle_v6_insn (state, instr))
+ break;
+#endif
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ state->NtransSig = LOW;
+ if (StoreByte (state, instr, lhs))
+ LSBase = lhs - LSRegRHS;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x67: /* Load Byte, WriteBack, Post Dec, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6
+ && handle_v6_insn (state, instr))
+ break;
+#endif
+
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ temp = lhs - LSRegRHS;
+ state->NtransSig = LOW;
+ if (LoadByte (state, instr, lhs, LUNSIGNED))
+ LSBase = temp;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x68: /* Store Word, No WriteBack, Post Inc, Reg. */
+ if ((instr & 0x70) == 0x10) { //pkhbt
+ u8 idest = BITS(12, 15);
+ u8 rfis = BITS(16, 19);
+ u8 rlast = BITS(0, 3);
+ u8 ishi = BITS(7,11);
+ state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000);
+ break;
+ } else if ((instr & 0x70) == 0x50) { //pkhtb
+ u8 rd_idx = BITS(12, 15);
+ u8 rn_idx = BITS(16, 19);
+ u8 rm_idx = BITS(0, 3);
+ u8 imm5 = BITS(7, 11) ? BITS(7, 11) : 31;
+ state->Reg[rd_idx] = ((static_cast<s32>(state->Reg[rm_idx]) >> imm5) & 0xFFFF) | ((state->Reg[rn_idx]) & 0xFFFF0000);
+ break;
+ } else if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6
+ && handle_v6_insn (state, instr))
+ break;
+#endif
+
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ if (StoreWord (state, instr, lhs))
+ LSBase = lhs + LSRegRHS;
+ break;
+
+ case 0x69: /* Load Word, No WriteBack, Post Inc, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ temp = lhs + LSRegRHS;
+ if (LoadWord (state, instr, lhs))
+ LSBase = temp;
+ break;
+
+ case 0x6a: /* Store Word, WriteBack, Post Inc, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6
+ && handle_v6_insn (state, instr))
+ break;
+#endif
+
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ state->NtransSig = LOW;
+ if (StoreWord (state, instr, lhs))
+ LSBase = lhs + LSRegRHS;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x6b: /* Load Word, WriteBack, Post Inc, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6
+ && handle_v6_insn (state, instr))
+ break;
+#endif
+
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ temp = lhs + LSRegRHS;
+ state->NtransSig = LOW;
+ if (LoadWord (state, instr, lhs))
+ LSBase = temp;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x6c: /* Store Byte, No WriteBack, Post Inc, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6
+ && handle_v6_insn (state, instr))
+ break;
+#endif
+
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ if (StoreByte (state, instr, lhs))
+ LSBase = lhs + LSRegRHS;
+ break;
+
+ case 0x6d: /* Load Byte, No WriteBack, Post Inc, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ temp = lhs + LSRegRHS;
+ if (LoadByte (state, instr, lhs, LUNSIGNED))
+ LSBase = temp;
+ break;
+
+ case 0x6e: /* Store Byte, WriteBack, Post Inc, Reg. */
+#if 0
+ if (state->is_v6) {
+ int Rm = 0;
+ /* utxb */
+ if (BITS(15, 19) == 0xf && BITS(4, 7) == 0x7) {
+ Rm = (RHS >> (8 * BITS(10, 11))) & 0xff;
+ DEST = Rm;
+ }
+ }
+#endif
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6
+ && handle_v6_insn (state, instr))
+ break;
+#endif
+
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ state->NtransSig = LOW;
+ if (StoreByte (state, instr, lhs))
+ LSBase = lhs + LSRegRHS;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x6f: /* Load Byte, WriteBack, Post Inc, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6
+ && handle_v6_insn (state, instr))
+ break;
+#endif
+
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ lhs = LHS;
+ temp = lhs + LSRegRHS;
+ state->NtransSig = LOW;
+ if (LoadByte (state, instr, lhs, LUNSIGNED))
+ LSBase = temp;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ break;
+
+ case 0x70: /* Store Word, No WriteBack, Pre Dec, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6 && handle_v6_insn (state, instr))
+ break;
+#endif
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ (void) StoreWord (state, instr, LHS - LSRegRHS);
+ break;
+
+ case 0x71: /* Load Word, No WriteBack, Pre Dec, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ (void) LoadWord (state, instr, LHS - LSRegRHS);
+ break;
+
+ case 0x72: /* Store Word, WriteBack, Pre Dec, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ temp = LHS - LSRegRHS;
+ if (StoreWord (state, instr, temp))
+ LSBase = temp;
+ break;
+
+ case 0x73: /* Load Word, WriteBack, Pre Dec, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ temp = LHS - LSRegRHS;
+ if (LoadWord (state, instr, temp))
+ LSBase = temp;
+ break;
+
+ case 0x74: /* Store Byte, No WriteBack, Pre Dec, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6 && handle_v6_insn (state, instr))
+ break;
+#endif
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ (void) StoreByte (state, instr, LHS - LSRegRHS);
+ break;
+
+ case 0x75: /* Load Byte, No WriteBack, Pre Dec, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6 && handle_v6_insn (state, instr))
+ break;
+#endif
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ (void) LoadByte (state, instr, LHS - LSRegRHS, LUNSIGNED);
+ break;
+
+ case 0x76: /* Store Byte, WriteBack, Pre Dec, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ temp = LHS - LSRegRHS;
+ if (StoreByte (state, instr, temp))
+ LSBase = temp;
+ break;
+
+ case 0x77: /* Load Byte, WriteBack, Pre Dec, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ temp = LHS - LSRegRHS;
+ if (LoadByte (state, instr, temp, LUNSIGNED))
+ LSBase = temp;
+ break;
+
+ case 0x78: /* Store Word, No WriteBack, Pre Inc, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6 && handle_v6_insn (state, instr))
+ break;
+#endif
+
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ (void) StoreWord (state, instr, LHS + LSRegRHS);
+ break;
+
+ case 0x79: /* Load Word, No WriteBack, Pre Inc, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ (void) LoadWord (state, instr, LHS + LSRegRHS);
+ break;
+
+ case 0x7a: /* Store Word, WriteBack, Pre Inc, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6 && handle_v6_insn (state, instr))
+ break;
+#endif
+
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ temp = LHS + LSRegRHS;
+ if (StoreWord (state, instr, temp))
+ LSBase = temp;
+ break;
+
+ case 0x7b: /* Load Word, WriteBack, Pre Inc, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ temp = LHS + LSRegRHS;
+ if (LoadWord (state, instr, temp))
+ LSBase = temp;
+ break;
+
+ case 0x7c: /* Store Byte, No WriteBack, Pre Inc, Reg. */
+ if (BIT (4)) {
+#ifdef MODE32
+ if (state->is_v6 && handle_v6_insn (state, instr))
+ break;
+#endif
+
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ (void) StoreByte (state, instr, LHS + LSRegRHS);
+ break;
+
+ case 0x7d: /* Load Byte, No WriteBack, Pre Inc, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ (void) LoadByte (state, instr, LHS + LSRegRHS, LUNSIGNED);
+ break;
+
+ case 0x7e: /* Store Byte, WriteBack, Pre Inc, Reg. */
+ if (BIT (4)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ temp = LHS + LSRegRHS;
+ if (StoreByte (state, instr, temp))
+ LSBase = temp;
+ break;
+
+ case 0x7f: /* Load Byte, WriteBack, Pre Inc, Reg. */
+ if (BIT (4)) {
+ //LOG_DEBUG(Core_ARM11, "got unhandled special breakpoint");
+ return 1;
+ }
+ UNDEF_LSRBaseEQOffWb;
+ UNDEF_LSRBaseEQDestWb;
+ UNDEF_LSRPCBaseWb;
+ UNDEF_LSRPCOffWb;
+ temp = LHS + LSRegRHS;
+ if (LoadByte (state, instr, temp, LUNSIGNED))
+ LSBase = temp;
+ break;
+
+ /* Multiple Data Transfer Instructions. */
+
+ case 0x80: /* Store, No WriteBack, Post Dec. */
+ STOREMULT (instr, LSBase - LSMNumRegs + 4L, 0L);
+ break;
+
+ case 0x81: /* Load, No WriteBack, Post Dec. */
+ LOADMULT (instr, LSBase - LSMNumRegs + 4L, 0L);
+ break;
+
+ case 0x82: /* Store, WriteBack, Post Dec. */
+ temp = LSBase - LSMNumRegs;
+ STOREMULT (instr, temp + 4L, temp);
+ break;
+
+ case 0x83: /* Load, WriteBack, Post Dec. */
+ temp = LSBase - LSMNumRegs;
+ LOADMULT (instr, temp + 4L, temp);
+ break;
+
+ case 0x84: /* Store, Flags, No WriteBack, Post Dec. */
+ STORESMULT (instr, LSBase - LSMNumRegs + 4L, 0L);
+ break;
+
+ case 0x85: /* Load, Flags, No WriteBack, Post Dec. */
+ LOADSMULT (instr, LSBase - LSMNumRegs + 4L, 0L);
+ break;
+
+ case 0x86: /* Store, Flags, WriteBack, Post Dec. */
+ temp = LSBase - LSMNumRegs;
+ STORESMULT (instr, temp + 4L, temp);
+ break;
+
+ case 0x87: /* Load, Flags, WriteBack, Post Dec. */
+ temp = LSBase - LSMNumRegs;
+ LOADSMULT (instr, temp + 4L, temp);
+ break;
+
+ case 0x88: /* Store, No WriteBack, Post Inc. */
+ STOREMULT (instr, LSBase, 0L);
+ break;
+
+ case 0x89: /* Load, No WriteBack, Post Inc. */
+ LOADMULT (instr, LSBase, 0L);
+ break;
+
+ case 0x8a: /* Store, WriteBack, Post Inc. */
+ temp = LSBase;
+ STOREMULT (instr, temp, temp + LSMNumRegs);
+ break;
+
+ case 0x8b: /* Load, WriteBack, Post Inc. */
+ temp = LSBase;
+ LOADMULT (instr, temp, temp + LSMNumRegs);
+ break;
+
+ case 0x8c: /* Store, Flags, No WriteBack, Post Inc. */
+ STORESMULT (instr, LSBase, 0L);
+ break;
+
+ case 0x8d: /* Load, Flags, No WriteBack, Post Inc. */
+ LOADSMULT (instr, LSBase, 0L);
+ break;
+
+ case 0x8e: /* Store, Flags, WriteBack, Post Inc. */
+ temp = LSBase;
+ STORESMULT (instr, temp, temp + LSMNumRegs);
+ break;
+
+ case 0x8f: /* Load, Flags, WriteBack, Post Inc. */
+ temp = LSBase;
+ LOADSMULT (instr, temp, temp + LSMNumRegs);
+ break;
+
+ case 0x90: /* Store, No WriteBack, Pre Dec. */
+ STOREMULT (instr, LSBase - LSMNumRegs, 0L);
+ break;
+
+ case 0x91: /* Load, No WriteBack, Pre Dec. */
+ LOADMULT (instr, LSBase - LSMNumRegs, 0L);
+ break;
+
+ case 0x92: /* Store, WriteBack, Pre Dec. */
+ temp = LSBase - LSMNumRegs;
+ STOREMULT (instr, temp, temp);
+ break;
+
+ case 0x93: /* Load, WriteBack, Pre Dec. */
+ temp = LSBase - LSMNumRegs;
+ LOADMULT (instr, temp, temp);
+ break;
+
+ case 0x94: /* Store, Flags, No WriteBack, Pre Dec. */
+ STORESMULT (instr, LSBase - LSMNumRegs, 0L);
+ break;
+
+ case 0x95: /* Load, Flags, No WriteBack, Pre Dec. */
+ LOADSMULT (instr, LSBase - LSMNumRegs, 0L);
+ break;
+
+ case 0x96: /* Store, Flags, WriteBack, Pre Dec. */
+ temp = LSBase - LSMNumRegs;
+ STORESMULT (instr, temp, temp);
+ break;
+
+ case 0x97: /* Load, Flags, WriteBack, Pre Dec. */
+ temp = LSBase - LSMNumRegs;
+ LOADSMULT (instr, temp, temp);
+ break;
+
+ case 0x98: /* Store, No WriteBack, Pre Inc. */
+ STOREMULT (instr, LSBase + 4L, 0L);
+ break;
+
+ case 0x99: /* Load, No WriteBack, Pre Inc. */
+ LOADMULT (instr, LSBase + 4L, 0L);
+ break;
+
+ case 0x9a: /* Store, WriteBack, Pre Inc. */
+ temp = LSBase;
+ STOREMULT (instr, temp + 4L, temp + LSMNumRegs);
+ break;
+
+ case 0x9b: /* Load, WriteBack, Pre Inc. */
+ temp = LSBase;
+ LOADMULT (instr, temp + 4L, temp + LSMNumRegs);
+ break;
+
+ case 0x9c: /* Store, Flags, No WriteBack, Pre Inc. */
+ STORESMULT (instr, LSBase + 4L, 0L);
+ break;
+
+ case 0x9d: /* Load, Flags, No WriteBack, Pre Inc. */
+ LOADSMULT (instr, LSBase + 4L, 0L);
+ break;
+
+ case 0x9e: /* Store, Flags, WriteBack, Pre Inc. */
+ temp = LSBase;
+ STORESMULT (instr, temp + 4L, temp + LSMNumRegs);
+ break;
+
+ case 0x9f: /* Load, Flags, WriteBack, Pre Inc. */
+ temp = LSBase;
+ LOADSMULT (instr, temp + 4L, temp + LSMNumRegs);
+ break;
+
+ /* Branch forward. */
+ case 0xa0:
+ case 0xa1:
+ case 0xa2:
+ case 0xa3:
+ case 0xa4:
+ case 0xa5:
+ case 0xa6:
+ case 0xa7:
+ state->Reg[15] = pc + 8 + POSBRANCH;
+ FLUSHPIPE;
+ break;
+
+ /* Branch backward. */
+ case 0xa8:
+ case 0xa9:
+ case 0xaa:
+ case 0xab:
+ case 0xac:
+ case 0xad:
+ case 0xae:
+ case 0xaf:
+ state->Reg[15] = pc + 8 + NEGBRANCH;
+ FLUSHPIPE;
+ break;
+
+ /* Branch and Link forward. */
+ case 0xb0:
+ case 0xb1:
+ case 0xb2:
+ case 0xb3:
+ case 0xb4:
+ case 0xb5:
+ case 0xb6:
+ case 0xb7:
+
+ /* Put PC into Link. */
+#ifdef MODE32
+ state->Reg[14] = pc + 4;
+#else
+ state->Reg[14] = (pc + 4) | ECC | ER15INT | EMODE;
+#endif
+ state->Reg[15] = pc + 8 + POSBRANCH;
+ FLUSHPIPE;
+
+#ifdef callstacker
+ memset(a, 0, 256);
+ aufloeser(a, state->Reg[15]);
+ printf("call %08X %08X %s(%08X %08X %08X %08X %08X %08X %08X)\n", state->Reg[14], state->Reg[15], a, state->Reg[0], state->Reg[1], state->Reg[2], state->Reg[3], mem_Read32(state->Reg[13]), mem_Read32(state->Reg[13] - 4),mem_Read32(state->Reg[13] - 8));
+#endif
+
+ break;
+
+ /* Branch and Link backward. */
+ case 0xb8:
+ case 0xb9:
+ case 0xba:
+ case 0xbb:
+ case 0xbc:
+ case 0xbd:
+ case 0xbe:
+ case 0xbf:
+ /* Put PC into Link. */
+#ifdef MODE32
+ state->Reg[14] = pc + 4;
+#else
+ state->Reg[14] = (pc + 4) | ECC | ER15INT | EMODE;
+#endif
+ state->Reg[15] = pc + 8 + NEGBRANCH;
+ FLUSHPIPE;
+
+#ifdef callstacker
+ memset(a, 0, 256);
+ aufloeser(a, state->Reg[15]);
+ printf("call %08X %08X %s(%08X %08X %08X %08X %08X %08X %08X)\n", state->Reg[14], state->Reg[15], a, state->Reg[0], state->Reg[1], state->Reg[2], state->Reg[3], mem_Read32(state->Reg[13]), mem_Read32(state->Reg[13] - 4),mem_Read32(state->Reg[13] - 8));
+#endif
+
+ break;
+
+ /* Co-Processor Data Transfers. */
+ case 0xc4:
+ if ((instr & 0x0FF00FF0) == 0xC400B10) { //vmov BIT(0-3), BIT(12-15), BIT(16-20), vmov d0, r0, r0
+ state->ExtReg[BITS(0, 3) << 1] = state->Reg[BITS(12, 15)];
+ state->ExtReg[(BITS(0, 3) << 1) + 1] = state->Reg[BITS(16, 20)];
+ break;
+ } else if (state->is_v5) {
+ /* Reading from R15 is UNPREDICTABLE. */
+ if (BITS (12, 15) == 15 || BITS (16, 19) == 15)
+ ARMul_UndefInstr (state, instr);
+ /* Is access to coprocessor 0 allowed ? */
+ else if (!CP_ACCESS_ALLOWED(state, CPNum))
+ ARMul_UndefInstr (state, instr);
+ else {
+ /* MCRR, ARMv5TE and up */
+ ARMul_MCRR (state, instr, DEST, state->Reg[LHSReg]);
+ break;
+ }
+ }
+ /* Drop through. */
+
+ case 0xc0: /* Store , No WriteBack , Post Dec. */
+ ARMul_STC (state, instr, LHS);
+ break;
+
+ case 0xc5:
+ if ((instr & 0x00000FF0) == 0xB10) { //vmov BIT(12-15), BIT(16-20), BIT(0-3) vmov r0, r0, d0
+ state->Reg[BITS(12, 15)] = state->ExtReg[BITS(0, 3) << 1];
+ state->Reg[BITS(16, 19)] = state->ExtReg[(BITS(0, 3) << 1) + 1];
+ break;
+ } else if (state->is_v5) {
+ /* Writes to R15 are UNPREDICATABLE. */
+ if (DESTReg == 15 || LHSReg == 15)
+ ARMul_UndefInstr (state, instr);
+ /* Is access to the coprocessor allowed ? */
+ else if (!CP_ACCESS_ALLOWED(state, CPNum)) {
+ ARMul_UndefInstr(state, instr);
+ } else {
+ /* MRRC, ARMv5TE and up */
+ ARMul_MRRC (state, instr, &DEST, &(state->Reg[LHSReg]));
+ break;
+ }
+ }
+ /* Drop through. */
+
+ case 0xc1: /* Load , No WriteBack , Post Dec. */
+ ARMul_LDC (state, instr, LHS);
+ break;
+
+ case 0xc2:
+ case 0xc6: /* Store , WriteBack , Post Dec. */
+ lhs = LHS;
+ state->Base = lhs - LSCOff;
+ ARMul_STC (state, instr, lhs);
+ break;
+
+ case 0xc3:
+ case 0xc7: /* Load , WriteBack , Post Dec. */
+ lhs = LHS;
+ state->Base = lhs - LSCOff;
+ ARMul_LDC (state, instr, lhs);
+ break;
+
+ case 0xc8:
+ case 0xcc: /* Store , No WriteBack , Post Inc. */
+ ARMul_STC (state, instr, LHS);
+ break;
+
+ case 0xc9:
+ case 0xcd: /* Load , No WriteBack , Post Inc. */
+ ARMul_LDC (state, instr, LHS);
+ break;
+
+ case 0xca:
+ case 0xce: /* Store , WriteBack , Post Inc. */
+ lhs = LHS;
+ state->Base = lhs + LSCOff;
+ ARMul_STC (state, instr, LHS);
+ break;
+
+ case 0xcb:
+ case 0xcf: /* Load , WriteBack , Post Inc. */
+ lhs = LHS;
+ state->Base = lhs + LSCOff;
+ ARMul_LDC (state, instr, LHS);
+ break;
+
+ case 0xd0:
+ case 0xd4: /* Store , No WriteBack , Pre Dec. */
+ ARMul_STC (state, instr, LHS - LSCOff);
+ break;
+
+ case 0xd1:
+ case 0xd5: /* Load , No WriteBack , Pre Dec. */
+ ARMul_LDC (state, instr, LHS - LSCOff);
+ break;
+
+ case 0xd2:
+ case 0xd6: /* Store , WriteBack , Pre Dec. */
+ lhs = LHS - LSCOff;
+ state->Base = lhs;
+ ARMul_STC (state, instr, lhs);
+ break;
+
+ case 0xd3:
+ case 0xd7: /* Load , WriteBack , Pre Dec. */
+ lhs = LHS - LSCOff;
+ state->Base = lhs;
+ ARMul_LDC (state, instr, lhs);
+ break;
+
+ case 0xd8:
+ case 0xdc: /* Store , No WriteBack , Pre Inc. */
+ ARMul_STC (state, instr, LHS + LSCOff);
+ break;
+
+ case 0xd9:
+ case 0xdd: /* Load , No WriteBack , Pre Inc. */
+ ARMul_LDC (state, instr, LHS + LSCOff);
+ break;
+
+ case 0xda:
+ case 0xde: /* Store , WriteBack , Pre Inc. */
+ lhs = LHS + LSCOff;
+ state->Base = lhs;
+ ARMul_STC (state, instr, lhs);
+ break;
+
+ case 0xdb:
+ case 0xdf: /* Load , WriteBack , Pre Inc. */
+ lhs = LHS + LSCOff;
+ state->Base = lhs;
+ ARMul_LDC (state, instr, lhs);
+ break;
+
+ /* Co-Processor Register Transfers (MCR) and Data Ops. */
+
+ case 0xe2:
+ /*if (!CP_ACCESS_ALLOWED (state, CPNum)) {
+ ARMul_UndefInstr (state, instr);
+ break;
+ }*/
+
+ case 0xe0:
+ case 0xe4:
+ case 0xe6:
+ case 0xe8:
+ case 0xea:
+ case 0xec:
+ case 0xee:
+ if (BIT (4)) {
+ /* MCR. */
+ if (DESTReg == 15) {
+ UNDEF_MCRPC;
+#ifdef MODE32
+ ARMul_MCR (state, instr, state->Reg[15] + isize);
+#else
+ ARMul_MCR (state, instr, ECC | ER15INT | EMODE | ((state->Reg[15] + isize) & R15PCBITS));
+#endif
+ } else
+ ARMul_MCR (state, instr, DEST);
+ } else
+ /* CDP Part 1. */
+ ARMul_CDP (state, instr);
+ break;
+
+ /* Co-Processor Register Transfers (MRC) and Data Ops. */
+ case 0xe1:
+ case 0xe3:
+ case 0xe5:
+ case 0xe7:
+ case 0xe9:
+ case 0xeb:
+ case 0xed:
+ case 0xef:
+ if (BIT (4)) {
+ /* MRC */
+ temp = ARMul_MRC (state, instr);
+ if (DESTReg == 15) {
+ ASSIGNN ((temp & NBIT) != 0);
+ ASSIGNZ ((temp & ZBIT) != 0);
+ ASSIGNC ((temp & CBIT) != 0);
+ ASSIGNV ((temp & VBIT) != 0);
+ } else
+ DEST = temp;
+ } else
+ /* CDP Part 2. */
+ ARMul_CDP (state, instr);
+ break;
+
+ /* SWI instruction. */
+ case 0xf0:
+ case 0xf1:
+ case 0xf2:
+ case 0xf3:
+ case 0xf4:
+ case 0xf5:
+ case 0xf6:
+ case 0xf7:
+ case 0xf8:
+ case 0xf9:
+ case 0xfa:
+ case 0xfb:
+ case 0xfc:
+ case 0xfd:
+ case 0xfe:
+ case 0xff:
+ //svc_Execute(state, BITS(0, 23));
+ ProcessSwi(BITS(0, 7), state->Reg, state->m_currentThread);
+
+ break;
+ }
+ }
+
+#ifdef MODET
+donext:
+#endif
+ state->pc = pc;
+#if 0
+ /* shenoubang */
+ instr_sum++;
+ int i, j;
+ i = j = 0;
+ if (instr_sum >= 7388648) {
+ //if (pc == 0xc0008ab4) {
+ // printf("instr_sum: %d\n", instr_sum);
+ // start_kernel : 0xc000895c
+ printf("--------------------------------------------------\n");
+ for (i = 0; i < 16; i++) {
+ printf("[R%02d]:[0x%08x]\t", i, state->Reg[i]);
+ if ((i % 3) == 2) {
+ printf("\n");
+ }
+ }
+ printf("[cpr]:[0x%08x]\t[spr0]:[0x%08x]\n", state->Cpsr, state->Spsr[0]);
+ for (j = 1; j < 7; j++) {
+ printf("[spr%d]:[0x%08x]\t", j, state->Spsr[j]);
+ if ((j % 4) == 3) {
+ printf("\n");
+ }
+ }
+ printf("\n[PC]:[0x%08x]\t[INST]:[0x%08x]\t[COUNT]:[%d]\n", pc, instr, instr_sum);
+ printf("--------------------------------------------------\n");
+ }
+#endif
+
+#if 0
+ fprintf(state->state_log, "PC:0x%x\n", pc);
+ for (reg_index = 0; reg_index < 16; reg_index ++) {
+ if (state->Reg[reg_index] != mirror_register_file[reg_index]) {
+ fprintf(state->state_log, "R%d:0x%x\n", reg_index, state->Reg[reg_index]);
+ mirror_register_file[reg_index] = state->Reg[reg_index];
+ }
+ }
+ if (state->Cpsr != mirror_register_file[CPSR_REG]) {
+ fprintf(state->state_log, "Cpsr:0x%x\n", state->Cpsr);
+ mirror_register_file[CPSR_REG] = state->Cpsr;
+ }
+ if (state->RegBank[SVCBANK][13] != mirror_register_file[R13_SVC]) {
+ fprintf(state->state_log, "R13_SVC:0x%x\n", state->RegBank[SVCBANK][13]);
+ mirror_register_file[R13_SVC] = state->RegBank[SVCBANK][13];
+ }
+ if (state->RegBank[SVCBANK][14] != mirror_register_file[R14_SVC]) {
+ fprintf(state->state_log, "R14_SVC:0x%x\n", state->RegBank[SVCBANK][14]);
+ mirror_register_file[R14_SVC] = state->RegBank[SVCBANK][14];
+ }
+ if (state->RegBank[ABORTBANK][13] != mirror_register_file[R13_ABORT]) {
+ fprintf(state->state_log, "R13_ABORT:0x%x\n", state->RegBank[ABORTBANK][13]);
+ mirror_register_file[R13_ABORT] = state->RegBank[ABORTBANK][13];
+ }
+ if (state->RegBank[ABORTBANK][14] != mirror_register_file[R14_ABORT]) {
+ fprintf(state->state_log, "R14_ABORT:0x%x\n", state->RegBank[ABORTBANK][14]);
+ mirror_register_file[R14_ABORT] = state->RegBank[ABORTBANK][14];
+ }
+ if (state->RegBank[UNDEFBANK][13] != mirror_register_file[R13_UNDEF]) {
+ fprintf(state->state_log, "R13_UNDEF:0x%x\n", state->RegBank[UNDEFBANK][13]);
+ mirror_register_file[R13_UNDEF] = state->RegBank[UNDEFBANK][13];
+ }
+ if (state->RegBank[UNDEFBANK][14] != mirror_register_file[R14_UNDEF]) {
+ fprintf(state->state_log, "R14_UNDEF:0x%x\n", state->RegBank[UNDEFBANK][14]);
+ mirror_register_file[R14_UNDEF] = state->RegBank[UNDEFBANK][14];
+ }
+ if (state->RegBank[IRQBANK][13] != mirror_register_file[R13_IRQ]) {
+ fprintf(state->state_log, "R13_IRQ:0x%x\n", state->RegBank[IRQBANK][13]);
+ mirror_register_file[R13_IRQ] = state->RegBank[IRQBANK][13];
+ }
+ if (state->RegBank[IRQBANK][14] != mirror_register_file[R14_IRQ]) {
+ fprintf(state->state_log, "R14_IRQ:0x%x\n", state->RegBank[IRQBANK][14]);
+ mirror_register_file[R14_IRQ] = state->RegBank[IRQBANK][14];
+ }
+ if (state->RegBank[FIQBANK][8] != mirror_register_file[R8_FIRQ]) {
+ fprintf(state->state_log, "R8_FIRQ:0x%x\n", state->RegBank[FIQBANK][8]);
+ mirror_register_file[R8_FIRQ] = state->RegBank[FIQBANK][8];
+ }
+ if (state->RegBank[FIQBANK][9] != mirror_register_file[R9_FIRQ]) {
+ fprintf(state->state_log, "R9_FIRQ:0x%x\n", state->RegBank[FIQBANK][9]);
+ mirror_register_file[R9_FIRQ] = state->RegBank[FIQBANK][9];
+ }
+ if (state->RegBank[FIQBANK][10] != mirror_register_file[R10_FIRQ]) {
+ fprintf(state->state_log, "R10_FIRQ:0x%x\n", state->RegBank[FIQBANK][10]);
+ mirror_register_file[R10_FIRQ] = state->RegBank[FIQBANK][10];
+ }
+ if (state->RegBank[FIQBANK][11] != mirror_register_file[R11_FIRQ]) {
+ fprintf(state->state_log, "R11_FIRQ:0x%x\n", state->RegBank[FIQBANK][11]);
+ mirror_register_file[R11_FIRQ] = state->RegBank[FIQBANK][11];
+ }
+ if (state->RegBank[FIQBANK][12] != mirror_register_file[R12_FIRQ]) {
+ fprintf(state->state_log, "R12_FIRQ:0x%x\n", state->RegBank[FIQBANK][12]);
+ mirror_register_file[R12_FIRQ] = state->RegBank[FIQBANK][12];
+ }
+ if (state->RegBank[FIQBANK][13] != mirror_register_file[R13_FIRQ]) {
+ fprintf(state->state_log, "R13_FIRQ:0x%x\n", state->RegBank[FIQBANK][13]);
+ mirror_register_file[R13_FIRQ] = state->RegBank[FIQBANK][13];
+ }
+ if (state->RegBank[FIQBANK][14] != mirror_register_file[R14_FIRQ]) {
+ fprintf(state->state_log, "R14_FIRQ:0x%x\n", state->RegBank[FIQBANK][14]);
+ mirror_register_file[R14_FIRQ] = state->RegBank[FIQBANK][14];
+ }
+ if (state->Spsr[SVCBANK] != mirror_register_file[SPSR_SVC]) {
+ fprintf(state->state_log, "SPSR_SVC:0x%x\n", state->Spsr[SVCBANK]);
+ mirror_register_file[SPSR_SVC] = state->RegBank[SVCBANK];
+ }
+ if (state->Spsr[ABORTBANK] != mirror_register_file[SPSR_ABORT]) {
+ fprintf(state->state_log, "SPSR_ABORT:0x%x\n", state->Spsr[ABORTBANK]);
+ mirror_register_file[SPSR_ABORT] = state->RegBank[ABORTBANK];
+ }
+ if (state->Spsr[UNDEFBANK] != mirror_register_file[SPSR_UNDEF]) {
+ fprintf(state->state_log, "SPSR_UNDEF:0x%x\n", state->Spsr[UNDEFBANK]);
+ mirror_register_file[SPSR_UNDEF] = state->RegBank[UNDEFBANK];
+ }
+ if (state->Spsr[IRQBANK] != mirror_register_file[SPSR_IRQ]) {
+ fprintf(state->state_log, "SPSR_IRQ:0x%x\n", state->Spsr[IRQBANK]);
+ mirror_register_file[SPSR_IRQ] = state->RegBank[IRQBANK];
+ }
+ if (state->Spsr[FIQBANK] != mirror_register_file[SPSR_FIRQ]) {
+ fprintf(state->state_log, "SPSR_FIRQ:0x%x\n", state->Spsr[FIQBANK]);
+ mirror_register_file[SPSR_FIRQ] = state->RegBank[FIQBANK];
+ }
+
+#endif
+
+#ifdef NEED_UI_LOOP_HOOK
+ if (ui_loop_hook != NULL && ui_loop_hook_counter-- < 0) {
+ ui_loop_hook_counter = UI_LOOP_POLL_INTERVAL;
+ ui_loop_hook (0);
+ }
+#endif /* NEED_UI_LOOP_HOOK */
+
+ /*added energy_prof statement by ksh in 2004-11-26 */
+ //chy 2005-07-28 for standalone
+ //ARMul_do_energy(state,instr,pc);
+//teawater add for record reg value to ./reg.txt 2005.07.10---------------------
+ if (state->tea_break_ok && pc == state->tea_break_addr) {
+ //ARMul_Debug (state, 0, 0);
+ state->tea_break_ok = 0;
+ } else {
+ state->tea_break_ok = 1;
+ }
+//AJ2D--------------------------------------------------------------------------
+//chy 2006-04-14 for ctrl-c debug
+#if 0
+ if (debugmode) {
+ if (instr != ARMul_ABORTWORD) {
+ remote_interrupt_test_time++;
+ //chy 2006-04-14 2000 should be changed in skyeye_conf ???!!!
+ if (remote_interrupt_test_time >= 2000) {
+ remote_interrupt_test_time=0;
+ if (remote_interrupt()) {
+ //for test
+ //printf("SKYEYE: ICE_debug recv Ctrl_C\n");
+ state->EndCondition = 0;
+ state->Emulate = STOP;
+ }
+ }
+ }
+ }
+#endif
+
+ /* jump out every time */
+ //state->EndCondition = 0;
+ //state->Emulate = STOP;
+//chy 2006-04-12 for ICE debug
+TEST_EMULATE:
+ if (state->Emulate == ONCE)
+ state->Emulate = STOP;
+ //chy: 2003-08-23: should not use CHANGEMODE !!!!
+ /* If we have changed mode, allow the PC to advance before stopping. */
+ // else if (state->Emulate == CHANGEMODE)
+ // continue;
+ else if (state->Emulate != RUN)
+ break;
+ }
+
+ while (state->NumInstrsToExecute);
+exit:
+ state->decoded = decoded;
+ state->loaded = loaded;
+ state->pc = pc;
+ //chy 2006-04-12, for ICE debug
+ state->decoded_addr=decoded_addr;
+ state->loaded_addr=loaded_addr;
+
+ return pc;
+ }
+
+//teawater add for arm2x86 2005.02.17-------------------------------------------
+ /*ywc 2005-04-01*/
+//#include "tb.h"
+//#include "arm2x86_self.h"
+
+ static volatile void (*gen_func) (void);
+//static volatile ARMul_State *tmp_st;
+//static volatile ARMul_State *save_st;
+ static volatile uint32_t tmp_st;
+ static volatile uint32_t save_st;
+ static volatile uint32_t save_T0;
+ static volatile uint32_t save_T1;
+ static volatile uint32_t save_T2;
+
+#ifdef MODE32
+#ifdef DBCT
+//teawater change for debug function 2005.07.09---------------------------------
+ ARMword
+ ARMul_Emulate32_dbct (ARMul_State * state) {
+ static int init = 0;
+ static FILE *fd;
+
+ /*if (!init) {
+ fd = fopen("./pc.txt", "w");
+ if (!fd) {
+ exit(-1);
+ }
+ init = 1;
+ } */
+
+ state->Reg[15] += INSN_SIZE;
+ do {
+ /*if (skyeye_config.log.logon>=1) {
+ if (state->NumInstrs>=skyeye_config.log.start && state->NumInstrs<=skyeye_config.log.end) {
+ static int mybegin=0;
+ static int myinstrnum=0;
+
+ if (mybegin==0) mybegin=1;
+ if (mybegin==1) {
+ state->Reg[15] -= INSN_SIZE;
+ if (skyeye_config.log.logon>=1) fprintf(skyeye_logfd,"N %llx :p %x,i %x,",state->NumInstrs, (state->Reg[15] - INSN_SIZE), instr);
+ if (skyeye_config.log.logon>=2) SKYEYE_OUTREGS(skyeye_logfd);
+ if (skyeye_config.log.logon>=3) SKYEYE_OUTMOREREGS(skyeye_logfd);
+ fprintf(skyeye_logfd,"\n");
+ if (skyeye_config.log.length>0) {
+ myinstrnum++;
+ if (myinstrnum>=skyeye_config.log.length) {
+ myinstrnum=0;
+ fflush(skyeye_logfd);
+ fseek(skyeye_logfd,0L,SEEK_SET);
+ }
+ }
+ state->Reg[15] += INSN_SIZE;
+ }
+ }
+ } */
+ state->trap = 0;
+ gen_func =
+ (void *) tb_find (state, state->Reg[15] - INSN_SIZE);
+ if (!gen_func) {
+ //fprintf(stderr, "SKYEYE: tb_find: Error in find the translate block.\n");
+ //exit(-1);
+ //TRAP_INSN_ABORT
+ //TEA_OUT(printf("\n------------\npc:%x\n", state->Reg[15] - INSN_SIZE));
+ //TEA_OUT(printf("TRAP_INSN_ABORT\n"));
+//teawater add for xscale(arm v5) 2005.09.01------------------------------------
+ /*XScale_set_fsr_far(state, ARMul_CP15_R5_MMU_EXCPT, state->Reg[15] - INSN_SIZE);
+ state->Reg[15] += INSN_SIZE;
+ ARMul_Abort(state, ARMul_PrefetchAbortV);
+ state->Reg[15] += INSN_SIZE;
+ goto next; */
+ state->trap = TRAP_INSN_ABORT;
+ goto check;
+//AJ2D--------------------------------------------------------------------------
+ }
+
+ save_st = (uint32_t) st;
+ save_T0 = T0;
+ save_T1 = T1;
+ save_T2 = T2;
+ tmp_st = (uint32_t) state;
+ wmb ();
+ st = (ARMul_State *) tmp_st;
+ gen_func ();
+ st = (ARMul_State *) save_st;
+ T0 = save_T0;
+ T1 = save_T1;
+ T2 = save_T2;
+
+ /*if (state->trap != TRAP_OUT) {
+ state->tea_break_ok = 1;
+ }
+ if (state->trap <= TRAP_SET_R15) {
+ goto next;
+ } */
+ //TEA_OUT(printf("\n------------\npc:%x\n", state->Reg[15] - INSN_SIZE));
+//teawater add check thumb 2005.07.21-------------------------------------------
+ /*if (TFLAG) {
+ state->Reg[15] -= 2;
+ return(state->Reg[15]);
+ } */
+//AJ2D--------------------------------------------------------------------------
+
+//teawater add for xscale(arm v5) 2005.09.01------------------------------------
+check:
+//AJ2D--------------------------------------------------------------------------
+ switch (state->trap) {
+ case TRAP_RESET: {
+ //TEA_OUT(printf("TRAP_RESET\n"));
+ ARMul_Abort (state, ARMul_ResetV);
+ state->Reg[15] += INSN_SIZE;
+ }
+ break;
+ case TRAP_UNPREDICTABLE: {
+ //ARMul_Debug (state, 0, 0);
+ }
+ break;
+ case TRAP_INSN_UNDEF: {
+ //TEA_OUT(printf("TRAP_INSN_UNDEF\n"));
+ state->Reg[15] += INSN_SIZE;
+ ARMul_UndefInstr (state, 0);
+ state->Reg[15] += INSN_SIZE;
+ }
+ break;
+ case TRAP_SWI: {
+ //TEA_OUT(printf("TRAP_SWI\n"));
+ state->Reg[15] += INSN_SIZE;
+ ARMul_Abort (state, ARMul_SWIV);
+ state->Reg[15] += INSN_SIZE;
+ }
+ break;
+//teawater add for xscale(arm v5) 2005.09.01------------------------------------
+ case TRAP_INSN_ABORT: {
+ /*XScale_set_fsr_far (state,
+ ARMul_CP15_R5_MMU_EXCPT,
+ state->Reg[15] -
+ INSN_SIZE);*/
+ state->Reg[15] += INSN_SIZE;
+ ARMul_Abort (state, ARMul_PrefetchAbortV);
+ state->Reg[15] += INSN_SIZE;
+ }
+ break;
+//AJ2D--------------------------------------------------------------------------
+ case TRAP_DATA_ABORT: {
+ //TEA_OUT(printf("TRAP_DATA_ABORT\n"));
+ state->Reg[15] += INSN_SIZE;
+ ARMul_Abort (state, ARMul_DataAbortV);
+ state->Reg[15] += INSN_SIZE;
+ }
+ break;
+ case TRAP_IRQ: {
+ //TEA_OUT(printf("TRAP_IRQ\n"));
+ state->Reg[15] += INSN_SIZE;
+ ARMul_Abort (state, ARMul_IRQV);
+ state->Reg[15] += INSN_SIZE;
+ }
+ break;
+ case TRAP_FIQ: {
+ //TEA_OUT(printf("TRAP_FIQ\n"));
+ state->Reg[15] += INSN_SIZE;
+ ARMul_Abort (state, ARMul_FIQV);
+ state->Reg[15] += INSN_SIZE;
+ }
+ break;
+ case TRAP_SETS_R15: {
+ //TEA_OUT(printf("TRAP_SETS_R15\n"));
+ /*if (state->Bank > 0) {
+ state->Cpsr = state->Spsr[state->Bank];
+ ARMul_CPSRAltered (state);
+ } */
+ WriteSR15 (state, state->Reg[15]);
+ }
+ break;
+ case TRAP_SET_CPSR: {
+ //TEA_OUT(printf("TRAP_SET_CPSR\n"));
+ //chy 2006-02-15 USERBANK=SYSTEMBANK=0
+ //chy 2006-02-16 should use Mode to test
+ //if (state->Bank > 0) {
+ if (state->Mode != USER26MODE && state->Mode != USER32MODE) {
+ //ARMul_CPSRAltered (state);
+ }
+ state->Reg[15] += INSN_SIZE;
+ }
+ break;
+ case TRAP_OUT: {
+ //TEA_OUT(printf("TRAP_OUT\n"));
+ goto out;
+ }
+ break;
+ case TRAP_BREAKPOINT: {
+ //TEA_OUT(printf("TRAP_BREAKPOINT\n"));
+ state->Reg[15] -= INSN_SIZE;
+ if (!ARMul_OSHandleSWI
+ (state, SWI_Breakpoint)) {
+ ARMul_Abort (state, ARMul_SWIV);
+ }
+ state->Reg[15] += INSN_SIZE;
+ }
+ break;
+ }
+
+next:
+ if (state->Emulate == ONCE) {
+ state->Emulate = STOP;
+ break;
+ } else if (state->Emulate != RUN) {
+ break;
+ }
+ } while (!state->stop_simulator);
+
+out:
+ state->Reg[15] -= INSN_SIZE;
+ return (state->Reg[15]);
+ }
+#endif
+//AJ2D--------------------------------------------------------------------------
+#endif
+//AJ2D--------------------------------------------------------------------------
+
+ /* This routine evaluates most Data Processing register RHS's with the S
+ bit clear. It is intended to be called from the macro DPRegRHS, which
+ filters the common case of an unshifted register with in line code. */
+
+ static ARMword
+ GetDPRegRHS (ARMul_State * state, ARMword instr) {
+ ARMword shamt, base;
+
+ base = RHSReg;
+ if (BIT (4)) {
+ /* Shift amount in a register. */
+ UNDEF_Shift;
+ INCPC;
+#ifndef MODE32
+ if (base == 15)
+ base = ECC | ER15INT | R15PC | EMODE;
+ else
+#endif
+ base = state->Reg[base];
+ ARMul_Icycles (state, 1, 0L);
+ shamt = state->Reg[BITS (8, 11)] & 0xff;
+ switch ((int) BITS (5, 6)) {
+ case LSL:
+ if (shamt == 0)
+ return (base);
+ else if (shamt >= 32)
+ return (0);
+ else
+ return (base << shamt);
+ case LSR:
+ if (shamt == 0)
+ return (base);
+ else if (shamt >= 32)
+ return (0);
+ else
+ return (base >> shamt);
+ case ASR:
+ if (shamt == 0)
+ return (base);
+ else if (shamt >= 32)
+ return ((ARMword) ((int) base >> 31L));
+ else
+ return ((ARMword)
+ (( int) base >> (int) shamt));
+ case ROR:
+ shamt &= 0x1f;
+ if (shamt == 0)
+ return (base);
+ else
+ return ((base << (32 - shamt)) |
+ (base >> shamt));
+ }
+ } else {
+ /* Shift amount is a constant. */
+#ifndef MODE32
+ if (base == 15)
+ base = ECC | ER15INT | R15PC | EMODE;
+ else
+#endif
+ base = state->Reg[base];
+ shamt = BITS (7, 11);
+ switch ((int) BITS (5, 6)) {
+ case LSL:
+ return (base << shamt);
+ case LSR:
+ if (shamt == 0)
+ return (0);
+ else
+ return (base >> shamt);
+ case ASR:
+ if (shamt == 0)
+ return ((ARMword) (( int) base >> 31L));
+ else
+ return ((ARMword)
+ (( int) base >> (int) shamt));
+ case ROR:
+ if (shamt == 0)
+ /* It's an RRX. */
+ return ((base >> 1) | (CFLAG << 31));
+ else
+ return ((base << (32 - shamt)) |
+ (base >> shamt));
+ }
+ }
+
+ return 0;
+ }
+
+ /* This routine evaluates most Logical Data Processing register RHS's
+ with the S bit set. It is intended to be called from the macro
+ DPSRegRHS, which filters the common case of an unshifted register
+ with in line code. */
+
+ static ARMword
+ GetDPSRegRHS (ARMul_State * state, ARMword instr) {
+ ARMword shamt, base;
+
+ base = RHSReg;
+ if (BIT (4)) {
+ /* Shift amount in a register. */
+ UNDEF_Shift;
+ INCPC;
+#ifndef MODE32
+ if (base == 15)
+ base = ECC | ER15INT | R15PC | EMODE;
+ else
+#endif
+ base = state->Reg[base];
+ ARMul_Icycles (state, 1, 0L);
+ shamt = state->Reg[BITS (8, 11)] & 0xff;
+ switch ((int) BITS (5, 6)) {
+ case LSL:
+ if (shamt == 0)
+ return (base);
+ else if (shamt == 32) {
+ ASSIGNC (base & 1);
+ return (0);
+ } else if (shamt > 32) {
+ CLEARC;
+ return (0);
+ } else {
+ ASSIGNC ((base >> (32 - shamt)) & 1);
+ return (base << shamt);
+ }
+ case LSR:
+ if (shamt == 0)
+ return (base);
+ else if (shamt == 32) {
+ ASSIGNC (base >> 31);
+ return (0);
+ } else if (shamt > 32) {
+ CLEARC;
+ return (0);
+ } else {
+ ASSIGNC ((base >> (shamt - 1)) & 1);
+ return (base >> shamt);
+ }
+ case ASR:
+ if (shamt == 0)
+ return (base);
+ else if (shamt >= 32) {
+ ASSIGNC (base >> 31L);
+ return ((ARMword) (( int) base >> 31L));
+ } else {
+ ASSIGNC ((ARMword)
+ (( int) base >>
+ (int) (shamt - 1)) & 1);
+ return ((ARMword)
+ ((int) base >> (int) shamt));
+ }
+ case ROR:
+ if (shamt == 0)
+ return (base);
+ shamt &= 0x1f;
+ if (shamt == 0) {
+ ASSIGNC (base >> 31);
+ return (base);
+ } else {
+ ASSIGNC ((base >> (shamt - 1)) & 1);
+ return ((base << (32 - shamt)) |
+ (base >> shamt));
+ }
+ }
+ } else {
+ /* Shift amount is a constant. */
+#ifndef MODE32
+ if (base == 15)
+ base = ECC | ER15INT | R15PC | EMODE;
+ else
+#endif
+ base = state->Reg[base];
+ shamt = BITS (7, 11);
+
+ switch ((int) BITS (5, 6)) {
+ case LSL:
+ ASSIGNC ((base >> (32 - shamt)) & 1);
+ return (base << shamt);
+ case LSR:
+ if (shamt == 0) {
+ ASSIGNC (base >> 31);
+ return (0);
+ } else {
+ ASSIGNC ((base >> (shamt - 1)) & 1);
+ return (base >> shamt);
+ }
+ case ASR:
+ if (shamt == 0) {
+ ASSIGNC (base >> 31L);
+ return ((ARMword) ((int) base >> 31L));
+ } else {
+ ASSIGNC ((ARMword)
+ ((int) base >>
+ (int) (shamt - 1)) & 1);
+ return ((ARMword)
+ (( int) base >> (int) shamt));
+ }
+ case ROR:
+ if (shamt == 0) {
+ /* It's an RRX. */
+ shamt = CFLAG;
+ ASSIGNC (base & 1);
+ return ((base >> 1) | (shamt << 31));
+ } else {
+ ASSIGNC ((base >> (shamt - 1)) & 1);
+ return ((base << (32 - shamt)) |
+ (base >> shamt));
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ /* This routine handles writes to register 15 when the S bit is not set. */
+
+ static void
+ WriteR15 (ARMul_State * state, ARMword src) {
+ /* The ARM documentation states that the two least significant bits
+ are discarded when setting PC, except in the cases handled by
+ WriteR15Branch() below. It's probably an oversight: in THUMB
+ mode, the second least significant bit should probably not be
+ discarded. */
+#ifdef MODET
+ if (TFLAG)
+ src &= 0xfffffffe;
+ else
+#endif
+ src &= 0xfffffffc;
+
+#ifdef MODE32
+ state->Reg[15] = src & PCBITS;
+#else
+ state->Reg[15] = (src & R15PCBITS) | ECC | ER15INT | EMODE;
+ ARMul_R15Altered (state);
+#endif
+
+ FLUSHPIPE;
+ }
+
+ /* This routine handles writes to register 15 when the S bit is set. */
+
+ static void
+ WriteSR15 (ARMul_State * state, ARMword src) {
+#ifdef MODE32
+ if (state->Bank > 0) {
+ state->Cpsr = state->Spsr[state->Bank];
+ ARMul_CPSRAltered (state);
+ }
+#ifdef MODET
+ if (TFLAG)
+ src &= 0xfffffffe;
+ else
+#endif
+ src &= 0xfffffffc;
+ state->Reg[15] = src & PCBITS;
+#else
+#ifdef MODET
+ if (TFLAG)
+ /* ARMul_R15Altered would have to support it. */
+ abort ();
+ else
+#endif
+ src &= 0xfffffffc;
+
+ if (state->Bank == USERBANK)
+ state->Reg[15] =
+ (src & (CCBITS | R15PCBITS)) | ER15INT | EMODE;
+ else
+ state->Reg[15] = src;
+
+ ARMul_R15Altered (state);
+#endif
+ FLUSHPIPE;
+ }
+
+ /* In machines capable of running in Thumb mode, BX, BLX, LDR and LDM
+ will switch to Thumb mode if the least significant bit is set. */
+
+ static void
+ WriteR15Branch (ARMul_State * state, ARMword src) {
+#ifdef MODET
+ if (src & 1) {
+ /* Thumb bit. */
+ SETT;
+ state->Reg[15] = src & 0xfffffffe;
+ } else {
+ CLEART;
+ state->Reg[15] = src & 0xfffffffc;
+ }
+ state->Cpsr = ARMul_GetCPSR (state);
+ FLUSHPIPE;
+#else
+ WriteR15 (state, src);
+#endif
+ }
+
+ /* This routine evaluates most Load and Store register RHS's. It is
+ intended to be called from the macro LSRegRHS, which filters the
+ common case of an unshifted register with in line code. */
+
+ static ARMword
+ GetLSRegRHS (ARMul_State * state, ARMword instr) {
+ ARMword shamt, base;
+
+ base = RHSReg;
+#ifndef MODE32
+ if (base == 15)
+ /* Now forbidden, but ... */
+ base = ECC | ER15INT | R15PC | EMODE;
+ else
+#endif
+ base = state->Reg[base];
+
+ shamt = BITS (7, 11);
+ switch ((int) BITS (5, 6)) {
+ case LSL:
+ return (base << shamt);
+ case LSR:
+ if (shamt == 0)
+ return (0);
+ else
+ return (base >> shamt);
+ case ASR:
+ if (shamt == 0)
+ return ((ARMword) (( int) base >> 31L));
+ else
+ return ((ARMword) (( int) base >> (int) shamt));
+ case ROR:
+ if (shamt == 0)
+ /* It's an RRX. */
+ return ((base >> 1) | (CFLAG << 31));
+ else
+ return ((base << (32 - shamt)) | (base >> shamt));
+ default:
+ break;
+ }
+ return 0;
+ }
+
+ /* This routine evaluates the ARM7T halfword and signed transfer RHS's. */
+
+ static ARMword
+ GetLS7RHS (ARMul_State * state, ARMword instr) {
+ if (BIT (22) == 0) {
+ /* Register. */
+#ifndef MODE32
+ if (RHSReg == 15)
+ /* Now forbidden, but ... */
+ return ECC | ER15INT | R15PC | EMODE;
+#endif
+ return state->Reg[RHSReg];
+ }
+
+ /* Immediate. */
+ return BITS (0, 3) | (BITS (8, 11) << 4);
+ }
+
+ /* This function does the work of loading a word for a LDR instruction. */
+#define MEM_LOAD_LOG(description) if (skyeye_config.log.memlogon >= 1) { \
+ fprintf(skyeye_logfd, \
+ "m LOAD %s: N %llx :p %x :i %x :a %x :d %x\n", \
+ description, state->NumInstrs, state->pc, instr, \
+ address, dest); \
+ }
+
+#define MEM_STORE_LOG(description) if (skyeye_config.log.memlogon >= 1) { \
+ fprintf(skyeye_logfd, \
+ "m STORE %s: N %llx :p %x :i %x :a %x :d %x\n", \
+ description, state->NumInstrs, state->pc, instr, \
+ address, DEST); \
+ }
+
+ static unsigned
+ LoadWord (ARMul_State * state, ARMword instr, ARMword address) {
+ ARMword dest;
+
+ BUSUSEDINCPCS;
+#ifndef MODE32
+ if (ADDREXCEPT (address))
+ INTERNALABORT (address);
+#endif
+
+ dest = ARMul_LoadWordN (state, address);
+
+ if (state->Aborted) {
+ TAKEABORT;
+ return state->lateabtSig;
+ }
+ if (address & 3)
+ dest = ARMul_Align (state, address, dest);
+ WRITEDESTB (dest);
+ ARMul_Icycles (state, 1, 0L);
+
+ //MEM_LOAD_LOG("WORD");
+
+ return (DESTReg != LHSReg);
+ }
+
+#ifdef MODET
+ /* This function does the work of loading a halfword. */
+
+ static unsigned
+ LoadHalfWord (ARMul_State * state, ARMword instr, ARMword address,
+ int signextend) {
+ ARMword dest;
+
+ BUSUSEDINCPCS;
+#ifndef MODE32
+ if (ADDREXCEPT (address))
+ INTERNALABORT (address);
+#endif
+ dest = ARMul_LoadHalfWord (state, address);
+ if (state->Aborted) {
+ TAKEABORT;
+ return state->lateabtSig;
+ }
+ UNDEF_LSRBPC;
+ if (signextend)
+ if (dest & 1 << (16 - 1))
+ dest = (dest & ((1 << 16) - 1)) - (1 << 16);
+
+ WRITEDEST (dest);
+ ARMul_Icycles (state, 1, 0L);
+
+ //MEM_LOAD_LOG("HALFWORD");
+
+ return (DESTReg != LHSReg);
+ }
+
+#endif /* MODET */
+
+ /* This function does the work of loading a byte for a LDRB instruction. */
+
+ static unsigned
+ LoadByte (ARMul_State * state, ARMword instr, ARMword address, int signextend) {
+ ARMword dest;
+
+ BUSUSEDINCPCS;
+#ifndef MODE32
+ if (ADDREXCEPT (address))
+ INTERNALABORT (address);
+#endif
+ dest = ARMul_LoadByte (state, address);
+ if (state->Aborted) {
+ TAKEABORT;
+ return state->lateabtSig;
+ }
+ UNDEF_LSRBPC;
+ if (signextend)
+ if (dest & 1 << (8 - 1))
+ dest = (dest & ((1 << 8) - 1)) - (1 << 8);
+
+ WRITEDEST (dest);
+ ARMul_Icycles (state, 1, 0L);
+
+ //MEM_LOAD_LOG("BYTE");
+
+ return (DESTReg != LHSReg);
+ }
+
+ /* This function does the work of loading two words for a LDRD instruction. */
+
+ static void
+ Handle_Load_Double (ARMul_State * state, ARMword instr) {
+ ARMword dest_reg;
+ ARMword addr_reg;
+ ARMword write_back = BIT (21);
+ ARMword immediate = BIT (22);
+ ARMword add_to_base = BIT (23);
+ ARMword pre_indexed = BIT (24);
+ ARMword offset;
+ ARMword addr;
+ ARMword sum;
+ ARMword base;
+ ARMword value1;
+ ARMword value2;
+
+ BUSUSEDINCPCS;
+
+ /* If the writeback bit is set, the pre-index bit must be clear. */
+ if (write_back && !pre_indexed) {
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+
+ /* Extract the base address register. */
+ addr_reg = LHSReg;
+
+ /* Extract the destination register and check it. */
+ dest_reg = DESTReg;
+
+ /* Destination register must be even. */
+ if ((dest_reg & 1)
+ /* Destination register cannot be LR. */
+ || (dest_reg == 14)) {
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+
+ /* Compute the base address. */
+ base = state->Reg[addr_reg];
+
+ /* Compute the offset. */
+ offset = immediate ? ((BITS (8, 11) << 4) | BITS (0, 3)) : state->
+ Reg[RHSReg];
+
+ /* Compute the sum of the two. */
+ if (add_to_base)
+ sum = base + offset;
+ else
+ sum = base - offset;
+
+ /* If this is a pre-indexed mode use the sum. */
+ if (pre_indexed)
+ addr = sum;
+ else
+ addr = base;
+
+ /* The address must be aligned on a 8 byte boundary. */
+ /*if (addr & 0x7) {
+ #ifdef ABORTS
+ ARMul_DATAABORT (addr);
+ #else
+ ARMul_UndefInstr (state, instr);
+ #endif
+ return;
+ }*/
+ /* Lets just forcibly align it for now */
+ //addr = (addr + 7) & ~7;
+
+ /* For pre indexed or post indexed addressing modes,
+ check that the destination registers do not overlap
+ the address registers. */
+ if ((!pre_indexed || write_back)
+ && (addr_reg == dest_reg || addr_reg == dest_reg + 1)) {
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+
+ /* Load the words. */
+ value1 = ARMul_LoadWordN (state, addr);
+ value2 = ARMul_LoadWordN (state, addr + 4);
+
+ /* Check for data aborts. */
+ if (state->Aborted) {
+ TAKEABORT;
+ return;
+ }
+
+ ARMul_Icycles (state, 2, 0L);
+
+ /* Store the values. */
+ state->Reg[dest_reg] = value1;
+ state->Reg[dest_reg + 1] = value2;
+
+ /* Do the post addressing and writeback. */
+ if (!pre_indexed)
+ addr = sum;
+
+ if (!pre_indexed || write_back)
+ state->Reg[addr_reg] = addr;
+ }
+
+ /* This function does the work of storing two words for a STRD instruction. */
+
+ static void
+ Handle_Store_Double (ARMul_State * state, ARMword instr) {
+ ARMword src_reg;
+ ARMword addr_reg;
+ ARMword write_back = BIT (21);
+ ARMword immediate = BIT (22);
+ ARMword add_to_base = BIT (23);
+ ARMword pre_indexed = BIT (24);
+ ARMword offset;
+ ARMword addr;
+ ARMword sum;
+ ARMword base;
+
+ BUSUSEDINCPCS;
+
+ /* If the writeback bit is set, the pre-index bit must be clear. */
+ if (write_back && !pre_indexed) {
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+
+ /* Extract the base address register. */
+ addr_reg = LHSReg;
+
+ /* Base register cannot be PC. */
+ if (addr_reg == 15) {
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+
+ /* Extract the source register. */
+ src_reg = DESTReg;
+
+ /* Source register must be even. */
+ if (src_reg & 1) {
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+
+ /* Compute the base address. */
+ base = state->Reg[addr_reg];
+
+ /* Compute the offset. */
+ offset = immediate ? ((BITS (8, 11) << 4) | BITS (0, 3)) : state->
+ Reg[RHSReg];
+
+ /* Compute the sum of the two. */
+ if (add_to_base)
+ sum = base + offset;
+ else
+ sum = base - offset;
+
+ /* If this is a pre-indexed mode use the sum. */
+ if (pre_indexed)
+ addr = sum;
+ else
+ addr = base;
+
+ /* The address must be aligned on a 8 byte boundary. */
+ /*if (addr & 0x7) {
+ #ifdef ABORTS
+ ARMul_DATAABORT (addr);
+ #else
+ ARMul_UndefInstr (state, instr);
+ #endif
+ return;
+ }*/
+ /* Lets just forcibly align it for now */
+ //addr = (addr + 7) & ~7;
+
+ /* For pre indexed or post indexed addressing modes,
+ check that the destination registers do not overlap
+ the address registers. */
+ if ((!pre_indexed || write_back)
+ && (addr_reg == src_reg || addr_reg == src_reg + 1)) {
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+
+ /* Load the words. */
+ ARMul_StoreWordN (state, addr, state->Reg[src_reg]);
+ ARMul_StoreWordN (state, addr + 4, state->Reg[src_reg + 1]);
+
+ if (state->Aborted) {
+ TAKEABORT;
+ return;
+ }
+
+ /* Do the post addressing and writeback. */
+ if (!pre_indexed)
+ addr = sum;
+
+ if (!pre_indexed || write_back)
+ state->Reg[addr_reg] = addr;
+ }
+
+ /* This function does the work of storing a word from a STR instruction. */
+
+ static unsigned
+ StoreWord (ARMul_State * state, ARMword instr, ARMword address) {
+ //MEM_STORE_LOG("WORD");
+
+ BUSUSEDINCPCN;
+#ifndef MODE32
+ if (DESTReg == 15)
+ state->Reg[15] = ECC | ER15INT | R15PC | EMODE;
+#endif
+#ifdef MODE32
+ ARMul_StoreWordN (state, address, DEST);
+#else
+ if (VECTORACCESS (address) || ADDREXCEPT (address)) {
+ INTERNALABORT (address);
+ (void) ARMul_LoadWordN (state, address);
+ } else
+ ARMul_StoreWordN (state, address, DEST);
+#endif
+ if (state->Aborted) {
+ TAKEABORT;
+ return state->lateabtSig;
+ }
+
+ return TRUE;
+ }
+
+#ifdef MODET
+ /* This function does the work of storing a byte for a STRH instruction. */
+
+ static unsigned
+ StoreHalfWord (ARMul_State * state, ARMword instr, ARMword address) {
+ //MEM_STORE_LOG("HALFWORD");
+
+ BUSUSEDINCPCN;
+
+#ifndef MODE32
+ if (DESTReg == 15)
+ state->Reg[15] = ECC | ER15INT | R15PC | EMODE;
+#endif
+
+#ifdef MODE32
+ ARMul_StoreHalfWord (state, address, DEST);
+#else
+ if (VECTORACCESS (address) || ADDREXCEPT (address)) {
+ INTERNALABORT (address);
+ (void) ARMul_LoadHalfWord (state, address);
+ } else
+ ARMul_StoreHalfWord (state, address, DEST);
+#endif
+
+ if (state->Aborted) {
+ TAKEABORT;
+ return state->lateabtSig;
+ }
+ return TRUE;
+ }
+
+#endif /* MODET */
+
+ /* This function does the work of storing a byte for a STRB instruction. */
+
+ static unsigned
+ StoreByte (ARMul_State * state, ARMword instr, ARMword address) {
+ //MEM_STORE_LOG("BYTE");
+
+ BUSUSEDINCPCN;
+#ifndef MODE32
+ if (DESTReg == 15)
+ state->Reg[15] = ECC | ER15INT | R15PC | EMODE;
+#endif
+#ifdef MODE32
+ ARMul_StoreByte (state, address, DEST);
+#else
+ if (VECTORACCESS (address) || ADDREXCEPT (address)) {
+ INTERNALABORT (address);
+ (void) ARMul_LoadByte (state, address);
+ } else
+ ARMul_StoreByte (state, address, DEST);
+#endif
+ if (state->Aborted) {
+ TAKEABORT;
+ return state->lateabtSig;
+ }
+ //UNDEF_LSRBPC;
+ return TRUE;
+ }
+
+ /* This function does the work of loading the registers listed in an LDM
+ instruction, when the S bit is clear. The code here is always increment
+ after, it's up to the caller to get the input address correct and to
+ handle base register modification. */
+
+ static void
+ LoadMult (ARMul_State * state, ARMword instr, ARMword address, ARMword WBBase) {
+ ARMword dest, temp;
+
+ //UNDEF_LSMNoRegs;
+ //UNDEF_LSMPCBase;
+ //UNDEF_LSMBaseInListWb;
+ BUSUSEDINCPCS;
+#ifndef MODE32
+ if (ADDREXCEPT (address))
+ INTERNALABORT (address);
+#endif
+ /*chy 2004-05-23 may write twice
+ if (BIT (21) && LHSReg != 15)
+ LSBase = WBBase;
+ */
+ /* N cycle first. */
+ for (temp = 0; !BIT (temp); temp++);
+
+ dest = ARMul_LoadWordN (state, address);
+
+ if (!state->abortSig && !state->Aborted)
+ state->Reg[temp++] = dest;
+ else if (!state->Aborted) {
+ //XScale_set_fsr_far (state, ARMul_CP15_R5_ST_ALIGN, address);
+ state->Aborted = ARMul_DataAbortV;
+ }
+ /*chy 2004-05-23 chy goto end*/
+ if (state->Aborted)
+ goto L_ldm_makeabort;
+ /* S cycles from here on. */
+ for (; temp < 16; temp++)
+ if (BIT (temp)) {
+ /* Load this register. */
+ address += 4;
+ dest = ARMul_LoadWordS (state, address);
+
+ if (!state->abortSig && !state->Aborted)
+ state->Reg[temp] = dest;
+ else if (!state->Aborted) {
+ /*XScale_set_fsr_far (state,
+ ARMul_CP15_R5_ST_ALIGN,
+ address);*/
+ state->Aborted = ARMul_DataAbortV;
+ }
+ /*chy 2004-05-23 chy goto end */
+ if (state->Aborted)
+ goto L_ldm_makeabort;
+ }
+
+ if (BIT (15) && !state->Aborted)
+ /* PC is in the reg list. */
+ WriteR15Branch (state, PC);
+
+ /* To write back the final register. */
+ /* ARMul_Icycles (state, 1, 0L);*/
+ /*chy 2004-05-23, see below
+ if (state->Aborted)
+ {
+ if (BIT (21) && LHSReg != 15)
+ LSBase = WBBase;
+
+ TAKEABORT;
+ }
+ */
+ /*chy 2004-05-23 should compare the Abort Models*/
+L_ldm_makeabort:
+ /* To write back the final register. */
+ ARMul_Icycles (state, 1, 0L);
+
+ /* chy 2005-11-24, bug found by benjl@cse.unsw.edu.au, etc */
+ /*
+ if (state->Aborted)
+ {
+ if (BIT (21) && LHSReg != 15)
+ if (!(state->abortSig && state->Aborted && state->lateabtSig == LOW))
+ LSBase = WBBase;
+ TAKEABORT;
+ }else if (BIT (21) && LHSReg != 15)
+ LSBase = WBBase;
+ */
+ if (state->Aborted) {
+ if (BIT (21) && LHSReg != 15) {
+ if (!(state->abortSig)) {
+ }
+ }
+ TAKEABORT;
+ } else if (BIT (21) && LHSReg != 15) {
+ LSBase = WBBase;
+ }
+ /* chy 2005-11-24, over */
+ }
+
+ /* This function does the work of loading the registers listed in an LDM
+ instruction, when the S bit is set. The code here is always increment
+ after, it's up to the caller to get the input address correct and to
+ handle base register modification. */
+
+ static void
+ LoadSMult (ARMul_State * state,
+ ARMword instr, ARMword address, ARMword WBBase) {
+ ARMword dest, temp;
+
+ //UNDEF_LSMNoRegs;
+ //UNDEF_LSMPCBase;
+ //UNDEF_LSMBaseInListWb;
+
+ BUSUSEDINCPCS;
+
+#ifndef MODE32
+ if (ADDREXCEPT (address))
+ INTERNALABORT (address);
+#endif
+ /* chy 2004-05-23, may write twice
+ if (BIT (21) && LHSReg != 15)
+ LSBase = WBBase;
+ */
+ if (!BIT (15) && state->Bank != USERBANK) {
+ /* Temporary reg bank switch. */
+ (void) ARMul_SwitchMode (state, state->Mode, USER26MODE);
+ UNDEF_LSMUserBankWb;
+ }
+
+ /* N cycle first. */
+ for (temp = 0; !BIT (temp); temp++);
+
+ dest = ARMul_LoadWordN (state, address);
+
+ if (!state->abortSig)
+ state->Reg[temp++] = dest;
+ else if (!state->Aborted) {
+ //XScale_set_fsr_far (state, ARMul_CP15_R5_ST_ALIGN, address);
+ state->Aborted = ARMul_DataAbortV;
+ }
+
+ /*chy 2004-05-23 chy goto end*/
+ if (state->Aborted)
+ goto L_ldm_s_makeabort;
+ /* S cycles from here on. */
+ for (; temp < 16; temp++)
+ if (BIT (temp)) {
+ /* Load this register. */
+ address += 4;
+ dest = ARMul_LoadWordS (state, address);
+
+ if (!state->abortSig && !state->Aborted)
+ state->Reg[temp] = dest;
+ else if (!state->Aborted) {
+ /*XScale_set_fsr_far (state,
+ ARMul_CP15_R5_ST_ALIGN,
+ address);*/
+ state->Aborted = ARMul_DataAbortV;
+ }
+ /*chy 2004-05-23 chy goto end */
+ if (state->Aborted)
+ goto L_ldm_s_makeabort;
+ }
+
+ /*chy 2004-05-23 label of ldm_s_makeabort*/
+L_ldm_s_makeabort:
+ /*chy 2004-06-06 LSBase process should be here, not in the end of this function. Because ARMul_CPSRAltered maybe change R13(SP) R14(lr). If not, simulate INSTR ldmia sp!,[....pc]^ error.*/
+ /*chy 2004-05-23 should compare the Abort Models*/
+ if (state->Aborted) {
+ if (BIT (21) && LHSReg != 15)
+ if (!
+ (state->abortSig && state->Aborted
+ && state->lateabtSig == LOW))
+ LSBase = WBBase;
+ TAKEABORT;
+ } else if (BIT (21) && LHSReg != 15)
+ LSBase = WBBase;
+
+ if (BIT (15) && !state->Aborted) {
+ /* PC is in the reg list. */
+#ifdef MODE32
+ //chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
+ if (state->Mode != USER26MODE && state->Mode != USER32MODE ) {
+ state->Cpsr = GETSPSR (state->Bank);
+ ARMul_CPSRAltered (state);
+ }
+
+ WriteR15 (state, PC);
+#else
+ //chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
+ if (state->Mode == USER26MODE || state->Mode == USER32MODE ) {
+ /* Protect bits in user mode. */
+ ASSIGNN ((state->Reg[15] & NBIT) != 0);
+ ASSIGNZ ((state->Reg[15] & ZBIT) != 0);
+ ASSIGNC ((state->Reg[15] & CBIT) != 0);
+ ASSIGNV ((state->Reg[15] & VBIT) != 0);
+ } else
+ ARMul_R15Altered (state);
+
+ FLUSHPIPE;
+#endif
+ }
+
+ //chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
+ if (!BIT (15) && state->Mode != USER26MODE
+ && state->Mode != USER32MODE )
+ /* Restore the correct bank. */
+ (void) ARMul_SwitchMode (state, USER26MODE, state->Mode);
+
+ /* To write back the final register. */
+ ARMul_Icycles (state, 1, 0L);
+ /* chy 2004-05-23, see below
+ if (state->Aborted)
+ {
+ if (BIT (21) && LHSReg != 15)
+ LSBase = WBBase;
+
+ TAKEABORT;
+ }
+ */
+ }
+
+ /* This function does the work of storing the registers listed in an STM
+ instruction, when the S bit is clear. The code here is always increment
+ after, it's up to the caller to get the input address correct and to
+ handle base register modification. */
+
+ static void
+ StoreMult (ARMul_State * state,
+ ARMword instr, ARMword address, ARMword WBBase) {
+ ARMword temp;
+
+ UNDEF_LSMNoRegs;
+ UNDEF_LSMPCBase;
+ UNDEF_LSMBaseInListWb;
+
+ if (!TFLAG)
+ /* N-cycle, increment the PC and update the NextInstr state. */
+ BUSUSEDINCPCN;
+
+#ifndef MODE32
+ if (VECTORACCESS (address) || ADDREXCEPT (address))
+ INTERNALABORT (address);
+
+ if (BIT (15))
+ PATCHR15;
+#endif
+
+ /* N cycle first. */
+ for (temp = 0; !BIT (temp); temp++);
+
+#ifdef MODE32
+ ARMul_StoreWordN (state, address, state->Reg[temp++]);
+#else
+ if (state->Aborted) {
+ (void) ARMul_LoadWordN (state, address);
+
+ /* Fake the Stores as Loads. */
+ for (; temp < 16; temp++)
+ if (BIT (temp)) {
+ /* Save this register. */
+ address += 4;
+ (void) ARMul_LoadWordS (state, address);
+ }
+
+ if (BIT (21) && LHSReg != 15)
+ LSBase = WBBase;
+ TAKEABORT;
+ return;
+ } else
+ ARMul_StoreWordN (state, address, state->Reg[temp++]);
+#endif
+
+ if (state->abortSig && !state->Aborted) {
+ //XScale_set_fsr_far (state, ARMul_CP15_R5_ST_ALIGN, address);
+ state->Aborted = ARMul_DataAbortV;
+ }
+
+//chy 2004-05-23, needn't store other when aborted
+ if (state->Aborted)
+ goto L_stm_takeabort;
+
+ /* S cycles from here on. */
+ for (; temp < 16; temp++)
+ if (BIT (temp)) {
+ /* Save this register. */
+ address += 4;
+
+ ARMul_StoreWordS (state, address, state->Reg[temp]);
+
+ if (state->abortSig && !state->Aborted) {
+ /*XScale_set_fsr_far (state,
+ ARMul_CP15_R5_ST_ALIGN,
+ address);*/
+ state->Aborted = ARMul_DataAbortV;
+ }
+ //chy 2004-05-23, needn't store other when aborted
+ if (state->Aborted)
+ goto L_stm_takeabort;
+ }
+
+//chy 2004-05-23,should compare the Abort Models
+L_stm_takeabort:
+ if (BIT (21) && LHSReg != 15) {
+ if (!
+ (state->abortSig && state->Aborted
+ && state->lateabtSig == LOW))
+ LSBase = WBBase;
+ }
+ if (state->Aborted)
+ TAKEABORT;
+ }
+
+ /* This function does the work of storing the registers listed in an STM
+ instruction when the S bit is set. The code here is always increment
+ after, it's up to the caller to get the input address correct and to
+ handle base register modification. */
+
+ static void
+ StoreSMult (ARMul_State * state,
+ ARMword instr, ARMword address, ARMword WBBase) {
+ ARMword temp;
+
+ UNDEF_LSMNoRegs;
+ UNDEF_LSMPCBase;
+ UNDEF_LSMBaseInListWb;
+
+ BUSUSEDINCPCN;
+
+#ifndef MODE32
+ if (VECTORACCESS (address) || ADDREXCEPT (address))
+ INTERNALABORT (address);
+
+ if (BIT (15))
+ PATCHR15;
+#endif
+
+ if (state->Bank != USERBANK) {
+ /* Force User Bank. */
+ (void) ARMul_SwitchMode (state, state->Mode, USER26MODE);
+ UNDEF_LSMUserBankWb;
+ }
+
+ for (temp = 0; !BIT (temp); temp++); /* N cycle first. */
+
+#ifdef MODE32
+ ARMul_StoreWordN (state, address, state->Reg[temp++]);
+#else
+ if (state->Aborted) {
+ (void) ARMul_LoadWordN (state, address);
+
+ for (; temp < 16; temp++)
+ /* Fake the Stores as Loads. */
+ if (BIT (temp)) {
+ /* Save this register. */
+ address += 4;
+
+ (void) ARMul_LoadWordS (state, address);
+ }
+
+ if (BIT (21) && LHSReg != 15)
+ LSBase = WBBase;
+
+ TAKEABORT;
+ return;
+ } else
+ ARMul_StoreWordN (state, address, state->Reg[temp++]);
+#endif
+
+ if (state->abortSig && !state->Aborted) {
+ //XScale_set_fsr_far (state, ARMul_CP15_R5_ST_ALIGN, address);
+ state->Aborted = ARMul_DataAbortV;
+ }
+
+//chy 2004-05-23, needn't store other when aborted
+ if (state->Aborted)
+ goto L_stm_s_takeabort;
+ /* S cycles from here on. */
+ for (; temp < 16; temp++)
+ if (BIT (temp)) {
+ /* Save this register. */
+ address += 4;
+
+ ARMul_StoreWordS (state, address, state->Reg[temp]);
+
+ if (state->abortSig && !state->Aborted) {
+ /*XScale_set_fsr_far (state,
+ ARMul_CP15_R5_ST_ALIGN,
+ address);*/
+ state->Aborted = ARMul_DataAbortV;
+ }
+ //chy 2004-05-23, needn't store other when aborted
+ if (state->Aborted)
+ goto L_stm_s_takeabort;
+ }
+
+ //chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
+ if (state->Mode != USER26MODE && state->Mode != USER32MODE )
+ /* Restore the correct bank. */
+ (void) ARMul_SwitchMode (state, USER26MODE, state->Mode);
+
+//chy 2004-05-23,should compare the Abort Models
+L_stm_s_takeabort:
+ if (BIT (21) && LHSReg != 15) {
+ if (!
+ (state->abortSig && state->Aborted
+ && state->lateabtSig == LOW))
+ LSBase = WBBase;
+ }
+
+ if (state->Aborted)
+ TAKEABORT;
+ }
+
+ /* This function does the work of adding two 32bit values
+ together, and calculating if a carry has occurred. */
+
+ static ARMword
+ Add32 (ARMword a1, ARMword a2, int *carry) {
+ ARMword result = (a1 + a2);
+ unsigned int uresult = (unsigned int) result;
+ unsigned int ua1 = (unsigned int) a1;
+
+ /* If (result == RdLo) and (state->Reg[nRdLo] == 0),
+ or (result > RdLo) then we have no carry. */
+ if ((uresult == ua1) ? (a2 != 0) : (uresult < ua1))
+ *carry = 1;
+ else
+ *carry = 0;
+
+ return result;
+ }
+
+ /* This function does the work of multiplying
+ two 32bit values to give a 64bit result. */
+
+ static unsigned
+ Multiply64 (ARMul_State * state, ARMword instr, int msigned, int scc) {
+ /* Operand register numbers. */
+ int nRdHi, nRdLo, nRs, nRm;
+ ARMword RdHi = 0, RdLo = 0, Rm;
+ /* Cycle count. */
+ int scount;
+
+ nRdHi = BITS (16, 19);
+ nRdLo = BITS (12, 15);
+ nRs = BITS (8, 11);
+ nRm = BITS (0, 3);
+
+ /* Needed to calculate the cycle count. */
+ Rm = state->Reg[nRm];
+
+ /* Check for illegal operand combinations first. */
+ if (nRdHi != 15
+ && nRdLo != 15
+ && nRs != 15
+ //&& nRm != 15 && nRdHi != nRdLo && nRdHi != nRm && nRdLo != nRm) {
+ && nRm != 15 && nRdHi != nRdLo ) {
+ /* Intermediate results. */
+ ARMword lo, mid1, mid2, hi;
+ int carry;
+ ARMword Rs = state->Reg[nRs];
+ int sign = 0;
+
+ if (msigned) {
+ /* Compute sign of result and adjust operands if necessary. */
+ sign = (Rm ^ Rs) & 0x80000000;
+
+ if (((signed int) Rm) < 0)
+ Rm = -Rm;
+
+ if (((signed int) Rs) < 0)
+ Rs = -Rs;
+ }
+
+ /* We can split the 32x32 into four 16x16 operations. This
+ ensures that we do not lose precision on 32bit only hosts. */
+ lo = ((Rs & 0xFFFF) * (Rm & 0xFFFF));
+ mid1 = ((Rs & 0xFFFF) * ((Rm >> 16) & 0xFFFF));
+ mid2 = (((Rs >> 16) & 0xFFFF) * (Rm & 0xFFFF));
+ hi = (((Rs >> 16) & 0xFFFF) * ((Rm >> 16) & 0xFFFF));
+
+ /* We now need to add all of these results together, taking
+ care to propogate the carries from the additions. */
+ RdLo = Add32 (lo, (mid1 << 16), &carry);
+ RdHi = carry;
+ RdLo = Add32 (RdLo, (mid2 << 16), &carry);
+ RdHi += (carry + ((mid1 >> 16) & 0xFFFF) +
+ ((mid2 >> 16) & 0xFFFF) + hi);
+
+ if (sign) {
+ /* Negate result if necessary. */
+ RdLo = ~RdLo;
+ RdHi = ~RdHi;
+ if (RdLo == 0xFFFFFFFF) {
+ RdLo = 0;
+ RdHi += 1;
+ } else
+ RdLo += 1;
+ }
+
+ state->Reg[nRdLo] = RdLo;
+ state->Reg[nRdHi] = RdHi;
+ } else {
+ fprintf (stderr, "sim: MULTIPLY64 - INVALID ARGUMENTS, instr=0x%x\n", instr);
+ }
+ if (scc)
+ /* Ensure that both RdHi and RdLo are used to compute Z,
+ but don't let RdLo's sign bit make it to N. */
+ ARMul_NegZero (state, RdHi | (RdLo >> 16) | (RdLo & 0xFFFF));
+
+ /* The cycle count depends on whether the instruction is a signed or
+ unsigned multiply, and what bits are clear in the multiplier. */
+ if (msigned && (Rm & ((unsigned) 1 << 31)))
+ /* Invert the bits to make the check against zero. */
+ Rm = ~Rm;
+
+ if ((Rm & 0xFFFFFF00) == 0)
+ scount = 1;
+ else if ((Rm & 0xFFFF0000) == 0)
+ scount = 2;
+ else if ((Rm & 0xFF000000) == 0)
+ scount = 3;
+ else
+ scount = 4;
+
+ return 2 + scount;
+ }
+
+ /* This function does the work of multiplying two 32bit
+ values and adding a 64bit value to give a 64bit result. */
+
+ static unsigned
+ MultiplyAdd64 (ARMul_State * state, ARMword instr, int msigned, int scc) {
+ unsigned scount;
+ ARMword RdLo, RdHi;
+ int nRdHi, nRdLo;
+ int carry = 0;
+
+ nRdHi = BITS (16, 19);
+ nRdLo = BITS (12, 15);
+
+ RdHi = state->Reg[nRdHi];
+ RdLo = state->Reg[nRdLo];
+
+ scount = Multiply64 (state, instr, msigned, LDEFAULT);
+
+ RdLo = Add32 (RdLo, state->Reg[nRdLo], &carry);
+ RdHi = (RdHi + state->Reg[nRdHi]) + carry;
+
+ state->Reg[nRdLo] = RdLo;
+ state->Reg[nRdHi] = RdHi;
+
+ if (scc)
+ /* Ensure that both RdHi and RdLo are used to compute Z,
+ but don't let RdLo's sign bit make it to N. */
+ ARMul_NegZero (state, RdHi | (RdLo >> 16) | (RdLo & 0xFFFF));
+
+ /* Extra cycle for addition. */
+ return scount + 1;
+ }
+
+ /* Attempt to emulate an ARMv6 instruction.
+ Returns non-zero upon success. */
+
+ static int handle_v6_insn(ARMul_State* state, ARMword instr) {
+ switch (BITS(20, 27)) {
+ case 0x03:
+ printf ("Unhandled v6 insn: ldr\n");
+ break;
+ case 0x04: // UMAAL
+ {
+ const u8 rm_idx = BITS(8, 11);
+ const u8 rn_idx = BITS(0, 3);
+ const u8 rd_lo_idx = BITS(12, 15);
+ const u8 rd_hi_idx = BITS(16, 19);
+
+ const u32 rm_val = state->Reg[rm_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+ const u32 rd_lo_val = state->Reg[rd_lo_idx];
+ const u32 rd_hi_val = state->Reg[rd_hi_idx];
+
+ const u64 result = (rn_val * rm_val) + rd_lo_val + rd_hi_val;
+
+ state->Reg[rd_lo_idx] = (result & 0xFFFFFFFF);
+ state->Reg[rd_hi_idx] = ((result >> 32) & 0xFFFFFFFF);
+ return 1;
+ }
+ break;
+ case 0x06:
+ printf ("Unhandled v6 insn: mls/str\n");
+ break;
+ case 0x16:
+ printf ("Unhandled v6 insn: smi\n");
+ break;
+ case 0x18:
+ if (BITS(4, 7) == 0x9) {
+ /* strex */
+ u32 l = LHSReg;
+ u32 r = RHSReg;
+ u32 lhs = LHS;
+
+ bool enter = false;
+
+ if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr))enter = true;
+ //StoreWord(state, lhs, RHS)
+ if (state->Aborted) {
+ TAKEABORT;
+ }
+
+ if (enter) {
+ ARMul_StoreWordS(state, lhs, RHS);
+ state->Reg[DESTReg] = 0;
+ }
+ else {
+ state->Reg[DESTReg] = 1;
+ }
+
+ return 1;
+ }
+ printf ("Unhandled v6 insn: strex\n");
+ break;
+ case 0x19:
+ /* ldrex */
+ if (BITS(4, 7) == 0x9) {
+ u32 lhs = LHS;
+
+ state->currentexaddr = lhs;
+ state->currentexval = ARMul_ReadWord(state, lhs);
+
+ LoadWord(state, instr, lhs);
+ return 1;
+ }
+ printf ("Unhandled v6 insn: ldrex\n");
+ break;
+ case 0x1a:
+ printf ("Unhandled v6 insn: strexd\n");
+ break;
+ case 0x1b:
+ printf ("Unhandled v6 insn: ldrexd\n");
+ break;
+ case 0x1c:
+ if (BITS(4, 7) == 0x9) {
+ /* strexb */
+ u32 lhs = LHS;
+
+ bool enter = false;
+
+ if (state->currentexval == (u32)ARMul_ReadByte(state, state->currentexaddr))enter = true;
+
+ BUSUSEDINCPCN;
+ if (state->Aborted) {
+ TAKEABORT;
+ }
+
+ if (enter) {
+ ARMul_StoreByte(state, lhs, RHS);
+ state->Reg[DESTReg] = 0;
+ }
+ else {
+ state->Reg[DESTReg] = 1;
+ }
+
+ //printf("In %s, strexb not implemented\n", __FUNCTION__);
+ UNDEF_LSRBPC;
+ /* WRITESDEST (dest); */
+ return 1;
+ }
+ printf ("Unhandled v6 insn: strexb\n");
+ break;
+ case 0x1d:
+ if ((BITS(4, 7)) == 0x9) {
+ /* ldrexb */
+ u32 lhs = LHS;
+ LoadByte(state, instr, lhs, LUNSIGNED);
+
+ state->currentexaddr = lhs;
+ state->currentexval = (u32)ARMul_ReadByte(state, lhs);
+
+ //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]);
+ //printf("ldrexb\n");
+ //printf("instr is %x rm is %d\n", instr, BITS(16, 19));
+ //exit(-1);
+
+ //printf("In %s, ldrexb not implemented\n", __FUNCTION__);
+ return 1;
+ }
+ printf ("Unhandled v6 insn: ldrexb\n");
+ break;
+ case 0x1e:
+ printf ("Unhandled v6 insn: strexh\n");
+ break;
+ case 0x1f:
+ printf ("Unhandled v6 insn: ldrexh\n");
+ break;
+ case 0x30:
+ printf ("Unhandled v6 insn: movw\n");
+ break;
+ case 0x32:
+ printf ("Unhandled v6 insn: nop/sev/wfe/wfi/yield\n");
+ break;
+ case 0x34:
+ printf ("Unhandled v6 insn: movt\n");
+ break;
+ case 0x3f:
+ printf ("Unhandled v6 insn: rbit\n");
+ break;
+ case 0x61: // SADD16, SASX, SSAX, and SSUB16
+ if ((instr & 0xFF0) == 0xf10 || (instr & 0xFF0) == 0xf30 ||
+ (instr & 0xFF0) == 0xf50 || (instr & 0xFF0) == 0xf70)
+ {
+ const u8 rd_idx = BITS(12, 15);
+ const u8 rm_idx = BITS(0, 3);
+ const u8 rn_idx = BITS(16, 19);
+ const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF);
+ const s16 rn_hi = ((state->Reg[rn_idx] >> 16) & 0xFFFF);
+ const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF);
+ const s16 rm_hi = ((state->Reg[rm_idx] >> 16) & 0xFFFF);
+
+ s32 lo_result;
+ s32 hi_result;
+
+ // SADD16
+ if ((instr & 0xFF0) == 0xf10) {
+ lo_result = (rn_lo + rm_lo);
+ hi_result = (rn_hi + rm_hi);
+ }
+ // SASX
+ else if ((instr & 0xFF0) == 0xf30) {
+ lo_result = (rn_lo - rm_hi);
+ hi_result = (rn_hi + rm_lo);
+ }
+ // SSAX
+ else if ((instr & 0xFF0) == 0xf50) {
+ lo_result = (rn_lo + rm_hi);
+ hi_result = (rn_hi - rm_lo);
+ }
+ // SSUB16
+ else {
+ lo_result = (rn_lo - rm_lo);
+ hi_result = (rn_hi - rm_hi);
+ }
+
+ state->Reg[rd_idx] = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
+
+ if (lo_result >= 0) {
+ state->GEFlag |= (1 << 16);
+ state->GEFlag |= (1 << 17);
+ } else {
+ state->GEFlag &= ~(1 << 16);
+ state->GEFlag &= ~(1 << 17);
+ }
+
+ if (hi_result >= 0) {
+ state->GEFlag |= (1 << 18);
+ state->GEFlag |= (1 << 19);
+ } else {
+ state->GEFlag &= ~(1 << 18);
+ state->GEFlag &= ~(1 << 19);
+ }
+
+ return 1;
+ }
+ // SADD8/SSUB8
+ else if ((instr & 0xFF0) == 0xf90 || (instr & 0xFF0) == 0xff0)
+ {
+ const u8 rd_idx = BITS(12, 15);
+ const u8 rm_idx = BITS(0, 3);
+ const u8 rn_idx = BITS(16, 19);
+ const u32 rm_val = state->Reg[rm_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+
+ s32 lo_val1, lo_val2;
+ s32 hi_val1, hi_val2;
+
+ // SADD8
+ if ((instr & 0xFF0) == 0xf90) {
+ lo_val1 = (s32)(s8)(rn_val & 0xFF) + (s32)(s8)(rm_val & 0xFF);
+ lo_val2 = (s32)(s8)((rn_val >> 8) & 0xFF) + (s32)(s8)((rm_val >> 8) & 0xFF);
+ hi_val1 = (s32)(s8)((rn_val >> 16) & 0xFF) + (s32)(s8)((rm_val >> 16) & 0xFF);
+ hi_val2 = (s32)(s8)((rn_val >> 24) & 0xFF) + (s32)(s8)((rm_val >> 24) & 0xFF);
+ }
+ // SSUB8
+ else {
+ lo_val1 = (s32)(s8)(rn_val & 0xFF) - (s32)(s8)(rm_val & 0xFF);
+ lo_val2 = (s32)(s8)((rn_val >> 8) & 0xFF) - (s32)(s8)((rm_val >> 8) & 0xFF);
+ hi_val1 = (s32)(s8)((rn_val >> 16) & 0xFF) - (s32)(s8)((rm_val >> 16) & 0xFF);
+ hi_val2 = (s32)(s8)((rn_val >> 24) & 0xFF) - (s32)(s8)((rm_val >> 24) & 0xFF);
+ }
+
+ if (lo_val1 >= 0)
+ state->GEFlag |= (1 << 16);
+ else
+ state->GEFlag &= ~(1 << 16);
+
+ if (lo_val2 >= 0)
+ state->GEFlag |= (1 << 17);
+ else
+ state->GEFlag &= ~(1 << 17);
+
+ if (hi_val1 >= 0)
+ state->GEFlag |= (1 << 18);
+ else
+ state->GEFlag &= ~(1 << 18);
+
+ if (hi_val2 >= 0)
+ state->GEFlag |= (1 << 19);
+ else
+ state->GEFlag &= ~(1 << 19);
+
+ state->Reg[rd_idx] = ((lo_val1 & 0xFF) | ((lo_val2 & 0xFF) << 8) | ((hi_val1 & 0xFF) << 16) | ((hi_val2 & 0xFF) << 24));
+ return 1;
+ }
+ else {
+ printf("Unhandled v6 insn: %08x", instr);
+ }
+ break;
+ case 0x62: // QADD16, QASX, QSAX, QSUB16, QADD8, and QSUB8
+ {
+ const u8 op2 = BITS(5, 7);
+
+ const u8 rd_idx = BITS(12, 15);
+ const u8 rn_idx = BITS(16, 19);
+ const u8 rm_idx = BITS(0, 3);
+ const u16 rm_lo = (state->Reg[rm_idx] & 0xFFFF);
+ const u16 rm_hi = ((state->Reg[rm_idx] >> 0x10) & 0xFFFF);
+ const u16 rn_lo = (state->Reg[rn_idx] & 0xFFFF);
+ const u16 rn_hi = ((state->Reg[rn_idx] >> 0x10) & 0xFFFF);
+
+ u16 lo_result = 0;
+ u16 hi_result = 0;
+
+ // QADD16
+ if (op2 == 0x00) {
+ lo_result = ARMul_SignedSaturatedAdd16(rn_lo, rm_lo);
+ hi_result = ARMul_SignedSaturatedAdd16(rn_hi, rm_hi);
+ }
+ // QASX
+ else if (op2 == 0x01) {
+ lo_result = ARMul_SignedSaturatedSub16(rn_lo, rm_hi);
+ hi_result = ARMul_SignedSaturatedAdd16(rn_hi, rm_lo);
+ }
+ // QSAX
+ else if (op2 == 0x02) {
+ lo_result = ARMul_SignedSaturatedAdd16(rn_lo, rm_hi);
+ hi_result = ARMul_SignedSaturatedSub16(rn_hi, rm_lo);
+ }
+ // QSUB16
+ else if (op2 == 0x03) {
+ lo_result = ARMul_SignedSaturatedSub16(rn_lo, rm_lo);
+ hi_result = ARMul_SignedSaturatedSub16(rn_hi, rm_hi);
+ }
+ // QADD8
+ else if (op2 == 0x04) {
+ lo_result = ARMul_SignedSaturatedAdd8(rn_lo & 0xFF, rm_lo & 0xFF) |
+ ARMul_SignedSaturatedAdd8(rn_lo >> 8, rm_lo >> 8) << 8;
+ hi_result = ARMul_SignedSaturatedAdd8(rn_hi & 0xFF, rm_hi & 0xFF) |
+ ARMul_SignedSaturatedAdd8(rn_hi >> 8, rm_hi >> 8) << 8;
+ }
+ // QSUB8
+ else if (op2 == 0x07) {
+ lo_result = ARMul_SignedSaturatedSub8(rn_lo & 0xFF, rm_lo & 0xFF) |
+ ARMul_SignedSaturatedSub8(rn_lo >> 8, rm_lo >> 8) << 8;
+ hi_result = ARMul_SignedSaturatedSub8(rn_hi & 0xFF, rm_hi & 0xFF) |
+ ARMul_SignedSaturatedSub8(rn_hi >> 8, rm_hi >> 8) << 8;
+ }
+
+ state->Reg[rd_idx] = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
+ return 1;
+ }
+ break;
+ case 0x63:
+ printf ("Unhandled v6 insn: shadd/shsub\n");
+ break;
+ case 0x65:
+ {
+ u32 rd = (instr >> 12) & 0xF;
+ u32 rn = (instr >> 16) & 0xF;
+ u32 rm = (instr >> 0) & 0xF;
+ u32 from = state->Reg[rn];
+ u32 to = state->Reg[rm];
+
+ if ((instr & 0xFF0) == 0xF10 || (instr & 0xFF0) == 0xF70) { // UADD16/USUB16
+ u32 h1, h2;
+ state->Cpsr &= 0xfff0ffff;
+ if ((instr & 0x0F0) == 0x070) { // USUB16
+ h1 = ((u16)from - (u16)to);
+ h2 = ((u16)(from >> 16) - (u16)(to >> 16));
+
+ if (!(h1 & 0xffff0000))
+ state->GEFlag |= (3 << 16);
+ else
+ state->GEFlag &= ~(3 << 16);
+
+ if (!(h2 & 0xffff0000))
+ state->GEFlag |= (3 << 18);
+ else
+ state->GEFlag &= ~(3 << 18);
+ }
+ else { // UADD16
+ h1 = ((u16)from + (u16)to);
+ h2 = ((u16)(from >> 16) + (u16)(to >> 16));
+
+ if (h1 & 0xffff0000)
+ state->GEFlag |= (3 << 16);
+ else
+ state->GEFlag &= ~(3 << 16);
+
+ if (h2 & 0xffff0000)
+ state->GEFlag |= (3 << 18);
+ else
+ state->GEFlag &= ~(3 << 18);
+ }
+
+ state->Reg[rd] = (u32)((h1 & 0xffff) | ((h2 & 0xffff) << 16));
+ return 1;
+ }
+ else
+ if ((instr & 0xFF0) == 0xF90 || (instr & 0xFF0) == 0xFF0) { // UADD8/USUB8
+ u32 b1, b2, b3, b4;
+ state->Cpsr &= 0xfff0ffff;
+ if ((instr & 0x0F0) == 0x0F0) { // USUB8
+ b1 = ((u8)from - (u8)to);
+ b2 = ((u8)(from >> 8) - (u8)(to >> 8));
+ b3 = ((u8)(from >> 16) - (u8)(to >> 16));
+ b4 = ((u8)(from >> 24) - (u8)(to >> 24));
+
+ if (!(b1 & 0xffffff00))
+ state->GEFlag |= (1 << 16);
+ else
+ state->GEFlag &= ~(1 << 16);
+
+ if (!(b2 & 0xffffff00))
+ state->GEFlag |= (1 << 17);
+ else
+ state->GEFlag &= ~(1 << 17);
+
+ if (!(b3 & 0xffffff00))
+ state->GEFlag |= (1 << 18);
+ else
+ state->GEFlag &= ~(1 << 18);
+
+ if (!(b4 & 0xffffff00))
+ state->GEFlag |= (1 << 19);
+ else
+ state->GEFlag &= ~(1 << 19);
+ }
+ else { // UADD8
+ b1 = ((u8)from + (u8)to);
+ b2 = ((u8)(from >> 8) + (u8)(to >> 8));
+ b3 = ((u8)(from >> 16) + (u8)(to >> 16));
+ b4 = ((u8)(from >> 24) + (u8)(to >> 24));
+
+ if (b1 & 0xffffff00)
+ state->GEFlag |= (1 << 16);
+ else
+ state->GEFlag &= ~(1 << 16);
+
+ if (b2 & 0xffffff00)
+ state->GEFlag |= (1 << 17);
+ else
+ state->GEFlag &= ~(1 << 17);
+
+ if (b3 & 0xffffff00)
+ state->GEFlag |= (1 << 18);
+ else
+ state->GEFlag &= ~(1 << 18);
+
+ if (b4 & 0xffffff00)
+ state->GEFlag |= (1 << 19);
+ else
+ state->GEFlag &= ~(1 << 19);
+ }
+
+ state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24);
+ return 1;
+ }
+ }
+ printf("Unhandled v6 insn: uasx/usax\n");
+ break;
+ case 0x66: // UQADD16, UQASX, UQSAX, UQSUB16, UQADD8, and UQSUB8
+ {
+ const u8 rd_idx = BITS(12, 15);
+ const u8 rm_idx = BITS(0, 3);
+ const u8 rn_idx = BITS(16, 19);
+ const u8 op2 = BITS(5, 7);
+ const u32 rm_val = state->Reg[rm_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+
+ u16 lo_val = 0;
+ u16 hi_val = 0;
+
+ // UQADD16
+ if (op2 == 0x00) {
+ lo_val = ARMul_UnsignedSaturatedAdd16(rn_val & 0xFFFF, rm_val & 0xFFFF);
+ hi_val = ARMul_UnsignedSaturatedAdd16((rn_val >> 16) & 0xFFFF, (rm_val >> 16) & 0xFFFF);
+ }
+ // UQASX
+ else if (op2 == 0x01) {
+ lo_val = ARMul_UnsignedSaturatedSub16(rn_val & 0xFFFF, (rm_val >> 16) & 0xFFFF);
+ hi_val = ARMul_UnsignedSaturatedAdd16((rn_val >> 16) & 0xFFFF, rm_val & 0xFFFF);
+ }
+ // UQSAX
+ else if (op2 == 0x02) {
+ lo_val = ARMul_UnsignedSaturatedAdd16(rn_val & 0xFFFF, (rm_val >> 16) & 0xFFFF);
+ hi_val = ARMul_UnsignedSaturatedSub16((rn_val >> 16) & 0xFFFF, rm_val & 0xFFFF);
+ }
+ // UQSUB16
+ else if (op2 == 0x03) {
+ lo_val = ARMul_UnsignedSaturatedSub16(rn_val & 0xFFFF, rm_val & 0xFFFF);
+ hi_val = ARMul_UnsignedSaturatedSub16((rn_val >> 16) & 0xFFFF, (rm_val >> 16) & 0xFFFF);
+ }
+ // UQADD8
+ else if (op2 == 0x04) {
+ lo_val = ARMul_UnsignedSaturatedAdd8(rn_val, rm_val) |
+ ARMul_UnsignedSaturatedAdd8(rn_val >> 8, rm_val >> 8) << 8;
+ hi_val = ARMul_UnsignedSaturatedAdd8(rn_val >> 16, rm_val >> 16) |
+ ARMul_UnsignedSaturatedAdd8(rn_val >> 24, rm_val >> 24) << 8;
+ }
+ // UQSUB8
+ else {
+ lo_val = ARMul_UnsignedSaturatedSub8(rn_val, rm_val) |
+ ARMul_UnsignedSaturatedSub8(rn_val >> 8, rm_val >> 8) << 8;
+ hi_val = ARMul_UnsignedSaturatedSub8(rn_val >> 16, rm_val >> 16) |
+ ARMul_UnsignedSaturatedSub8(rn_val >> 24, rm_val >> 24) << 8;
+ }
+
+ state->Reg[rd_idx] = ((lo_val & 0xFFFF) | hi_val << 16);
+ return 1;
+ }
+ break;
+ case 0x67: // UHADD16, UHASX, UHSAX, UHSUB16, UHADD8, and UHSUB8.
+ {
+ const u8 op2 = BITS(5, 7);
+
+ const u8 rm_idx = BITS(0, 3);
+ const u8 rn_idx = BITS(16, 19);
+ const u8 rd_idx = BITS(12, 15);
+
+ const u32 rm_val = state->Reg[rm_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+
+ if (op2 == 0x00 || op2 == 0x01 || op2 == 0x02 || op2 == 0x03)
+ {
+ u32 lo_val = 0;
+ u32 hi_val = 0;
+
+ // UHADD16
+ if (op2 == 0x00) {
+ lo_val = (rn_val & 0xFFFF) + (rm_val & 0xFFFF);
+ hi_val = ((rn_val >> 16) & 0xFFFF) + ((rm_val >> 16) & 0xFFFF);
+ }
+ // UHASX
+ else if (op2 == 0x01) {
+ lo_val = (rn_val & 0xFFFF) - ((rm_val >> 16) & 0xFFFF);
+ hi_val = ((rn_val >> 16) & 0xFFFF) + (rm_val & 0xFFFF);
+ }
+ // UHSAX
+ else if (op2 == 0x02) {
+ lo_val = (rn_val & 0xFFFF) + ((rm_val >> 16) & 0xFFFF);
+ hi_val = ((rn_val >> 16) & 0xFFFF) - (rm_val & 0xFFFF);
+ }
+ // UHSUB16
+ else if (op2 == 0x03) {
+ lo_val = (rn_val & 0xFFFF) - (rm_val & 0xFFFF);
+ hi_val = ((rn_val >> 16) & 0xFFFF) - ((rm_val >> 16) & 0xFFFF);
+ }
+
+ lo_val >>= 1;
+ hi_val >>= 1;
+
+ state->Reg[rd_idx] = (lo_val & 0xFFFF) | ((hi_val & 0xFFFF) << 16);
+ return 1;
+ }
+ else if (op2 == 0x04 || op2 == 0x07) {
+ u32 sum1;
+ u32 sum2;
+ u32 sum3;
+ u32 sum4;
+
+ // UHADD8
+ if (op2 == 0x04) {
+ sum1 = (rn_val & 0xFF) + (rm_val & 0xFF);
+ sum2 = ((rn_val >> 8) & 0xFF) + ((rm_val >> 8) & 0xFF);
+ sum3 = ((rn_val >> 16) & 0xFF) + ((rm_val >> 16) & 0xFF);
+ sum4 = ((rn_val >> 24) & 0xFF) + ((rm_val >> 24) & 0xFF);
+ }
+ // UHSUB8
+ else {
+ sum1 = (rn_val & 0xFF) - (rm_val & 0xFF);
+ sum2 = ((rn_val >> 8) & 0xFF) - ((rm_val >> 8) & 0xFF);
+ sum3 = ((rn_val >> 16) & 0xFF) - ((rm_val >> 16) & 0xFF);
+ sum4 = ((rn_val >> 24) & 0xFF) - ((rm_val >> 24) & 0xFF);
+ }
+
+ sum1 >>= 1;
+ sum2 >>= 1;
+ sum3 >>= 1;
+ sum4 >>= 1;
+
+ state->Reg[rd_idx] = (sum1 & 0xFF) | ((sum2 & 0xFF) << 8) | ((sum3 & 0xFF) << 16) | ((sum4 & 0xFF) << 24);
+ return 1;
+ }
+ }
+ break;
+ case 0x68:
+ {
+ u32 rd = (instr >> 12) & 0xF;
+ u32 rn = (instr >> 16) & 0xF;
+ u32 rm = (instr >> 0) & 0xF;
+ u32 from = state->Reg[rn];
+ u32 to = state->Reg[rm];
+ u32 cpsr = ARMul_GetCPSR(state);
+ if ((instr & 0xFF0) == 0xFB0) { // SEL
+ u32 result;
+ if (cpsr & (1 << 16))
+ result = from & 0xff;
+ else
+ result = to & 0xff;
+ if (cpsr & (1 << 17))
+ result |= from & 0x0000ff00;
+ else
+ result |= to & 0x0000ff00;
+ if (cpsr & (1 << 18))
+ result |= from & 0x00ff0000;
+ else
+ result |= to & 0x00ff0000;
+ if (cpsr & (1 << 19))
+ result |= from & 0xff000000;
+ else
+ result |= to & 0xff000000;
+ state->Reg[rd] = result;
+ return 1;
+ }
+ }
+ printf("Unhandled v6 insn: pkh/sxtab/selsxtb\n");
+ break;
+
+ case 0x6a: // SSAT, SSAT16, SXTB, and SXTAB
+ {
+ const u8 op2 = BITS(5, 7);
+
+ // SSAT16
+ if (op2 == 0x01) {
+ const u8 rd_idx = BITS(12, 15);
+ const u8 rn_idx = BITS(0, 3);
+ const u8 num_bits = BITS(16, 19) + 1;
+ const s16 min = -(0x8000 >> (16 - num_bits));
+ const s16 max = (0x7FFF >> (16 - num_bits));
+ s16 rn_lo = (state->Reg[rn_idx]);
+ s16 rn_hi = (state->Reg[rn_idx] >> 16);
+
+ if (rn_lo > max) {
+ rn_lo = max;
+ SETQ;
+ } else if (rn_lo < min) {
+ rn_lo = min;
+ SETQ;
+ }
+
+ if (rn_hi > max) {
+ rn_hi = max;
+ SETQ;
+ } else if (rn_hi < min) {
+ rn_hi = min;
+ SETQ;
+ }
+
+ state->Reg[rd_idx] = (rn_lo & 0xFFFF) | ((rn_hi & 0xFFFF) << 16);
+ return 1;
+ }
+ else if (op2 == 0x03) {
+ const u8 rotation = BITS(10, 11) * 8;
+ u32 rm = ((state->Reg[BITS(0, 3)] >> rotation) & 0xFF) | (((state->Reg[BITS(0, 3)] << (32 - rotation)) & 0xFF) & 0xFF);
+ if (rm & 0x80)
+ rm |= 0xffffff00;
+
+ // SXTB, otherwise SXTAB
+ if (BITS(16, 19) == 0xf)
+ state->Reg[BITS(12, 15)] = rm;
+ else
+ state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + rm;
+
+ return 1;
+ }
+ else {
+ printf("Unimplemented op: SSAT");
+ }
+ }
+ break;
+
+ case 0x6b: // REV, REV16, SXTH, and SXTAH
+ {
+ const u8 op2 = BITS(5, 7);
+
+ // REV
+ if (op2 == 0x01) {
+ DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24);
+ return 1;
+ }
+ // REV16
+ else if (op2 == 0x05) {
+ DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8);
+ return 1;
+ }
+ else if (op2 == 0x03) {
+ const u8 rotate = BITS(10, 11) * 8;
+
+ u32 rm = ((state->Reg[BITS(0, 3)] >> rotate) & 0xFFFF) | (((state->Reg[BITS(0, 3)] << (32 - rotate)) & 0xFFFF) & 0xFFFF);
+ if (rm & 0x8000)
+ rm |= 0xffff0000;
+
+ // SXTH, otherwise SXTAH
+ if (BITS(16, 19) == 15)
+ state->Reg[BITS(12, 15)] = rm;
+ else
+ state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + rm;
+
+ return 1;
+ }
+ }
+ break;
+
+ case 0x6c: // UXTB16 and UXTAB16
+ {
+ const u8 rm_idx = BITS(0, 3);
+ const u8 rn_idx = BITS(16, 19);
+ const u8 rd_idx = BITS(12, 15);
+ const u32 rm_val = state->Reg[rm_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+ const u32 rotation = BITS(10, 11) * 8;
+ const u32 rotated_rm = ((rm_val << (32 - rotation)) | (rm_val >> rotation));
+
+ // UXTB16
+ if ((instr & 0xf03f0) == 0xf0070) {
+ state->Reg[rd_idx] = rotated_rm & 0x00FF00FF;
+ }
+ else { // UXTAB16
+ const u8 lo_rotated = (rotated_rm & 0xFF);
+ const u16 lo_result = (rn_val & 0xFFFF) + (u16)lo_rotated;
+
+ const u8 hi_rotated = (rotated_rm >> 16) & 0xFF;
+ const u16 hi_result = (rn_val >> 16) + (u16)hi_rotated;
+
+ state->Reg[rd_idx] = ((hi_result << 16) | (lo_result & 0xFFFF));
+ }
+
+ return 1;
+ }
+ break;
+ case 0x6e: // USAT, USAT16, UXTB, and UXTAB
+ {
+ const u8 op2 = BITS(5, 7);
+
+ // USAT16
+ if (op2 == 0x01) {
+ const u8 rd_idx = BITS(12, 15);
+ const u8 rn_idx = BITS(0, 3);
+ const u8 num_bits = BITS(16, 19);
+ const s16 max = 0xFFFF >> (16 - num_bits);
+ s16 rn_lo = (state->Reg[rn_idx]);
+ s16 rn_hi = (state->Reg[rn_idx] >> 16);
+
+ if (max < rn_lo) {
+ rn_lo = max;
+ SETQ;
+ } else if (rn_lo < 0) {
+ rn_lo = 0;
+ SETQ;
+ }
+
+ if (max < rn_hi) {
+ rn_hi = max;
+ SETQ;
+ } else if (rn_hi < 0) {
+ rn_hi = 0;
+ SETQ;
+ }
+
+ state->Reg[rd_idx] = (rn_lo & 0xFFFF) | ((rn_hi << 16) & 0xFFFF);
+ return 1;
+ }
+ else if (op2 == 0x03) {
+ const u8 rotate = BITS(10, 11) * 8;
+ const u32 rm = ((state->Reg[BITS(0, 3)] >> rotate) & 0xFF) | (((state->Reg[BITS(0, 3)] << (32 - rotate)) & 0xFF) & 0xFF);
+
+ if (BITS(16, 19) == 0xf)
+ /* UXTB */
+ state->Reg[BITS(12, 15)] = rm;
+ else
+ /* UXTAB */
+ state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + rm;
+
+ return 1;
+ }
+ else {
+ printf("Unimplemented op: USAT");
+ }
+ }
+ break;
+
+ case 0x6f: // UXTH, UXTAH, and REVSH.
+ {
+ const u8 op2 = BITS(5, 7);
+
+ // REVSH
+ if (op2 == 0x05) {
+ DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00) >> 8);
+ if (DEST & 0x8000)
+ DEST |= 0xffff0000;
+ return 1;
+ }
+ // UXTH and UXTAH
+ else if (op2 == 0x03) {
+ const u8 rotate = BITS(10, 11) * 8;
+ const ARMword rm = ((state->Reg[BITS(0, 3)] >> rotate) & 0xFFFF) | (((state->Reg[BITS(0, 3)] << (32 - rotate)) & 0xFFFF) & 0xFFFF);
+
+ // UXTH
+ if (BITS(16, 19) == 0xf) {
+ state->Reg[BITS(12, 15)] = rm;
+ }
+ // UXTAH
+ else {
+ state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + rm;
+ }
+
+ return 1;
+ }
+ }
+ case 0x70:
+ // ichfly
+ // SMUAD, SMUSD, SMLAD, and SMLSD
+ if ((instr & 0xf0d0) == 0xf010 || (instr & 0xf0d0) == 0xf050 ||
+ (instr & 0xd0) == 0x10 || (instr & 0xd0) == 0x50)
+ {
+ const u8 rd_idx = BITS(16, 19);
+ const u8 rn_idx = BITS(0, 3);
+ const u8 rm_idx = BITS(8, 11);
+ const u8 ra_idx = BITS(12, 15);
+ const bool do_swap = (BIT(5) == 1);
+
+ u32 rm_val = state->Reg[rm_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+
+ if (do_swap)
+ rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16));
+
+ const s16 rm_lo = (rm_val & 0xFFFF);
+ const s16 rm_hi = ((rm_val >> 16) & 0xFFFF);
+ const s16 rn_lo = (rn_val & 0xFFFF);
+ const s16 rn_hi = ((rn_val >> 16) & 0xFFFF);
+
+ const u32 product1 = (rn_lo * rm_lo);
+ const u32 product2 = (rn_hi * rm_hi);
+
+ // SMUAD and SMLAD
+ if (BIT(6) == 0) {
+ state->Reg[rd_idx] = product1 + product2;
+
+ if (BITS(12, 15) != 15) {
+ state->Reg[rd_idx] += state->Reg[ra_idx];
+ if (ARMul_AddOverflowQ(product1 + product2, state->Reg[ra_idx]))
+ SETQ;
+ }
+
+ if (ARMul_AddOverflowQ(product1, product2))
+ SETQ;
+ }
+ // SMUSD and SMLSD
+ else {
+ state->Reg[rd_idx] = product1 - product2;
+
+ if (BITS(12, 15) != 15) {
+ state->Reg[rd_idx] += state->Reg[ra_idx];
+
+ if (ARMul_AddOverflowQ(product1 - product2, state->Reg[ra_idx]))
+ SETQ;
+ }
+ }
+
+ return 1;
+ }
+ break;
+ case 0x74: // SMLALD and SMLSLD
+ {
+ const u8 rm_idx = BITS(8, 11);
+ const u8 rn_idx = BITS(0, 3);
+ const u8 rdlo_idx = BITS(12, 15);
+ const u8 rdhi_idx = BITS(16, 19);
+ const bool do_swap = (BIT(5) == 1);
+
+ const u32 rdlo_val = state->Reg[rdlo_idx];
+ const u32 rdhi_val = state->Reg[rdhi_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+ u32 rm_val = state->Reg[rm_idx];
+
+ if (do_swap)
+ rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16));
+
+ const s32 product1 = (s16)(rn_val & 0xFFFF) * (s16)(rm_val & 0xFFFF);
+ const s32 product2 = (s16)((rn_val >> 16) & 0xFFFF) * (s16)((rm_val >> 16) & 0xFFFF);
+ s64 result;
+
+ // SMLALD
+ if (BIT(6) == 0) {
+ result = (product1 + product2) + (s64)(rdlo_val | ((s64)rdhi_val << 32));
+ }
+ // SMLSLD
+ else {
+ result = (product1 - product2) + (s64)(rdlo_val | ((s64)rdhi_val << 32));
+ }
+
+ state->Reg[rdlo_idx] = (result & 0xFFFFFFFF);
+ state->Reg[rdhi_idx] = ((result >> 32) & 0xFFFFFFFF);
+ return 1;
+ }
+ break;
+ case 0x75: // SMMLA, SMMUL, and SMMLS
+ {
+ const u8 rm_idx = BITS(8, 11);
+ const u8 rn_idx = BITS(0, 3);
+ const u8 ra_idx = BITS(12, 15);
+ const u8 rd_idx = BITS(16, 19);
+ const bool do_round = (BIT(5) == 1);
+
+ const u32 rm_val = state->Reg[rm_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+
+ // Assume SMMUL by default.
+ s64 result = (s64)(s32)rn_val * (s64)(s32)rm_val;
+
+ if (ra_idx != 15) {
+ const u32 ra_val = state->Reg[ra_idx];
+
+ // SMMLA, otherwise SMMLS
+ if (BIT(6) == 0)
+ result += ((s64)ra_val << 32);
+ else
+ result = ((s64)ra_val << 32) - result;
+ }
+
+ if (do_round)
+ result += 0x80000000;
+
+ state->Reg[rd_idx] = ((result >> 32) & 0xFFFFFFFF);
+ return 1;
+ }
+ break;
+ case 0x78:
+ if (BITS(20, 24) == 0x18)
+ {
+ const u8 rm_idx = BITS(8, 11);
+ const u8 rn_idx = BITS(0, 3);
+ const u8 rd_idx = BITS(16, 19);
+
+ const u32 rm_val = state->Reg[rm_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+
+ const u8 diff1 = ARMul_UnsignedAbsoluteDifference(rn_val & 0xFF, rm_val & 0xFF);
+ const u8 diff2 = ARMul_UnsignedAbsoluteDifference((rn_val >> 8) & 0xFF, (rm_val >> 8) & 0xFF);
+ const u8 diff3 = ARMul_UnsignedAbsoluteDifference((rn_val >> 16) & 0xFF, (rm_val >> 16) & 0xFF);
+ const u8 diff4 = ARMul_UnsignedAbsoluteDifference((rn_val >> 24) & 0xFF, (rm_val >> 24) & 0xFF);
+
+ u32 finalDif = (diff1 + diff2 + diff3 + diff4);
+
+ // Op is USADA8 if true.
+ const u8 ra_idx = BITS(12, 15);
+ if (ra_idx != 15)
+ finalDif += state->Reg[ra_idx];
+
+ state->Reg[rd_idx] = finalDif;
+ return 1;
+ }
+ break;
+ case 0x7a:
+ printf ("Unhandled v6 insn: usbfx\n");
+ break;
+ case 0x7c:
+ printf ("Unhandled v6 insn: bfc/bfi\n");
+ break;
+ case 0x84:
+ printf ("Unhandled v6 insn: srs\n");
+ break;
+ default:
+ break;
+ }
+ printf("Unhandled v6 insn: UNKNOWN: %08x %08X\n", instr, BITS(20, 27));
+ return 0;
+ }
--- /dev/null
+/* arminit.c -- ARMulator initialization: ARM6 Instruction Emulator.
+ Copyright (C) 1994 Advanced RISC Machines Ltd.
+
+ 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 of the License, 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. */
+
+//#include <unistd.h>
+
+#include "Kernel.h"
+
+#include "arm/skyeye_common/armdefs.h"
+#include "arm/skyeye_common/armemu.h"
+
+/***************************************************************************\
+* Definitions for the emulator architecture *
+\***************************************************************************/
+
+void ARMul_EmulateInit (void);
+ARMul_State *ARMul_NewState (ARMul_State * state);
+void ARMul_Reset (ARMul_State * state);
+ARMword ARMul_DoCycle (ARMul_State * state);
+unsigned ARMul_DoCoPro (ARMul_State * state);
+ARMword ARMul_DoProg (ARMul_State * state);
+ARMword ARMul_DoInstr (ARMul_State * state);
+void ARMul_Abort (ARMul_State * state, ARMword address);
+
+unsigned ARMul_MultTable[32] = {
+ 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
+ 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16
+};
+ARMword ARMul_ImmedTable[4096]; /* immediate DP LHS values */
+char ARMul_BitList[256]; /* number of bits in a byte table */
+
+//chy 2006-02-22 add test debugmode
+extern int debugmode;
+extern int remote_interrupt( void );
+
+
+void arm_dyncom_Abort(ARMul_State * state, ARMword vector)
+{
+ ARMul_Abort(state, vector);
+}
+
+
+/* ahe-ykl : the following code to initialize user mode
+ code is architecture dependent and probably model dependant. */
+
+/*#include "skyeye_arch.h"
+#include "skyeye_pref.h"
+#include "skyeye_exec_info.h"
+#include "bank_defs.h"*/
+//#include "armcpu.h"
+//#include "skyeye_callback.h"
+
+/*
+ ARM_CPU_State* cpu = get_current_cpu();
+ arm_core_t* core = &cpu->core[0];
+
+ uint32_t sp = info->initial_sp;
+
+ core->Cpsr = 0x10; // User mode
+// FIXME: may need to add thumb
+core->Reg[13] = sp;
+core->Reg[10] = info->start_data;
+core->Reg[0] = 0;
+bus_read(32, sp + 4, &(core->Reg[1]));
+bus_read(32, sp + 8, &(core->Reg[2]));
+*/
+/***************************************************************************\
+* Call this routine once to set up the emulator's tables. *
+\***************************************************************************/
+
+void
+ARMul_EmulateInit (void)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < 4096; i++) { /* the values of 12 bit dp rhs's */
+ ARMul_ImmedTable[i] = ROTATER (i & 0xffL, (i >> 7L) & 0x1eL);
+ }
+
+ for (i = 0; i < 256; ARMul_BitList[i++] = 0); /* how many bits in LSM */
+ for (j = 1; j < 256; j <<= 1)
+ for (i = 0; i < 256; i++)
+ if ((i & j) > 0)
+ ARMul_BitList[i]++;
+
+ for (i = 0; i < 256; i++)
+ ARMul_BitList[i] *= 4; /* you always need 4 times these values */
+
+}
+
+/***************************************************************************\
+* Returns a new instantiation of the ARMulator's state *
+\***************************************************************************/
+
+ARMul_State *
+ARMul_NewState (ARMul_State *state)
+{
+ unsigned i, j;
+
+ memset (state, 0, sizeof (ARMul_State));
+
+ state->Emulate = RUN;
+ for (i = 0; i < 16; i++) {
+ state->Reg[i] = 0;
+ for (j = 0; j < 7; j++)
+ state->RegBank[j][i] = 0;
+ }
+ for (i = 0; i < 7; i++)
+ state->Spsr[i] = 0;
+ state->Mode = 0;
+
+ state->CallDebug = FALSE;
+ state->Debug = FALSE;
+ state->VectorCatch = 0;
+ state->Aborted = FALSE;
+ state->Reseted = FALSE;
+ state->Inted = 3;
+ state->LastInted = 3;
+
+ state->CommandLine = NULL;
+
+ state->EventSet = 0;
+ state->Now = 0;
+ state->EventPtr =
+ (struct EventNode **) malloc ((unsigned) EVENTLISTSIZE *
+ sizeof (struct EventNode *));
+#if DIFF_STATE
+ state->state_log = fopen("/data/state.log", "w");
+ printf("create pc log file.\n");
+#endif
+ if (state->EventPtr == NULL) {
+ printf ("SKYEYE: ARMul_NewState malloc state->EventPtr error\n");
+ exit(-1);
+ //skyeye_exit (-1);
+ }
+ for (i = 0; i < EVENTLISTSIZE; i++)
+ *(state->EventPtr + i) = NULL;
+#if SAVE_LOG
+ state->state_log = fopen("/tmp/state.log", "w");
+ printf("create pc log file.\n");
+#else
+#if DIFF_LOG
+ state->state_log = fopen("/tmp/state.log", "r");
+ printf("loaded pc log file.\n");
+#endif
+#endif
+
+#ifdef ARM61
+ state->prog32Sig = LOW;
+ state->data32Sig = LOW;
+#else
+ state->prog32Sig = HIGH;
+ state->data32Sig = HIGH;
+#endif
+
+ state->lateabtSig = HIGH;
+ state->bigendSig = LOW;
+
+ //chy:2003-08-19
+ state->LastTime = 0;
+ state->CP14R0_CCD = -1;
+
+ /* ahe-ykl: common function for interpret and dyncom */
+ /*sky_pref_t *pref = get_skyeye_pref();
+ if (pref->user_mode_sim)
+ register_callback(arm_user_mode_init, Bootmach_callback);
+ */
+
+ memset(&state->exclusive_tag_array[0], 0xFF, sizeof(state->exclusive_tag_array[0]) * 128);
+ state->exclusive_access_state = 0;
+ //state->cpu = (cpu_config_t *) malloc (sizeof (cpu_config_t));
+ //state->mem_bank = (mem_config_t *) malloc (sizeof (mem_config_t));
+ return (state);
+}
+
+/***************************************************************************\
+* Call this routine to set ARMulator to model a certain processor *
+\***************************************************************************/
+
+void
+ARMul_SelectProcessor (ARMul_State * state, unsigned properties)
+{
+ if (properties & ARM_Fix26_Prop) {
+ state->prog32Sig = LOW;
+ state->data32Sig = LOW;
+ } else {
+ state->prog32Sig = HIGH;
+ state->data32Sig = HIGH;
+ }
+ /* 2004-05-09 chy
+ below line sould be in skyeye_mach_XXX.c 's XXX_mach_init function
+ */
+ // state->lateabtSig = HIGH;
+
+
+ state->is_v4 =
+ (properties & (ARM_v4_Prop | ARM_v5_Prop)) ? HIGH : LOW;
+ state->is_v5 = (properties & ARM_v5_Prop) ? HIGH : LOW;
+ state->is_v5e = (properties & ARM_v5e_Prop) ? HIGH : LOW;
+ state->is_XScale = (properties & ARM_XScale_Prop) ? HIGH : LOW;
+ state->is_iWMMXt = (properties & ARM_iWMMXt_Prop) ? HIGH : LOW;
+ /* state->is_v6 = LOW */;
+ /* jeff.du 2010-08-05 */
+ state->is_v6 = (properties & ARM_v6_Prop) ? HIGH : LOW;
+ state->is_ep9312 = (properties & ARM_ep9312_Prop) ? HIGH : LOW;
+ //chy 2005-09-19
+ state->is_pxa27x = (properties & ARM_PXA27X_Prop) ? HIGH : LOW;
+
+ /* shenoubang 2012-3-11 */
+ state->is_v7 = (properties & ARM_v7_Prop) ? HIGH : LOW;
+
+ /* Only initialse the coprocessor support once we
+ know what kind of chip we are dealing with. */
+ //ARMul_CoProInit (state);
+
+}
+
+/***************************************************************************\
+* Call this routine to set up the initial machine state (or perform a RESET *
+\***************************************************************************/
+
+void
+ARMul_Reset (ARMul_State * state)
+{
+ //fprintf(stderr,"armul_reset 0: state-> Cpsr 0x%x, Mode %d\n",state->Cpsr,state->Mode);
+ state->NextInstr = 0;
+ if (state->prog32Sig) {
+ state->Reg[15] = 0;
+ state->Cpsr = INTBITS | SVC32MODE;
+ state->Mode = SVC32MODE;
+ } else {
+ state->Reg[15] = R15INTBITS | SVC26MODE;
+ state->Cpsr = INTBITS | SVC26MODE;
+ state->Mode = SVC26MODE;
+ }
+ //fprintf(stderr,"armul_reset 1: state-> Cpsr 0x%x, Mode %d\n",state->Cpsr,state->Mode);
+ //ARMul_CPSRAltered (state);
+ state->Bank = SVCBANK;
+ FLUSHPIPE;
+
+ state->EndCondition = 0;
+ state->ErrorCode = 0;
+
+ //fprintf(stderr,"armul_reset 2: state-> Cpsr 0x%x, Mode %d\n",state->Cpsr,state->Mode);
+ state->NresetSig = HIGH;
+ state->NfiqSig = HIGH;
+ state->NirqSig = HIGH;
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ state->abortSig = LOW;
+ state->AbortAddr = 1;
+
+ state->NumInstrs = 0;
+ state->NumNcycles = 0;
+ state->NumScycles = 0;
+ state->NumIcycles = 0;
+ state->NumCcycles = 0;
+ state->NumFcycles = 0;
+
+ //fprintf(stderr,"armul_reset 3: state-> Cpsr 0x%x, Mode %d\n",state->Cpsr,state->Mode);
+ //mmu_reset (state);
+ //fprintf(stderr,"armul_reset 4: state-> Cpsr 0x%x, Mode %d\n",state->Cpsr,state->Mode);
+
+ //mem_reset (state); /* move to memory/ram.c */
+
+ //fprintf(stderr,"armul_reset 5: state-> Cpsr 0x%x, Mode %d\n",state->Cpsr,state->Mode);
+ /*remove later. walimis 03.7.17 */
+ //io_reset(state);
+ //lcd_disable(state);
+
+ /*ywc 2005-04-07 move from ARMul_NewState , because skyeye_config.no_dbct will
+ *be configured in skyeye_option_init and it is called after ARMul_NewState*/
+ state->tea_break_ok = 0;
+ state->tea_break_addr = 0;
+ state->tea_pc = 0;
+#ifdef DBCT
+ if (!skyeye_config.no_dbct) {
+ //teawater add for arm2x86 2005.02.14-------------------------------------------
+ if (arm2x86_init (state)) {
+ printf ("SKYEYE: arm2x86_init error\n");
+ //skyeye_exit (-1);
+ }
+ //AJ2D--------------------------------------------------------------------------
+ }
+#endif
+}
+
+
+/***************************************************************************\
+* Emulate the execution of an entire program. Start the correct emulator *
+* (Emulate26 for a 26 bit ARM and Emulate32 for a 32 bit ARM), return the *
+* address of the last instruction that is executed. *
+\***************************************************************************/
+
+//teawater add DBCT_TEST_SPEED 2005.10.04---------------------------------------
+#ifdef DBCT_TEST_SPEED
+static ARMul_State *dbct_test_speed_state = NULL;
+static void
+dbct_test_speed_sig(int signo)
+{
+ printf("\n0x%llx %llu\n", dbct_test_speed_state->instr_count, dbct_test_speed_state->instr_count);
+ exit(0);
+ //skyeye_exit(0);
+}
+#endif //DBCT_TEST_SPEED
+//AJ2D--------------------------------------------------------------------------
+
+ARMword
+ARMul_DoProg (ARMul_State * state)
+{
+ ARMword pc = 0;
+
+ /*
+ * 2007-01-24 removed the term-io functions by Anthony Lee,
+ * moved to "device/uart/skyeye_uart_stdio.c".
+ */
+
+//teawater add DBCT_TEST_SPEED 2005.10.04---------------------------------------
+#ifdef DBCT_TEST_SPEED
+ {
+ if (!dbct_test_speed_state) {
+ //init timer
+ struct itimerval value;
+ struct sigaction act;
+
+ dbct_test_speed_state = state;
+ state->instr_count = 0;
+ act.sa_handler = dbct_test_speed_sig;
+ act.sa_flags = SA_RESTART;
+ //cygwin don't support ITIMER_VIRTUAL or ITIMER_PROF
+#ifndef __CYGWIN__
+ if (sigaction(SIGVTALRM, &act, NULL) == -1) {
+#else
+ if (sigaction(SIGALRM, &act, NULL) == -1) {
+#endif //__CYGWIN__
+ fprintf(stderr, "init timer error.\n");
+ exit(-1);
+ //skyeye_exit(-1);
+ }
+ if (skyeye_config.dbct_test_speed_sec) {
+ value.it_value.tv_sec = skyeye_config.dbct_test_speed_sec;
+ } else {
+ value.it_value.tv_sec = DBCT_TEST_SPEED_SEC;
+ }
+ printf("dbct_test_speed_sec = %ld\n", value.it_value.tv_sec);
+ value.it_value.tv_usec = 0;
+ value.it_interval.tv_sec = 0;
+ value.it_interval.tv_usec = 0;
+#ifndef __CYGWIN__
+ if (setitimer(ITIMER_VIRTUAL, &value, NULL) == -1) {
+#else
+ if (setitimer(ITIMER_REAL, &value, NULL) == -1) {
+#endif //__CYGWIN__
+ fprintf(stderr, "init timer error.\n");
+ //skyeye_exit(-1);
+ }
+ }
+ }
+#endif //DBCT_TEST_SPEED
+//AJ2D--------------------------------------------------------------------------
+ state->Emulate = RUN;
+ while (state->Emulate != STOP) {
+ state->Emulate = RUN;
+
+ /*ywc 2005-03-31 */
+ if (state->prog32Sig && ARMul_MODE32BIT) {
+#ifdef DBCT
+ if (skyeye_config.no_dbct) {
+ pc = ARMul_Emulate32 (state);
+ } else {
+ pc = ARMul_Emulate32_dbct (state);
+ }
+#else
+ pc = ARMul_Emulate32 (state);
+#endif
+ }
+
+ else {
+ //pc = ARMul_Emulate26 (state);
+ }
+ //chy 2006-02-22, should test debugmode first
+ //chy 2006-04-14, put below codes in ARMul_Emulate
+#if 0
+ if(debugmode)
+ if(remote_interrupt())
+ state->Emulate = STOP;
+#endif
+ }
+
+ /*
+ * 2007-01-24 removed the term-io functions by Anthony Lee,
+ * moved to "device/uart/skyeye_uart_stdio.c".
+ */
+
+ return (pc);
+}
+
+/***************************************************************************\
+* Emulate the execution of one instruction. Start the correct emulator *
+* (Emulate26 for a 26 bit ARM and Emulate32 for a 32 bit ARM), return the *
+* address of the instruction that is executed. *
+\***************************************************************************/
+
+ARMword
+ARMul_DoInstr (ARMul_State * state)
+{
+ ARMword pc = 0;
+
+ state->Emulate = ONCE;
+
+ /*ywc 2005-03-31 */
+ if (state->prog32Sig && ARMul_MODE32BIT) {
+#ifdef DBCT
+ if (skyeye_config.no_dbct) {
+ pc = ARMul_Emulate32 (state);
+ } else {
+//teawater add compile switch for DBCT GDB RSP function 2005.10.21--------------
+#ifndef DBCT_GDBRSP
+ printf("DBCT GDBRSP function switch is off.\n");
+ printf("To use this function, open \"#define DBCT_GDBRSP\" in arch/arm/common/armdefs.h & recompile skyeye.\n");
+ skyeye_exit(-1);
+#endif //DBCT_GDBRSP
+//AJ2D--------------------------------------------------------------------------
+ pc = ARMul_Emulate32_dbct (state);
+ }
+#else
+ pc = ARMul_Emulate32 (state);
+#endif
+ }
+
+ //else
+ //pc = ARMul_Emulate26 (state);
+
+ return (pc);
+}
+
+/***************************************************************************\
+* This routine causes an Abort to occur, including selecting the correct *
+* mode, register bank, and the saving of registers. Call with the *
+* appropriate vector's memory address (0,4,8 ....) *
+\***************************************************************************/
+
+void
+ARMul_Abort (ARMul_State * state, ARMword vector)
+{
+ ARMword temp;
+ int isize = INSN_SIZE;
+ int esize = (TFLAG ? 0 : 4);
+ int e2size = (TFLAG ? -4 : 0);
+
+ state->Aborted = FALSE;
+
+ if (state->prog32Sig)
+ if (ARMul_MODE26BIT)
+ temp = R15PC;
+ else
+ temp = state->Reg[15];
+ else
+ temp = R15PC | ECC | ER15INT | EMODE;
+
+ switch (vector) {
+ case ARMul_ResetV: /* RESET */
+ SETABORT (INTBITS, state->prog32Sig ? SVC32MODE : SVC26MODE,
+ 0);
+ break;
+ case ARMul_UndefinedInstrV: /* Undefined Instruction */
+ SETABORT (IBIT, state->prog32Sig ? UNDEF32MODE : SVC26MODE,
+ isize);
+ break;
+ case ARMul_SWIV: /* Software Interrupt */
+ SETABORT (IBIT, state->prog32Sig ? SVC32MODE : SVC26MODE,
+ isize);
+ break;
+ case ARMul_PrefetchAbortV: /* Prefetch Abort */
+ state->AbortAddr = 1;
+ SETABORT (IBIT, state->prog32Sig ? ABORT32MODE : SVC26MODE,
+ esize);
+ break;
+ case ARMul_DataAbortV: /* Data Abort */
+ SETABORT (IBIT, state->prog32Sig ? ABORT32MODE : SVC26MODE,
+ e2size);
+ break;
+ case ARMul_AddrExceptnV: /* Address Exception */
+ SETABORT (IBIT, SVC26MODE, isize);
+ break;
+ case ARMul_IRQV: /* IRQ */
+ //chy 2003-09-02 the if sentence seems no use
+#if 0
+ if (!state->is_XScale || !state->CPRead[13] (state, 0, &temp)
+ || (temp & ARMul_CP13_R0_IRQ))
+#endif
+ SETABORT (IBIT,
+ state->prog32Sig ? IRQ32MODE : IRQ26MODE,
+ esize);
+ break;
+ case ARMul_FIQV: /* FIQ */
+ //chy 2003-09-02 the if sentence seems no use
+#if 0
+ if (!state->is_XScale || !state->CPRead[13] (state, 0, &temp)
+ || (temp & ARMul_CP13_R0_FIQ))
+#endif
+ SETABORT (INTBITS,
+ state->prog32Sig ? FIQ32MODE : FIQ26MODE,
+ esize);
+ break;
+ }
+
+ if (ARMul_MODE32BIT) {
+ /*if (state->mmu.control & CONTROL_VECTOR)
+ vector += 0xffff0000; //for v4 high exception address*/
+ if (state->vector_remap_flag)
+ vector += state->vector_remap_addr; /* support some remap function in LPC processor */
+ ARMul_SetR15 (state, vector);
+ } else
+ ARMul_SetR15 (state, R15CCINTMODE | vector);
+}
--- /dev/null
+/* armsupp.c -- ARMulator support code: ARM6 Instruction Emulator.
+ Copyright (C) 1994 Advanced RISC Machines Ltd.
+
+ 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 of the License, 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. */
+
+#include "Kernel.h"
+
+#include "arm/skyeye_common/armdefs.h"
+#include "arm/skyeye_common/armemu.h"
+#include "arm/disassembler/arm_disasm.h"
+#include "arm/memory.h"
+
+
+static ARMword ModeToBank (ARMword);
+static void EnvokeList (ARMul_State *, unsigned int, unsigned int);
+
+struct EventNode {
+ /* An event list node. */
+ unsigned (*func) (ARMul_State *); /* The function to call. */
+ struct EventNode *next;
+};
+
+/* This routine returns the value of a register from a mode. */
+
+ARMword
+ARMul_GetReg (ARMul_State * state, unsigned mode, unsigned reg)
+{
+ mode &= MODEBITS;
+ if (mode != state->Mode)
+ return (state->RegBank[ModeToBank ((ARMword) mode)][reg]);
+ else
+ return (state->Reg[reg]);
+}
+
+/* This routine sets the value of a register for a mode. */
+
+void
+ARMul_SetReg (ARMul_State * state, unsigned mode, unsigned reg, ARMword value)
+{
+ mode &= MODEBITS;
+ if (mode != state->Mode)
+ state->RegBank[ModeToBank ((ARMword) mode)][reg] = value;
+ else
+ state->Reg[reg] = value;
+}
+
+/* This routine returns the value of the PC, mode independently. */
+
+ARMword
+ARMul_GetPC (ARMul_State * state)
+{
+ if (state->Mode > SVC26MODE)
+ return state->Reg[15];
+ else
+ return R15PC;
+}
+
+/* This routine returns the value of the PC, mode independently. */
+
+ARMword
+ARMul_GetNextPC (ARMul_State * state)
+{
+ if (state->Mode > SVC26MODE)
+ return state->Reg[15] + INSN_SIZE;
+ else
+ return (state->Reg[15] + INSN_SIZE) & R15PCBITS;
+}
+
+/* This routine sets the value of the PC. */
+
+void
+ARMul_SetPC (ARMul_State * state, ARMword value)
+{
+ if (ARMul_MODE32BIT)
+ state->Reg[15] = value & PCBITS;
+ else
+ state->Reg[15] = R15CCINTMODE | (value & R15PCBITS);
+ FLUSHPIPE;
+}
+
+/* This routine returns the value of register 15, mode independently. */
+
+ARMword
+ARMul_GetR15 (ARMul_State * state)
+{
+ if (state->Mode > SVC26MODE)
+ return (state->Reg[15]);
+ else
+ return (R15PC | ECC | ER15INT | EMODE);
+}
+
+/* This routine sets the value of Register 15. */
+
+void
+ARMul_SetR15 (ARMul_State * state, ARMword value)
+{
+ if (ARMul_MODE32BIT)
+ state->Reg[15] = value & PCBITS;
+ else {
+ state->Reg[15] = value;
+ ARMul_R15Altered (state);
+ }
+ FLUSHPIPE;
+}
+
+/* This routine returns the value of the CPSR. */
+
+ARMword
+ARMul_GetCPSR (ARMul_State * state)
+{
+ //chy 2003-08-20: below is from gdb20030716, maybe isn't suitable for system simulator
+ //return (CPSR | state->Cpsr); for gdb20030716
+ return (CPSR); //had be tested in old skyeye with gdb5.0-5.3
+}
+
+/* This routine sets the value of the CPSR. */
+
+void
+ARMul_SetCPSR (ARMul_State * state, ARMword value)
+{
+ state->Cpsr = value;
+ ARMul_CPSRAltered (state);
+}
+
+/* This routine does all the nasty bits involved in a write to the CPSR,
+ including updating the register bank, given a MSR instruction. */
+
+void
+ARMul_FixCPSR (ARMul_State * state, ARMword instr, ARMword rhs)
+{
+ state->Cpsr = ARMul_GetCPSR (state);
+ //chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
+ if (state->Mode != USER26MODE && state->Mode != USER32MODE ) {
+ /* In user mode, only write flags. */
+ if (BIT (16))
+ SETPSR_C (state->Cpsr, rhs);
+ if (BIT (17))
+ SETPSR_X (state->Cpsr, rhs);
+ if (BIT (18))
+ SETPSR_S (state->Cpsr, rhs);
+ }
+ if (BIT (19))
+ SETPSR_F (state->Cpsr, rhs);
+ ARMul_CPSRAltered (state);
+}
+
+/* Get an SPSR from the specified mode. */
+
+ARMword
+ARMul_GetSPSR (ARMul_State * state, ARMword mode)
+{
+ ARMword bank = ModeToBank (mode & MODEBITS);
+
+ if (!BANK_CAN_ACCESS_SPSR (bank))
+ return ARMul_GetCPSR (state);
+
+ return state->Spsr[bank];
+}
+
+/* This routine does a write to an SPSR. */
+
+void
+ARMul_SetSPSR (ARMul_State * state, ARMword mode, ARMword value)
+{
+ ARMword bank = ModeToBank (mode & MODEBITS);
+
+ if (BANK_CAN_ACCESS_SPSR (bank))
+ state->Spsr[bank] = value;
+}
+
+/* This routine does a write to the current SPSR, given an MSR instruction. */
+
+void
+ARMul_FixSPSR (ARMul_State * state, ARMword instr, ARMword rhs)
+{
+ if (BANK_CAN_ACCESS_SPSR (state->Bank)) {
+ if (BIT (16))
+ SETPSR_C (state->Spsr[state->Bank], rhs);
+ if (BIT (17))
+ SETPSR_X (state->Spsr[state->Bank], rhs);
+ if (BIT (18))
+ SETPSR_S (state->Spsr[state->Bank], rhs);
+ if (BIT (19))
+ SETPSR_F (state->Spsr[state->Bank], rhs);
+ }
+}
+
+/* This routine updates the state of the emulator after the Cpsr has been
+ changed. Both the processor flags and register bank are updated. */
+
+void
+ARMul_CPSRAltered (ARMul_State * state)
+{
+ ARMword oldmode;
+
+ if (state->prog32Sig == LOW)
+ state->Cpsr &= (CCBITS | INTBITS | R15MODEBITS);
+
+ oldmode = state->Mode;
+
+ /*if (state->Mode != (state->Cpsr & MODEBITS)) {
+ state->Mode =
+ ARMul_SwitchMode (state, state->Mode,
+ state->Cpsr & MODEBITS);
+
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ }*/
+ //state->Cpsr &= ~MODEBITS;
+
+ ASSIGNINT (state->Cpsr & INTBITS);
+ //state->Cpsr &= ~INTBITS;
+ ASSIGNN ((state->Cpsr & NBIT) != 0);
+ //state->Cpsr &= ~NBIT;
+ ASSIGNZ ((state->Cpsr & ZBIT) != 0);
+ //state->Cpsr &= ~ZBIT;
+ ASSIGNC ((state->Cpsr & CBIT) != 0);
+ //state->Cpsr &= ~CBIT;
+ ASSIGNV ((state->Cpsr & VBIT) != 0);
+ //state->Cpsr &= ~VBIT;
+ ASSIGNQ ((state->Cpsr & QBIT) != 0);
+ //state->Cpsr &= ~QBIT;
+ state->GEFlag = (state->Cpsr & 0x000F0000);
+#ifdef MODET
+ ASSIGNT ((state->Cpsr & TBIT) != 0);
+ //state->Cpsr &= ~TBIT;
+#endif
+
+ if (oldmode > SVC26MODE) {
+ if (state->Mode <= SVC26MODE) {
+ state->Emulate = CHANGEMODE;
+ state->Reg[15] = ECC | ER15INT | EMODE | R15PC;
+ }
+ } else {
+ if (state->Mode > SVC26MODE) {
+ state->Emulate = CHANGEMODE;
+ state->Reg[15] = R15PC;
+ } else
+ state->Reg[15] = ECC | ER15INT | EMODE | R15PC;
+ }
+}
+
+/* This routine updates the state of the emulator after register 15 has
+ been changed. Both the processor flags and register bank are updated.
+ This routine should only be called from a 26 bit mode. */
+
+void
+ARMul_R15Altered (ARMul_State * state)
+{
+ if (state->Mode != R15MODE) {
+ state->Mode = ARMul_SwitchMode (state, state->Mode, R15MODE);
+ state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
+ }
+
+ if (state->Mode > SVC26MODE)
+ state->Emulate = CHANGEMODE;
+
+ ASSIGNR15INT (R15INT);
+
+ ASSIGNN ((state->Reg[15] & NBIT) != 0);
+ ASSIGNZ ((state->Reg[15] & ZBIT) != 0);
+ ASSIGNC ((state->Reg[15] & CBIT) != 0);
+ ASSIGNV ((state->Reg[15] & VBIT) != 0);
+}
+
+/* This routine controls the saving and restoring of registers across mode
+ changes. The regbank matrix is largely unused, only rows 13 and 14 are
+ used across all modes, 8 to 14 are used for FIQ, all others use the USER
+ column. It's easier this way. old and new parameter are modes numbers.
+ Notice the side effect of changing the Bank variable. */
+
+ARMword
+ARMul_SwitchMode (ARMul_State * state, ARMword oldmode, ARMword newmode)
+{
+ unsigned i;
+ ARMword oldbank;
+ ARMword newbank;
+ static int revision_value = 53;
+
+ oldbank = ModeToBank (oldmode);
+ newbank = state->Bank = ModeToBank (newmode);
+
+ /* Do we really need to do it? */
+ if (oldbank != newbank) {
+ if (oldbank == 3 && newbank == 2) {
+ //printf("icounter is %d PC is %x MODE CHANGED : %d --> %d\n", state->NumInstrs, state->pc, oldbank, newbank);
+ if (state->NumInstrs >= 5832487) {
+// printf("%d, ", state->NumInstrs + revision_value);
+// printf("revision_value : %d\n", revision_value);
+ revision_value ++;
+ }
+ }
+ /* Save away the old registers. */
+ switch (oldbank) {
+ case USERBANK:
+ case IRQBANK:
+ case SVCBANK:
+ case ABORTBANK:
+ case UNDEFBANK:
+ if (newbank == FIQBANK)
+ for (i = 8; i < 13; i++)
+ state->RegBank[USERBANK][i] =
+ state->Reg[i];
+ state->RegBank[oldbank][13] = state->Reg[13];
+ state->RegBank[oldbank][14] = state->Reg[14];
+ break;
+ case FIQBANK:
+ for (i = 8; i < 15; i++)
+ state->RegBank[FIQBANK][i] = state->Reg[i];
+ break;
+ case DUMMYBANK:
+ for (i = 8; i < 15; i++)
+ state->RegBank[DUMMYBANK][i] = 0;
+ break;
+ default:
+ abort ();
+ }
+
+ /* Restore the new registers. */
+ switch (newbank) {
+ case USERBANK:
+ case IRQBANK:
+ case SVCBANK:
+ case ABORTBANK:
+ case UNDEFBANK:
+ if (oldbank == FIQBANK)
+ for (i = 8; i < 13; i++)
+ state->Reg[i] =
+ state->RegBank[USERBANK][i];
+ state->Reg[13] = state->RegBank[newbank][13];
+ state->Reg[14] = state->RegBank[newbank][14];
+ break;
+ case FIQBANK:
+ for (i = 8; i < 15; i++)
+ state->Reg[i] = state->RegBank[FIQBANK][i];
+ break;
+ case DUMMYBANK:
+ for (i = 8; i < 15; i++)
+ state->Reg[i] = 0;
+ break;
+ default:
+ abort ();
+ }
+ }
+
+ return newmode;
+}
+
+/* Given a processor mode, this routine returns the
+ register bank that will be accessed in that mode. */
+
+static ARMword
+ModeToBank (ARMword mode)
+{
+ static ARMword bankofmode[] = {
+ USERBANK, FIQBANK, IRQBANK, SVCBANK,
+ DUMMYBANK, DUMMYBANK, DUMMYBANK, DUMMYBANK,
+ DUMMYBANK, DUMMYBANK, DUMMYBANK, DUMMYBANK,
+ DUMMYBANK, DUMMYBANK, DUMMYBANK, DUMMYBANK,
+ USERBANK, FIQBANK, IRQBANK, SVCBANK,
+ DUMMYBANK, DUMMYBANK, DUMMYBANK, ABORTBANK,
+ DUMMYBANK, DUMMYBANK, DUMMYBANK, UNDEFBANK,
+ DUMMYBANK, DUMMYBANK, DUMMYBANK, SYSTEMBANK
+ };
+
+ if (mode >= (sizeof (bankofmode) / sizeof (bankofmode[0])))
+ return DUMMYBANK;
+
+ return bankofmode[mode];
+}
+
+/* Returns the register number of the nth register in a reg list. */
+
+unsigned
+ARMul_NthReg (ARMword instr, unsigned number)
+{
+ unsigned bit, upto;
+
+ for (bit = 0, upto = 0; upto <= number; bit++)
+ if (BIT (bit))
+ upto++;
+
+ return (bit - 1);
+}
+
+/* Unsigned sum of absolute difference */
+u8 ARMul_UnsignedAbsoluteDifference(u8 left, u8 right)
+{
+ if (left > right)
+ return left - right;
+
+ return right - left;
+}
+
+/* Assigns the N and Z flags depending on the value of result. */
+
+void
+ARMul_NegZero (ARMul_State * state, ARMword result)
+{
+ if (NEG (result)) {
+ SETN;
+ CLEARZ;
+ } else if (result == 0) {
+ CLEARN;
+ SETZ;
+ } else {
+ CLEARN;
+ CLEARZ;
+ }
+}
+
+// Add with carry, indicates if a carry-out or signed overflow occurred.
+u32 AddWithCarry(u32 left, u32 right, u32 carry_in, bool* carry_out_occurred, bool* overflow_occurred)
+{
+ u64 unsigned_sum = (u64)left + (u64)right + (u64)carry_in;
+ s64 signed_sum = (s64)(s32)left + (s64)(s32)right + (s64)carry_in;
+ u64 result = (unsigned_sum & 0xFFFFFFFF);
+
+ if (carry_out_occurred)
+ *carry_out_occurred = (result != unsigned_sum);
+
+ if (overflow_occurred)
+ *overflow_occurred = ((s64)(s32)result != signed_sum);
+
+ return (u32)result;
+}
+
+// Compute whether an addition of A and B, giving RESULT, overflowed.
+bool AddOverflow(ARMword a, ARMword b, ARMword result)
+{
+ return ((NEG(a) && NEG(b) && POS(result)) ||
+ (POS(a) && POS(b) && NEG(result)));
+}
+
+// Compute whether a subtraction of A and B, giving RESULT, overflowed.
+bool SubOverflow(ARMword a, ARMword b, ARMword result)
+{
+ return ((NEG(a) && POS(b) && POS(result)) ||
+ (POS(a) && NEG(b) && NEG(result)));
+}
+
+/* Assigns the C flag after an addition of a and b to give result. */
+
+void
+ARMul_AddCarry (ARMul_State * state, ARMword a, ARMword b, ARMword result)
+{
+ ASSIGNC ((NEG (a) && NEG (b)) ||
+ (NEG (a) && POS (result)) || (NEG (b) && POS (result)));
+}
+
+/* Assigns the V flag after an addition of a and b to give result. */
+
+void
+ARMul_AddOverflow (ARMul_State * state, ARMword a, ARMword b, ARMword result)
+{
+ ASSIGNV (AddOverflow (a, b, result));
+}
+
+// Returns true if the Q flag should be set as a result of overflow.
+bool ARMul_AddOverflowQ(ARMword a, ARMword b)
+{
+ u32 result = a + b;
+ if (((result ^ a) & (u32)0x80000000) && ((a ^ b) & (u32)0x80000000) == 0)
+ return true;
+
+ return false;
+}
+
+/* Assigns the C flag after an subtraction of a and b to give result. */
+
+void
+ARMul_SubCarry (ARMul_State * state, ARMword a, ARMword b, ARMword result)
+{
+ ASSIGNC ((NEG (a) && POS (b)) ||
+ (NEG (a) && POS (result)) || (POS (b) && POS (result)));
+}
+
+/* Assigns the V flag after an subtraction of a and b to give result. */
+
+void
+ARMul_SubOverflow (ARMul_State * state, ARMword a, ARMword b, ARMword result)
+{
+ ASSIGNV (SubOverflow (a, b, result));
+}
+
+/* 8-bit signed saturated addition */
+u8 ARMul_SignedSaturatedAdd8(u8 left, u8 right)
+{
+ u8 result = left + right;
+
+ if (((result ^ left) & 0x80) && ((left ^ right) & 0x80) == 0) {
+ if (left & 0x80)
+ result = 0x80;
+ else
+ result = 0x7F;
+ }
+
+ return result;
+}
+
+/* 8-bit signed saturated subtraction */
+u8 ARMul_SignedSaturatedSub8(u8 left, u8 right)
+{
+ u8 result = left - right;
+
+ if (((result ^ left) & 0x80) && ((left ^ right) & 0x80) != 0) {
+ if (left & 0x80)
+ result = 0x80;
+ else
+ result = 0x7F;
+ }
+
+ return result;
+}
+
+/* 16-bit signed saturated addition */
+u16 ARMul_SignedSaturatedAdd16(u16 left, u16 right)
+{
+ u16 result = left + right;
+
+ if (((result ^ left) & 0x8000) && ((left ^ right) & 0x8000) == 0) {
+ if (left & 0x8000)
+ result = 0x8000;
+ else
+ result = 0x7FFF;
+ }
+
+ return result;
+}
+
+/* 16-bit signed saturated subtraction */
+u16 ARMul_SignedSaturatedSub16(u16 left, u16 right)
+{
+ u16 result = left - right;
+
+ if (((result ^ left) & 0x8000) && ((left ^ right) & 0x8000) != 0) {
+ if (left & 0x8000)
+ result = 0x8000;
+ else
+ result = 0x7FFF;
+ }
+
+ return result;
+}
+
+/* 8-bit unsigned saturated addition */
+u8 ARMul_UnsignedSaturatedAdd8(u8 left, u8 right)
+{
+ u8 result = left + right;
+
+ if (result < left)
+ result = 0xFF;
+
+ return result;
+}
+
+/* 16-bit unsigned saturated addition */
+u16 ARMul_UnsignedSaturatedAdd16(u16 left, u16 right)
+{
+ u16 result = left + right;
+
+ if (result < left)
+ result = 0xFFFF;
+
+ return result;
+}
+
+/* 8-bit unsigned saturated subtraction */
+u8 ARMul_UnsignedSaturatedSub8(u8 left, u8 right)
+{
+ if (left <= right)
+ return 0;
+
+ return left - right;
+}
+
+/* 16-bit unsigned saturated subtraction */
+u16 ARMul_UnsignedSaturatedSub16(u16 left, u16 right)
+{
+ if (left <= right)
+ return 0;
+
+ return left - right;
+}
+
+// Signed saturation.
+u32 ARMul_SignedSatQ(s32 value, u8 shift, bool* saturation_occurred)
+{
+ const u32 max = (1 << shift) - 1;
+ const s32 top = (value >> shift);
+
+ if (top > 0) {
+ *saturation_occurred = true;
+ return max;
+ }
+ else if (top < -1) {
+ *saturation_occurred = true;
+ return ~max;
+ }
+
+ *saturation_occurred = false;
+ return (u32)value;
+}
+
+// Unsigned saturation
+u32 ARMul_UnsignedSatQ(s32 value, u8 shift, bool* saturation_occurred)
+{
+ const u32 max = (1 << shift) - 1;
+
+ if (value < 0) {
+ *saturation_occurred = true;
+ return 0;
+ } else if ((u32)value > max) {
+ *saturation_occurred = true;
+ return max;
+ }
+
+ *saturation_occurred = false;
+ return (u32)value;
+}
+
+/* This function does the work of generating the addresses used in an
+ LDC instruction. The code here is always post-indexed, it's up to the
+ caller to get the input address correct and to handle base register
+ modification. It also handles the Busy-Waiting. */
+
+void
+ARMul_LDC (ARMul_State * state, ARMword instr, ARMword address)
+{
+ unsigned cpab;
+ ARMword data;
+
+ UNDEF_LSCPCBaseWb;
+ //printf("SKYEYE ARMul_LDC, CPnum is %x, instr %x, addr %x\n",CPNum, instr, address);
+ /*chy 2004-05-23 should update this function in the future,should concern dataabort*/
+// chy 2004-05-25 , fix it now,so needn't printf
+// printf("SKYEYE ARMul_LDC, should update this function!!!!!\n");
+ //exit(-1);
+
+ //if (!CP_ACCESS_ALLOWED (state, CPNum)) {
+ if (!state->LDC[CPNum]) {
+ /*
+ printf
+ ("SKYEYE ARMul_LDC,NOT ALLOW, underinstr, CPnum is %x, instr %x, addr %x\n",
+ CPNum, instr, address);
+ */
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+
+ /*if (ADDREXCEPT (address))
+ INTERNALABORT (address);*/
+
+ cpab = (state->LDC[CPNum]) (state, ARMul_FIRST, instr, 0);
+ while (cpab == ARMul_BUSY) {
+ ARMul_Icycles (state, 1, 0);
+
+ if (IntPending (state)) {
+ cpab = (state->LDC[CPNum]) (state, ARMul_INTERRUPT,
+ instr, 0);
+ return;
+ } else
+ cpab = (state->LDC[CPNum]) (state, ARMul_BUSY, instr,
+ 0);
+ }
+ if (cpab == ARMul_CANT) {
+ /*
+ printf
+ ("SKYEYE ARMul_LDC,NOT CAN, underinstr, CPnum is %x, instr %x, addr %x\n",
+ CPNum, instr, address);
+ */
+ CPTAKEABORT;
+ return;
+ }
+
+ cpab = (state->LDC[CPNum]) (state, ARMul_TRANSFER, instr, 0);
+ data = ARMul_LoadWordN (state, address);
+ //chy 2004-05-25
+ if (state->abortSig || state->Aborted)
+ goto L_ldc_takeabort;
+
+ BUSUSEDINCPCN;
+//chy 2004-05-25
+ /*
+ if (BIT (21))
+ LSBase = state->Base;
+ */
+
+ cpab = (state->LDC[CPNum]) (state, ARMul_DATA, instr, data);
+
+ while (cpab == ARMul_INC) {
+ address += 4;
+ data = ARMul_LoadWordN (state, address);
+ //chy 2004-05-25
+ if (state->abortSig || state->Aborted)
+ goto L_ldc_takeabort;
+
+ cpab = (state->LDC[CPNum]) (state, ARMul_DATA, instr, data);
+ }
+
+//chy 2004-05-25
+L_ldc_takeabort:
+ if (BIT (21)) {
+ if (!
+ ((state->abortSig || state->Aborted)
+ && state->lateabtSig == LOW))
+ LSBase = state->Base;
+ }
+
+ if (state->abortSig || state->Aborted)
+ TAKEABORT;
+}
+
+/* This function does the work of generating the addresses used in an
+ STC instruction. The code here is always post-indexed, it's up to the
+ caller to get the input address correct and to handle base register
+ modification. It also handles the Busy-Waiting. */
+
+void
+ARMul_STC (ARMul_State * state, ARMword instr, ARMword address)
+{
+ unsigned cpab;
+ ARMword data;
+
+ UNDEF_LSCPCBaseWb;
+
+ //printf("SKYEYE ARMul_STC, CPnum is %x, instr %x, addr %x\n",CPNum, instr, address);
+ /*chy 2004-05-23 should update this function in the future,should concern dataabort */
+// skyeye_instr_debug=0;printf("SKYEYE debug end!!!!\n");
+// chy 2004-05-25 , fix it now,so needn't printf
+// printf("SKYEYE ARMul_STC, should update this function!!!!!\n");
+
+ //exit(-1);
+ //if (!CP_ACCESS_ALLOWED (state, CPNum)) {
+ if (!state->STC[CPNum]) {
+ /*
+ printf
+ ("SKYEYE ARMul_STC,NOT ALLOW, undefinstr, CPnum is %x, instr %x, addr %x\n",
+ CPNum, instr, address);
+ */
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+
+ /*if (ADDREXCEPT (address) || VECTORACCESS (address))
+ INTERNALABORT (address);*/
+
+ cpab = (state->STC[CPNum]) (state, ARMul_FIRST, instr, &data);
+ while (cpab == ARMul_BUSY) {
+ ARMul_Icycles (state, 1, 0);
+ if (IntPending (state)) {
+ cpab = (state->STC[CPNum]) (state, ARMul_INTERRUPT,
+ instr, 0);
+ return;
+ } else
+ cpab = (state->STC[CPNum]) (state, ARMul_BUSY, instr,
+ &data);
+ }
+
+ if (cpab == ARMul_CANT) {
+ /*
+ printf
+ ("SKYEYE ARMul_STC,CANT, undefinstr, CPnum is %x, instr %x, addr %x\n",
+ CPNum, instr, address);
+ */
+ CPTAKEABORT;
+ return;
+ }
+ /*#ifndef MODE32
+ if (ADDREXCEPT (address) || VECTORACCESS (address))
+ INTERNALABORT (address);
+ #endif*/
+ BUSUSEDINCPCN;
+//chy 2004-05-25
+ /*
+ if (BIT (21))
+ LSBase = state->Base;
+ */
+ cpab = (state->STC[CPNum]) (state, ARMul_DATA, instr, &data);
+ ARMul_StoreWordN (state, address, data);
+ //chy 2004-05-25
+ if (state->abortSig || state->Aborted)
+ goto L_stc_takeabort;
+
+ while (cpab == ARMul_INC) {
+ address += 4;
+ cpab = (state->STC[CPNum]) (state, ARMul_DATA, instr, &data);
+ ARMul_StoreWordN (state, address, data);
+ //chy 2004-05-25
+ if (state->abortSig || state->Aborted)
+ goto L_stc_takeabort;
+ }
+//chy 2004-05-25
+L_stc_takeabort:
+ if (BIT (21)) {
+ if (!
+ ((state->abortSig || state->Aborted)
+ && state->lateabtSig == LOW))
+ LSBase = state->Base;
+ }
+
+ if (state->abortSig || state->Aborted)
+ TAKEABORT;
+}
+
+/* This function does the Busy-Waiting for an MCR instruction. */
+
+void
+ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source)
+{
+ unsigned cpab;
+ int cm = BITS(0, 3) & 0xf;
+ int cp = BITS(5, 7) & 0x7;
+ int rd = BITS(12, 15) & 0xf;
+ int cn = BITS(16, 19) & 0xf;
+ int cpopc = BITS(21, 23) & 0x7;
+
+ if (CPNum == 15 && source == 0) //Cache flush
+ {
+ return;
+ }
+
+ //printf("SKYEYE ARMul_MCR, CPnum is %x, source %x\n",CPNum, source);
+ //if (!CP_ACCESS_ALLOWED (state, CPNum)) {
+ if (!state->MCR[CPNum]) {
+ //chy 2004-07-19 should fix in the future ????!!!!
+ XDSERROR("SKYEYE ARMul_MCR, ACCESS_not ALLOWed, UndefinedInstr CPnum is %x, source %x", CPNum, source);
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+
+ //DEBUG("SKYEYE ARMul_MCR p%d, %d, r%d, c%d, c%d, %d\n", CPNum, cpopc, rd, cn, cm, cp);
+ //DEBUG("plutoo: MCR not implemented\n");
+ //exit(1);
+ //return;
+
+ cpab = (state->MCR[CPNum]) (state, ARMul_FIRST, instr, source);
+
+ while (cpab == ARMul_BUSY) {
+ ARMul_Icycles (state, 1, 0);
+
+ if (IntPending (state)) {
+ cpab = (state->MCR[CPNum]) (state, ARMul_INTERRUPT,
+ instr, 0);
+ return;
+ } else
+ cpab = (state->MCR[CPNum]) (state, ARMul_BUSY, instr,
+ source);
+ }
+
+ if (cpab == ARMul_CANT) {
+ XDSERROR("SKYEYE ARMul_MCR, CANT, UndefinedInstr %x CPnum is %x, source %x", instr, CPNum, source); //ichfly todo
+ //ARMul_Abort (state, ARMul_UndefinedInstrV);
+ } else {
+ BUSUSEDINCPCN;
+ ARMul_Ccycles (state, 1, 0);
+ }
+}
+
+/* This function does the Busy-Waiting for an MCRR instruction. */
+
+void
+ARMul_MCRR (ARMul_State * state, ARMword instr, ARMword source1, ARMword source2)
+{
+ unsigned cpab;
+
+ //if (!CP_ACCESS_ALLOWED (state, CPNum)) {
+ if (!state->MCRR[CPNum]) {
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+
+ cpab = (state->MCRR[CPNum]) (state, ARMul_FIRST, instr, source1, source2);
+
+ while (cpab == ARMul_BUSY) {
+ ARMul_Icycles (state, 1, 0);
+
+ if (IntPending (state)) {
+ cpab = (state->MCRR[CPNum]) (state, ARMul_INTERRUPT,
+ instr, 0, 0);
+ return;
+ } else
+ cpab = (state->MCRR[CPNum]) (state, ARMul_BUSY, instr,
+ source1, source2);
+ }
+ if (cpab == ARMul_CANT) {
+ printf ("In %s, CoProcesscor returned CANT, CPnum is %x, instr %x, source %x %x\n", __FUNCTION__, CPNum, instr, source1, source2);
+ ARMul_Abort (state, ARMul_UndefinedInstrV);
+ } else {
+ BUSUSEDINCPCN;
+ ARMul_Ccycles (state, 1, 0);
+ }
+}
+
+/* This function does the Busy-Waiting for an MRC instruction. */
+
+ARMword ARMul_MRC (ARMul_State * state, ARMword instr)
+{
+ int cm = BITS(0, 3) & 0xf;
+ int cp = BITS(5, 7) & 0x7;
+ int rd = BITS(12, 15) & 0xf;
+ int cn = BITS(16, 19) & 0xf;
+ int cpopc = BITS(21, 23) & 0x7;
+
+ if (cn == 13 && cm == 0 && cp == 3) { //c13,c0,3; returns CPU svc buffer
+ ARMword result = state->m_currentThread->m_TSL3DS;
+
+ if (result != -1) {
+ return result;
+ }
+ }
+
+ //DEBUG("SKYEYE ARMul_MRC p%d, %d, r%d, c%d, c%d, %d\n", CPNum, cpopc, rd, cn, cm, cp);
+ //DEBUG("plutoo: MRC not implemented\n");
+ //return;
+
+ unsigned cpab;
+ ARMword result = 0;
+
+ //printf("SKYEYE ARMul_MRC, CPnum is %x, instr %x\n",CPNum, instr);
+ //if (!CP_ACCESS_ALLOWED (state, CPNum)) {
+ if (!state->MRC[CPNum]) {
+ //chy 2004-07-19 should fix in the future????!!!!
+ XDSERROR("SKYEYE ARMul_MRC,NOT ALLOWed UndefInstr CPnum is %x, instr %x", CPNum, instr);
+ ARMul_UndefInstr (state, instr);
+ return -1;
+ }
+
+ cpab = (state->MRC[CPNum]) (state, ARMul_FIRST, instr, &result);
+ while (cpab == ARMul_BUSY) {
+ ARMul_Icycles (state, 1, 0);
+ if (IntPending (state)) {
+ cpab = (state->MRC[CPNum]) (state, ARMul_INTERRUPT,
+ instr, 0);
+ return (0);
+ } else
+ cpab = (state->MRC[CPNum]) (state, ARMul_BUSY, instr,
+ &result);
+ }
+ if (cpab == ARMul_CANT) {
+ printf ("SKYEYE ARMul_MRC,CANT UndefInstr CPnum is %x, instr %x\n", CPNum, instr);
+ ARMul_Abort (state, ARMul_UndefinedInstrV);
+ /* Parent will destroy the flags otherwise. */
+ result = ECC;
+ } else {
+ BUSUSEDINCPCN;
+ ARMul_Ccycles (state, 1, 0);
+ ARMul_Icycles (state, 1, 0);
+ }
+
+ return result;
+}
+
+/* This function does the Busy-Waiting for an MRRC instruction. (to verify) */
+
+void
+ARMul_MRRC (ARMul_State * state, ARMword instr, ARMword * dest1, ARMword * dest2)
+{
+ unsigned cpab;
+ ARMword result1 = 0;
+ ARMword result2 = 0;
+
+ //if (!CP_ACCESS_ALLOWED (state, CPNum)) {
+ if (!state->MRRC[CPNum]) {
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+
+ cpab = (state->MRRC[CPNum]) (state, ARMul_FIRST, instr, &result1, &result2);
+ while (cpab == ARMul_BUSY) {
+ ARMul_Icycles (state, 1, 0);
+ if (IntPending (state)) {
+ cpab = (state->MRRC[CPNum]) (state, ARMul_INTERRUPT,
+ instr, 0, 0);
+ return;
+ } else
+ cpab = (state->MRRC[CPNum]) (state, ARMul_BUSY, instr,
+ &result1, &result2);
+ }
+ if (cpab == ARMul_CANT) {
+ printf ("In %s, CoProcesscor returned CANT, CPnum is %x, instr %x\n", __FUNCTION__, CPNum, instr);
+ ARMul_Abort (state, ARMul_UndefinedInstrV);
+ } else {
+ BUSUSEDINCPCN;
+ ARMul_Ccycles (state, 1, 0);
+ ARMul_Icycles (state, 1, 0);
+ }
+
+ *dest1 = result1;
+ *dest2 = result2;
+}
+
+/* This function does the Busy-Waiting for an CDP instruction. */
+
+void
+ARMul_CDP (ARMul_State * state, ARMword instr)
+{
+ unsigned cpab;
+
+ //if (!CP_ACCESS_ALLOWED (state, CPNum)) {
+ if (!state->CDP[CPNum]) {
+ ARMul_UndefInstr (state, instr);
+ return;
+ }
+ cpab = (state->CDP[CPNum]) (state, ARMul_FIRST, instr);
+ while (cpab == ARMul_BUSY) {
+ ARMul_Icycles (state, 1, 0);
+ if (IntPending (state)) {
+ cpab = (state->CDP[CPNum]) (state, ARMul_INTERRUPT,
+ instr);
+ return;
+ } else
+ cpab = (state->CDP[CPNum]) (state, ARMul_BUSY, instr);
+ }
+ if (cpab == ARMul_CANT)
+ ARMul_Abort (state, ARMul_UndefinedInstrV);
+ else
+ BUSUSEDN;
+}
+
+/* This function handles Undefined instructions, as CP isntruction. */
+
+void
+ARMul_UndefInstr (ARMul_State * state, ARMword instr)
+{
+ //std::string disasm = ARM_Disasm::Disassemble(state->pc, instr);
+ //ERROR("Undefined instruction!! Disasm: %s Opcode: 0x%x", disasm.c_str(), instr);
+ ARMul_Abort (state, ARMul_UndefinedInstrV);
+}
+
+/* Return TRUE if an interrupt is pending, FALSE otherwise. */
+
+unsigned
+IntPending (ARMul_State * state)
+{
+ /* Any exceptions. */
+ if (state->NresetSig == LOW) {
+ ARMul_Abort (state, ARMul_ResetV);
+ return TRUE;
+ } else if (!state->NfiqSig && !FFLAG) {
+ ARMul_Abort (state, ARMul_FIQV);
+ return TRUE;
+ } else if (!state->NirqSig && !IFLAG) {
+ ARMul_Abort (state, ARMul_IRQV);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Align a word access to a non word boundary. */
+
+ARMword
+ARMul_Align (ARMul_State* state, ARMword address, ARMword data)
+{
+ /* This code assumes the address is really unaligned,
+ as a shift by 32 is undefined in C. */
+
+ address = (address & 3) << 3; /* Get the word address. */
+ return ((data >> address) | (data << (32 - address))); /* rot right */
+}
+
+/* This routine is used to call another routine after a certain number of
+ cycles have been executed. The first parameter is the number of cycles
+ delay before the function is called, the second argument is a pointer
+ to the function. A delay of zero doesn't work, just call the function. */
+
+void
+ARMul_ScheduleEvent (ARMul_State * state, unsigned int delay,
+ unsigned (*what) (ARMul_State *))
+{
+ unsigned int when;
+ struct EventNode *event;
+
+ if (state->EventSet++ == 0)
+ state->Now = ARMul_Time (state);
+ when = (state->Now + delay) % EVENTLISTSIZE;
+ event = (struct EventNode *) malloc (sizeof (struct EventNode));
+ if (!event) {
+ printf ("SKYEYE:ARMul_ScheduleEvent: malloc event error\n");
+ exit(-1);
+ //skyeye_exit (-1);
+ }
+ event->func = what;
+ event->next = *(state->EventPtr + when);
+ *(state->EventPtr + when) = event;
+}
+
+/* This routine is called at the beginning of
+ every cycle, to envoke scheduled events. */
+
+void
+ARMul_EnvokeEvent (ARMul_State * state)
+{
+ static unsigned int then;
+
+ then = state->Now;
+ state->Now = ARMul_Time (state) % EVENTLISTSIZE;
+ if (then < state->Now)
+ /* Schedule events. */
+ EnvokeList (state, then, state->Now);
+ else if (then > state->Now) {
+ /* Need to wrap around the list. */
+ EnvokeList (state, then, EVENTLISTSIZE - 1L);
+ EnvokeList (state, 0L, state->Now);
+ }
+}
+
+/* Envokes all the entries in a range. */
+
+static void
+EnvokeList (ARMul_State * state, unsigned int from, unsigned int to)
+{
+ for (; from <= to; from++) {
+ struct EventNode *anevent;
+
+ anevent = *(state->EventPtr + from);
+ while (anevent) {
+ (anevent->func) (state);
+ state->EventSet--;
+ anevent = anevent->next;
+ }
+ *(state->EventPtr + from) = NULL;
+ }
+}
+
+/* This routine is returns the number of clock ticks since the last reset. */
+
+unsigned int
+ARMul_Time (ARMul_State * state)
+{
+ return (state->NumScycles + state->NumNcycles +
+ state->NumIcycles + state->NumCcycles + state->NumFcycles);
+}
--- /dev/null
+/* armvirt.c -- ARMulator virtual memory interace: ARM6 Instruction Emulator.
+ Copyright (C) 1994 Advanced RISC Machines Ltd.
+
+ 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 of the License, 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. */
+
+/* This file contains a complete ARMulator memory model, modelling a
+"virtual memory" system. A much simpler model can be found in armfast.c,
+and that model goes faster too, but has a fixed amount of memory. This
+model's memory has 64K pages, allocated on demand from a 64K entry page
+table. The routines PutWord and GetWord implement this. Pages are never
+freed as they might be needed again. A single area of memory may be
+defined to generate aborts. */
+
+#include "Common.h"
+#include "Kernel.h"
+#include "arm/skyeye_common/armdefs.h"
+#include "arm/skyeye_common/armemu.h"
+
+#include "arm/memory.h"
+
+
+#define dumpstack 1
+#define dumpstacksize 0x10
+#define maxdmupaddr 0x0033a850
+
+/*ARMword ARMul_GetCPSR (ARMul_State * state) {
+return 0;
+}
+ARMword ARMul_GetSPSR (ARMul_State * state, ARMword mode) {
+return 0;
+}
+void ARMul_SetCPSR (ARMul_State * state, ARMword value) {
+
+}
+void ARMul_SetSPSR (ARMul_State * state, ARMword mode, ARMword value) {
+
+}*/
+
+void ARMul_Icycles(ARMul_State * state, unsigned number, ARMword address) {
+}
+
+void ARMul_Ccycles(ARMul_State * state, unsigned number, ARMword address) {
+}
+
+ARMword ARMul_LoadInstrS(ARMul_State * state, ARMword address, ARMword isize) {
+ state->NumScycles++;
+
+#ifdef HOURGLASS
+ if ((state->NumScycles & HOURGLASS_RATE) == 0) {
+ HOURGLASS;
+ }
+#endif
+ if (isize == 2)
+ {
+ u16 data;
+ if (unlikely(state->m_MemoryMap->Read16(address, data) != Success))
+ {
+ XDSERROR("size 2 error reading from %08x", address);
+ }
+ return data;
+ }
+ else
+ {
+ u32 data;
+ if (unlikely(state->m_MemoryMap->Read32(address, data) != Success))
+ {
+ XDSERROR("size 4 error reading from %08x", address);
+ }
+ return data;
+ }
+}
+
+ARMword ARMul_LoadInstrN(ARMul_State * state, ARMword address, ARMword isize) {
+ state->NumNcycles++;
+
+ if (isize == 2)
+ {
+ u16 data;
+ if (unlikely(state->m_MemoryMap->Read16(address, data) != Success))
+ {
+ XDSERROR("size 2 error reading from %08x", address);
+ }
+ return data;
+ }
+ else
+ {
+ u32 data;
+ if (unlikely(state->m_MemoryMap->Read32(address, data) != Success))
+ {
+ XDSERROR("size 4 error reading from %08x", address);
+ }
+ return data;
+ }
+}
+
+ARMword ARMul_ReLoadInstr(ARMul_State * state, ARMword address, ARMword isize) {
+ ARMword data;
+
+ if ((isize == 2) && (address & 0x2)) {
+ u16 data;
+ if (unlikely(state->m_MemoryMap->Read16(address, data) != Success))
+ {
+ XDSERROR("error reading from %08x", address);
+ }
+ return data & 0xFFFF;
+ }
+ if (unlikely(state->m_MemoryMap->Read32(address, data) != Success))
+ {
+ XDSERROR("size 4 error reading from %08x", address);
+ }
+ return data;
+}
+
+ARMword ARMul_ReadWord(ARMul_State * state, ARMword address) {
+ u32 data;
+ if (unlikely(state->m_MemoryMap->Read32(address, data) != Success))
+ {
+ XDSERROR("error %s thread %u reading from %08x", state->m_currentThread->m_owner->GetName(), state->m_currentThread->m_thread_id, address);
+ }
+ return data;
+}
+
+ARMword ARMul_LoadWordS(ARMul_State * state, ARMword address) {
+ state->NumScycles++;
+ return ARMul_ReadWord(state, address);
+}
+
+ARMword ARMul_LoadWordN(ARMul_State * state, ARMword address) {
+ state->NumNcycles++;
+ return ARMul_ReadWord(state, address);
+}
+
+ARMword ARMul_LoadHalfWord(ARMul_State * state, ARMword address) {
+ state->NumNcycles++;
+ u16 data;
+ if (unlikely(state->m_MemoryMap->Read16(address, data) != Success))
+ {
+ XDSERROR("error %s thread %u error reading from hword from %08x", state->m_currentThread->m_owner->GetName(), state->m_currentThread->m_thread_id, address);
+ }
+ return data;
+}
+
+ARMword ARMul_ReadByte(ARMul_State * state, ARMword address) {
+ u8 data;
+ if (unlikely(state->m_MemoryMap->Read8(address, data) != Success))
+ {
+ XDSERROR("error %s thread %u reading byte from %08x", state->m_currentThread->m_owner->GetName(), state->m_currentThread->m_thread_id, address);
+ data = 0x10;
+ }
+ return data;
+}
+
+ARMword ARMul_LoadByte(ARMul_State * state, ARMword address) {
+ state->NumNcycles++;
+ return ARMul_ReadByte(state, address);
+}
+
+void ARMul_StoreHalfWord(ARMul_State * state, ARMword address, ARMword data) {
+ state->NumNcycles++;
+ if (unlikely(state->m_MemoryMap->Write16(address, data) != Success))
+ {
+ XDSERROR("error %s thread %u writing %04x to %08x", state->m_currentThread->m_owner->GetName(), state->m_currentThread->m_thread_id, data, address);
+ }
+}
+
+void ARMul_StoreByte(ARMul_State * state, ARMword address, ARMword data) {
+ state->NumNcycles++;
+ ARMul_WriteByte(state, address, data);
+}
+
+ARMword ARMul_SwapWord(ARMul_State * state, ARMword address, ARMword data) {
+ ARMword temp;
+ state->NumNcycles++;
+ temp = ARMul_ReadWord(state, address);
+ state->NumNcycles++;
+ if (unlikely(state->m_MemoryMap->Write32(address, data) != Success))
+ {
+ XDSERROR("error writing to %08x", address);
+ }
+ return temp;
+}
+
+ARMword ARMul_SwapByte(ARMul_State * state, ARMword address, ARMword data) {
+ ARMword temp;
+ temp = ARMul_LoadByte(state, address);
+ if (unlikely(state->m_MemoryMap->Write8(address, data) != Success))
+ {
+ XDSERROR("error writing to %08x", address);
+ }
+ return temp;
+}
+
+void ARMul_WriteWord(ARMul_State * state, ARMword address, ARMword data) {
+ if (unlikely(state->m_MemoryMap->Write32(address, data) != Success))
+ {
+ XDSERROR("error writing to %08x data %08x", address,data);
+ }
+}
+
+void ARMul_WriteByte(ARMul_State * state, ARMword address, ARMword data)
+{
+ if (unlikely(state->m_MemoryMap->Write8(address, data) != Success))
+ {
+ XDSERROR("error %s thread %u writing %02x to %08x", state->m_currentThread->m_owner->GetName(), state->m_currentThread->m_thread_id, data, address);
+ }
+}
+void ARMul_WriteDouble(ARMul_State* state, ARMword address, u64 data) {
+ ARMul_WriteWord(state, address, (u32)(data >> 0));
+ ARMul_WriteWord(state, address + 4, (u32)(data >> 32));
+}
+
+
+void ARMul_StoreWordS(ARMul_State * state, ARMword address, ARMword data)
+{
+ state->NumScycles++;
+ ARMul_WriteWord(state, address, data);
+}
+
+void ARMul_StoreWordN(ARMul_State * state, ARMword address, ARMword data)
+{
+ state->NumNcycles++;
+ ARMul_WriteWord(state, address, data);
+}
--- /dev/null
+/* thumbemu.c -- Thumb instruction emulation.
+ Copyright (C) 1996, Cygnus Software Technologies Ltd.
+
+ 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 of the License, 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. */
+
+/* We can provide simple Thumb simulation by decoding the Thumb
+instruction into its corresponding ARM instruction, and using the
+existing ARM simulator. */
+
+#include "Kernel.h"
+
+#include "arm/skyeye_common/skyeye_defs.h"
+
+#ifndef MODET /* required for the Thumb instruction support */
+#if 1
+#error "MODET needs to be defined for the Thumb world to work"
+#else
+#define MODET (1)
+#endif
+#endif
+
+#include "arm/skyeye_common/armdefs.h"
+#include "arm/skyeye_common/armemu.h"
+#include "arm/skyeye_common/armos.h"
+
+
+/* Decode a 16bit Thumb instruction. The instruction is in the low
+ 16-bits of the tinstr field, with the following Thumb instruction
+ held in the high 16-bits. Passing in two Thumb instructions allows
+ easier simulation of the special dual BL instruction. */
+
+tdstate
+ARMul_ThumbDecode(
+ARMul_State *state,
+ARMword pc,
+ARMword tinstr,
+ARMword *ainstr)
+{
+ tdstate valid = t_decoded; /* default assumes a valid instruction */
+ ARMword next_instr;
+
+ if (state->bigendSig) {
+ next_instr = tinstr & 0xFFFF;
+ tinstr >>= 16;
+ }
+ else {
+ next_instr = tinstr >> 16;
+ tinstr &= 0xFFFF;
+ }
+
+#if 1 /* debugging to catch non updates */
+ * ainstr = 0xDEADC0DE;
+#endif
+
+ switch ((tinstr & 0xF800) >> 11) {
+ case 0: /* LSL */
+ case 1: /* LSR */
+ case 2: /* ASR */
+ /* Format 1 */
+ *ainstr = 0xE1B00000 /* base opcode */
+ | ((tinstr & 0x1800) >> (11 - 5)) /* shift type */
+ | ((tinstr & 0x07C0) << (7 - 6)) /* imm5 */
+ | ((tinstr & 0x0038) >> 3) /* Rs */
+ | ((tinstr & 0x0007) << 12); /* Rd */
+ break;
+ case 3: /* ADD/SUB */
+ /* Format 2 */
+ {
+ ARMword subset[4] = {
+ 0xE0900000, /* ADDS Rd,Rs,Rn */
+ 0xE0500000, /* SUBS Rd,Rs,Rn */
+ 0xE2900000, /* ADDS Rd,Rs,#imm3 */
+ 0xE2500000 /* SUBS Rd,Rs,#imm3 */
+ };
+ /* It is quicker indexing into a table, than performing switch
+ or conditionals: */
+ *ainstr = subset[(tinstr & 0x0600) >> 9] /* base opcode */
+ | ((tinstr & 0x01C0) >> 6) /* Rn or imm3 */
+ | ((tinstr & 0x0038) << (16 - 3)) /* Rs */
+ | ((tinstr & 0x0007) << (12 - 0)); /* Rd */
+ }
+ break;
+ case 4: /* MOV */
+ case 5: /* CMP */
+ case 6: /* ADD */
+ case 7: /* SUB */
+ /* Format 3 */
+ {
+ ARMword subset[4] = {
+ 0xE3B00000, /* MOVS Rd,#imm8 */
+ 0xE3500000, /* CMP Rd,#imm8 */
+ 0xE2900000, /* ADDS Rd,Rd,#imm8 */
+ 0xE2500000, /* SUBS Rd,Rd,#imm8 */
+ };
+ *ainstr = subset[(tinstr & 0x1800) >> 11] /* base opcode */
+ | ((tinstr & 0x00FF) >> 0) /* imm8 */
+ | ((tinstr & 0x0700) << (16 - 8)) /* Rn */
+ | ((tinstr & 0x0700) << (12 - 8)); /* Rd */
+ }
+ break;
+ case 8: /* Arithmetic and high register transfers */
+ /* TODO: Since the subsets for both Format 4 and Format 5
+ instructions are made up of different ARM encodings, we could
+ save the following conditional, and just have one large
+ subset. */
+ if ((tinstr & (1 << 10)) == 0) {
+ /* Format 4 */
+ enum OpcodeType { t_norm, t_shift, t_neg, t_mul };
+ struct ThumbOpcode {
+ ARMword opcode;
+ OpcodeType otype;
+ };
+
+ ThumbOpcode subset[16] = {
+ {
+ 0xE0100000, t_norm
+ }, /* ANDS Rd,Rd,Rs */
+ {
+ 0xE0300000, t_norm
+ }, /* EORS Rd,Rd,Rs */
+ {
+ 0xE1B00010, t_shift
+ }, /* MOVS Rd,Rd,LSL Rs */
+ {
+ 0xE1B00030, t_shift
+ }, /* MOVS Rd,Rd,LSR Rs */
+ {
+ 0xE1B00050, t_shift
+ }, /* MOVS Rd,Rd,ASR Rs */
+ {
+ 0xE0B00000, t_norm
+ }, /* ADCS Rd,Rd,Rs */
+ {
+ 0xE0D00000, t_norm
+ }, /* SBCS Rd,Rd,Rs */
+ {
+ 0xE1B00070, t_shift
+ }, /* MOVS Rd,Rd,ROR Rs */
+ {
+ 0xE1100000, t_norm
+ }, /* TST Rd,Rs */
+ {
+ 0xE2700000, t_neg
+ }, /* RSBS Rd,Rs,#0 */
+ {
+ 0xE1500000, t_norm
+ }, /* CMP Rd,Rs */
+ {
+ 0xE1700000, t_norm
+ }, /* CMN Rd,Rs */
+ {
+ 0xE1900000, t_norm
+ }, /* ORRS Rd,Rd,Rs */
+ {
+ 0xE0100090, t_mul
+ }, /* MULS Rd,Rd,Rs */
+ {
+ 0xE1D00000, t_norm
+ }, /* BICS Rd,Rd,Rs */
+ {
+ 0xE1F00000, t_norm
+ } /* MVNS Rd,Rs */
+ };
+ *ainstr = subset[(tinstr & 0x03C0) >> 6].opcode; /* base */
+ switch (subset[(tinstr & 0x03C0) >> 6].otype) {
+ case t_norm:
+ *ainstr |= ((tinstr & 0x0007) << 16) /* Rn */
+ | ((tinstr & 0x0007) << 12) /* Rd */
+ | ((tinstr & 0x0038) >> 3); /* Rs */
+ break;
+ case t_shift:
+ *ainstr |= ((tinstr & 0x0007) << 12) /* Rd */
+ | ((tinstr & 0x0007) >> 0) /* Rm */
+ | ((tinstr & 0x0038) << (8 - 3)); /* Rs */
+ break;
+ case t_neg:
+ *ainstr |= ((tinstr & 0x0007) << 12) /* Rd */
+ | ((tinstr & 0x0038) << (16 - 3)); /* Rn */
+ break;
+ case t_mul:
+ *ainstr |= ((tinstr & 0x0007) << 16) /* Rd */
+ | ((tinstr & 0x0007) << 8) /* Rs */
+ | ((tinstr & 0x0038) >> 3); /* Rm */
+ break;
+ }
+ }
+ else {
+ /* Format 5 */
+ ARMword Rd = ((tinstr & 0x0007) >> 0);
+ ARMword Rs = ((tinstr & 0x0038) >> 3);
+ if (tinstr & (1 << 7))
+ Rd += 8;
+ if (tinstr & (1 << 6))
+ Rs += 8;
+ switch ((tinstr & 0x03C0) >> 6) {
+ case 0x0: /* ADD Rd,Rd,Hs */
+ case 0x1: /* ADD Rd,Rd,Hs */
+ case 0x2: /* ADD Hd,Hd,Rs */
+ case 0x3: /* ADD Hd,Hd,Hs */
+ *ainstr = 0xE0800000 /* base */
+ | (Rd << 16) /* Rn */
+ | (Rd << 12) /* Rd */
+ | (Rs << 0); /* Rm */
+ break;
+ case 0x4: /* CMP Rd,Hs */
+ case 0x5: /* CMP Rd,Hs */
+ case 0x6: /* CMP Hd,Rs */
+ case 0x7: /* CMP Hd,Hs */
+ *ainstr = 0xE1500000 /* base */
+ | (Rd << 16) /* Rn */
+ | (Rd << 12) /* Rd */
+ | (Rs << 0); /* Rm */
+ break;
+ case 0x8: /* MOV Rd,Hs */
+ case 0x9: /* MOV Rd,Hs */
+ case 0xA: /* MOV Hd,Rs */
+ case 0xB: /* MOV Hd,Hs */
+ *ainstr = 0xE1A00000 /* base */
+ | (Rd << 16) /* Rn */
+ | (Rd << 12) /* Rd */
+ | (Rs << 0); /* Rm */
+ break;
+ case 0xC: /* BX Rs */
+ case 0xD: /* BX Hs */
+ *ainstr = 0xE12FFF10 /* base */
+ | ((tinstr & 0x0078) >> 3); /* Rd */
+ break;
+ case 0xE: /* BLX */
+ case 0xF: /* BLX */
+ if (state->is_v5) {
+ *ainstr = 0xE1200030 /* base */
+ | (Rs << 0); /* Rm */
+ }
+ else {
+ valid = t_undefined;
+ }
+ break;
+ }
+ }
+ break;
+ case 9: /* LDR Rd,[PC,#imm8] */
+ /* Format 6 */
+ *ainstr = 0xE59F0000 /* base */
+ | ((tinstr & 0x0700) << (12 - 8)) /* Rd */
+ | ((tinstr & 0x00FF) << (2 - 0)); /* off8 */
+ break;
+ case 10:
+ case 11:
+ /* TODO: Format 7 and Format 8 perform the same ARM encoding, so
+ the following could be merged into a single subset, saving on
+ the following boolean: */
+ if ((tinstr & (1 << 9)) == 0) {
+ /* Format 7 */
+ ARMword subset[4] = {
+ 0xE7800000, /* STR Rd,[Rb,Ro] */
+ 0xE7C00000, /* STRB Rd,[Rb,Ro] */
+ 0xE7900000, /* LDR Rd,[Rb,Ro] */
+ 0xE7D00000 /* LDRB Rd,[Rb,Ro] */
+ };
+ *ainstr = subset[(tinstr & 0x0C00) >> 10] /* base */
+ | ((tinstr & 0x0007) << (12 - 0)) /* Rd */
+ | ((tinstr & 0x0038) << (16 - 3)) /* Rb */
+ | ((tinstr & 0x01C0) >> 6); /* Ro */
+ }
+ else {
+ /* Format 8 */
+ ARMword subset[4] = {
+ 0xE18000B0, /* STRH Rd,[Rb,Ro] */
+ 0xE19000D0, /* LDRSB Rd,[Rb,Ro] */
+ 0xE19000B0, /* LDRH Rd,[Rb,Ro] */
+ 0xE19000F0 /* LDRSH Rd,[Rb,Ro] */
+ };
+ *ainstr = subset[(tinstr & 0x0C00) >> 10] /* base */
+ | ((tinstr & 0x0007) << (12 - 0)) /* Rd */
+ | ((tinstr & 0x0038) << (16 - 3)) /* Rb */
+ | ((tinstr & 0x01C0) >> 6); /* Ro */
+ }
+ break;
+ case 12: /* STR Rd,[Rb,#imm5] */
+ case 13: /* LDR Rd,[Rb,#imm5] */
+ case 14: /* STRB Rd,[Rb,#imm5] */
+ case 15: /* LDRB Rd,[Rb,#imm5] */
+ /* Format 9 */
+ {
+ ARMword subset[4] = {
+ 0xE5800000, /* STR Rd,[Rb,#imm5] */
+ 0xE5900000, /* LDR Rd,[Rb,#imm5] */
+ 0xE5C00000, /* STRB Rd,[Rb,#imm5] */
+ 0xE5D00000 /* LDRB Rd,[Rb,#imm5] */
+ };
+ /* The offset range defends on whether we are transferring a
+ byte or word value: */
+ *ainstr = subset[(tinstr & 0x1800) >> 11] /* base */
+ | ((tinstr & 0x0007) << (12 - 0)) /* Rd */
+ | ((tinstr & 0x0038) << (16 - 3)) /* Rb */
+ | ((tinstr & 0x07C0) >> (6 - ((tinstr & (1 << 12)) ? 0 : 2))); /* off5 */
+ }
+ break;
+ case 16: /* STRH Rd,[Rb,#imm5] */
+ case 17: /* LDRH Rd,[Rb,#imm5] */
+ /* Format 10 */
+ *ainstr = ((tinstr & (1 << 11)) /* base */
+ ? 0xE1D000B0 /* LDRH */
+ : 0xE1C000B0) /* STRH */
+ | ((tinstr & 0x0007) << (12 - 0)) /* Rd */
+ | ((tinstr & 0x0038) << (16 - 3)) /* Rb */
+ | ((tinstr & 0x01C0) >> (6 - 1)) /* off5, low nibble */
+ | ((tinstr & 0x0600) >> (9 - 8)); /* off5, high nibble */
+ break;
+ case 18: /* STR Rd,[SP,#imm8] */
+ case 19: /* LDR Rd,[SP,#imm8] */
+ /* Format 11 */
+ *ainstr = ((tinstr & (1 << 11)) /* base */
+ ? 0xE59D0000 /* LDR */
+ : 0xE58D0000) /* STR */
+ | ((tinstr & 0x0700) << (12 - 8)) /* Rd */
+ | ((tinstr & 0x00FF) << 2); /* off8 */
+ break;
+ case 20: /* ADD Rd,PC,#imm8 */
+ case 21: /* ADD Rd,SP,#imm8 */
+ /* Format 12 */
+ if ((tinstr & (1 << 11)) == 0) {
+ /* NOTE: The PC value used here should by word aligned */
+ /* We encode shift-left-by-2 in the rotate immediate field,
+ so no shift of off8 is needed. */
+ *ainstr = 0xE28F0F00 /* base */
+ | ((tinstr & 0x0700) << (12 - 8)) /* Rd */
+ | (tinstr & 0x00FF); /* off8 */
+ }
+ else {
+ /* We encode shift-left-by-2 in the rotate immediate field,
+ so no shift of off8 is needed. */
+ *ainstr = 0xE28D0F00 /* base */
+ | ((tinstr & 0x0700) << (12 - 8)) /* Rd */
+ | (tinstr & 0x00FF); /* off8 */
+ }
+ break;
+ case 22:
+ case 23:
+ if ((tinstr & 0x0F00) == 0x0000) {
+ // NOTE: The instruction contains a shift left of 2 equivalent (implemented as ROR #30):
+ *ainstr = ((tinstr & (1 << 7)) // base
+ ? 0xE24DDF00 // SUB
+ : 0xE28DDF00) // ADD
+ | (tinstr & 0x007F); // off7
+ }
+ else if ((tinstr & 0x0F00) == 0x0e00)
+ *ainstr = 0xEF000000 | SWI_Breakpoint;
+ else {
+ if ((tinstr & 0x600) == 0x400)
+ {
+ /* Format 14 */
+ u32 subset[4] = {
+ 0xE92D0000, /* STMDB sp!,{rlist} */
+ 0xE92D4000, /* STMDB sp!,{rlist,lr} */
+ 0xE8BD0000, /* LDMIA sp!,{rlist} */
+ 0xE8BD8000 /* LDMIA sp!,{rlist,pc} */
+ };
+ *ainstr = subset[((tinstr & (1 << 11)) >> 10) | ((tinstr & (1 << 8)) >> 8)] /* base */
+ | (tinstr & 0x00FF); /* mask8 */
+ }
+ else
+ {
+ //e6bf1071 sxth r1, r1
+ //e6af1071 sxtb r1, r1
+ //e6ff1078 uxth r1, r8
+ //e6ef1078 uxtb r1, r8
+
+ u32 subset[4] = { //Bit 12 - 15 dest Bit 0 - 3 src
+ 0xe6ff0070, /* uxth */
+ 0xe6ef0070, /* uxtb */
+ 0xe6bf0070, /* sxth */
+ 0xe6af0070 /* sxtb */
+ };
+
+ if ((tinstr & 0xF00) == 0x200) //Bit(7) unsigned (set = sxt. cleared = uxt) Bit(6) byte (set = .xtb cleared = .xth) Bit 5-3 Rb src Bit 2-0 Rd dest
+ {
+ *ainstr = subset[((tinstr & (0x3 << 6)) >> 6)] |
+ (tinstr & 0x7) << 12 |
+ (tinstr & 0x38) >> 3;
+ }
+ else if ((tinstr & 0x0FC0) == 0x0A00){
+ u32 Destr = (tinstr & 0x7);
+ u32 srcr = ((tinstr >> 3) & 0x7);
+ *ainstr = 0xE6BF0F30 | srcr | (Destr << 12);
+
+ }
+ else
+ {
+ valid = t_undefined;
+ XDSERROR("unk thumb instr %04x", tinstr);
+ }
+
+ }
+ }
+ break;
+ case 24: /* STMIA */
+ case 25: /* LDMIA */
+ /* Format 15 */
+ {
+ u32 Rb = (tinstr & 0x0700) >> 8;
+ if ((1 << Rb)&tinstr) //no write back if the register is in the list
+ {
+ *ainstr = ((tinstr & (1 << 11)) /* base */
+ ? 0xE8900000 /* LDMIA */
+ : 0xE8800000) /* STMIA */
+ | ((tinstr & 0x0700) << (16 - 8)) /* Rb */
+ | (tinstr & 0x00FF); /* mask8 */
+ break;
+ }
+ else
+ {
+ *ainstr = ((tinstr & (1 << 11)) /* base */
+ ? 0xE8B00000 /* LDMIA */
+ : 0xE8A00000) /* STMIA */
+ | ((tinstr & 0x0700) << (16 - 8)) /* Rb */
+ | (tinstr & 0x00FF); /* mask8 */
+ break;
+ }
+ }
+ case 26: /* Bcc */
+ case 27: /* Bcc/SWI */
+ if ((tinstr & 0x0F00) == 0x0F00) {
+ if (tinstr == (ARMul_ABORTWORD & 0xffff) &&
+ state->AbortAddr == pc) {
+ *ainstr = ARMul_ABORTWORD;
+ break;
+ }
+ /* Format 17 : SWI */
+ *ainstr = 0xEF000000;
+ /* Breakpoint must be handled specially. */
+ if ((tinstr & 0x00FF) == 0x18)
+ *ainstr |= ((tinstr & 0x00FF) << 16);
+ /* New breakpoint value. See gdb/arm-tdep.c */
+ else if ((tinstr & 0x00FF) == 0xFE)
+ *ainstr |= SWI_Breakpoint;
+ else
+ *ainstr |= (tinstr & 0x00FF);
+ }
+ else if ((tinstr & 0x0F00) != 0x0E00) {
+ /* Format 16 */
+ int doit = FALSE;
+ /* TODO: Since we are doing a switch here, we could just add
+ the SWI and undefined instruction checks into this
+ switch to same on a couple of conditionals: */
+ switch ((tinstr & 0x0F00) >> 8) {
+ case EQ:
+ doit = ZFLAG;
+ break;
+ case NE:
+ doit = !ZFLAG;
+ break;
+ case VS:
+ doit = VFLAG;
+ break;
+ case VC:
+ doit = !VFLAG;
+ break;
+ case MI:
+ doit = NFLAG;
+ break;
+ case PL:
+ doit = !NFLAG;
+ break;
+ case CS:
+ doit = CFLAG;
+ break;
+ case CC:
+ doit = !CFLAG;
+ break;
+ case HI:
+ doit = (CFLAG && !ZFLAG);
+ break;
+ case LS:
+ doit = (!CFLAG || ZFLAG);
+ break;
+ case GE:
+ doit = ((!NFLAG && !VFLAG)
+ || (NFLAG && VFLAG));
+ break;
+ case LT:
+ doit = ((NFLAG && !VFLAG)
+ || (!NFLAG && VFLAG));
+ break;
+ case GT:
+ doit = ((!NFLAG && !VFLAG && !ZFLAG)
+ || (NFLAG && VFLAG && !ZFLAG));
+ break;
+ case LE:
+ doit = ((NFLAG && !VFLAG)
+ || (!NFLAG && VFLAG)) || ZFLAG;
+ break;
+ }
+ if (doit) {
+ state->Reg[15] = (pc + 4
+ + (((tinstr & 0x7F) << 1)
+ | ((tinstr & (1 << 7)) ?
+ 0xFFFFFF00 : 0)));
+ FLUSHPIPE;
+ }
+ valid = t_branch;
+ }
+ else /* UNDEFINED : cc=1110(AL) uses different format */
+ valid = t_undefined;
+ break;
+ case 28: /* B */
+ /* Format 18 */
+ state->Reg[15] = (pc + 4 + (((tinstr & 0x3FF) << 1)
+ | ((tinstr & (1 << 10)) ?
+ 0xFFFFF800 : 0)));
+ FLUSHPIPE;
+ valid = t_branch;
+ break;
+ case 29:
+ if (tinstr & 0x1)
+ valid = t_undefined;
+ else{
+ /* BLX 1 for armv5t and above */
+ ARMword tmp = (pc + 2);
+ state->Reg[15] =
+ (state->Reg[14] + ((tinstr & 0x07FF) << 1)) & 0xFFFFFFFC;
+ state->Reg[14] = (tmp | 1);
+ CLEART;
+ LOG("After BLX(1),LR=0x%x,PC=0x%x, offset=0x%x", state->Reg[14], state->Reg[15], (tinstr & 0x7FF) << 1);
+ valid = t_branch;
+ FLUSHPIPE;
+ }
+ break;
+ case 30: /* BL instruction 1 */
+ /* Format 19 */
+ /* There is no single ARM instruction equivalent for this Thumb
+ instruction. To keep the simulation simple (from the user
+ perspective) we check if the following instruction is the
+ second half of this BL, and if it is we simulate it
+ immediately. */
+ state->Reg[14] = state->Reg[15]
+ + (((tinstr & 0x07FF) << 12)
+ | ((tinstr & (1 << 10)) ? 0xFF800000 : 0));
+ valid = t_branch; /* in-case we don't have the 2nd half */
+ //tinstr = next_instr; /* move the instruction down */
+ //if (((tinstr & 0xF800) >> 11) != 31)
+ // break; /* exit, since not correct instruction */
+ /* else we fall through to process the second half of the BL */
+ //pc += 2; /* point the pc at the 2nd half */
+ state->Reg[15] = pc + 2;
+ FLUSHPIPE;
+ break;
+ case 31: /* BL instruction 2 */
+ /* Format 19 */
+ /* There is no single ARM instruction equivalent for this
+ instruction. Also, it should only ever be matched with the
+ fmt19 "BL instruction 1" instruction. However, we do allow
+ the simulation of it on its own, with undefined results if
+ r14 is not suitably initialised. */
+ {
+ ARMword tmp = (pc + 2);
+ state->Reg[15] =
+ (state->Reg[14] + ((tinstr & 0x07FF) << 1));
+ state->Reg[14] = (tmp | 1);
+ valid = t_branch;
+ FLUSHPIPE;
+ }
+ break;
+ }
+
+ return valid;
+}
--- /dev/null
+class Memory {
+public:
+ static void Write8(u32 addr, u8 val) {
+
+ }
+ static void Write16(u32 addr, u16 val) {
+
+ }
+ static void Write32(u32 addr, u32 val) {
+
+ }
+ static u8 Read8(u32 addr) {
+ return 0;
+ }
+ static u16 Read16(u32 addr) {
+ return 0;
+ }
+ static u32 Read32(u32 addr) {
+ return 0;
+ }
+};
+
+class HLE {
+public:
+ static void CallSVC(u32 i) {
+
+ }
+};
--- /dev/null
+#ifndef __ARM_REGFORMAT_H__
+#define __ARM_REGFORMAT_H__
+
+enum {
+ R0 = 0,
+ R1,
+ R2,
+ R3,
+ R4,
+ R5,
+ R6,
+ R7,
+ R8,
+ R9,
+ R10,
+ R11,
+ R12,
+ R13,
+ LR,
+ R15, //PC,
+ CPSR_REG,
+ SPSR_REG,
+
+ PHYS_PC,
+ R13_USR,
+ R14_USR,
+ R13_SVC,
+ R14_SVC,
+ R13_ABORT,
+ R14_ABORT,
+ R13_UNDEF,
+ R14_UNDEF,
+ R13_IRQ,
+ R14_IRQ,
+ R8_FIRQ,
+ R9_FIRQ,
+ R10_FIRQ,
+ R11_FIRQ,
+ R12_FIRQ,
+ R13_FIRQ,
+ R14_FIRQ,
+ SPSR_INVALID1,
+ SPSR_INVALID2,
+ SPSR_SVC,
+ SPSR_ABORT,
+ SPSR_UNDEF,
+ SPSR_IRQ,
+ SPSR_FIRQ,
+ MODE_REG, /* That is the cpsr[4 : 0], just for calculation easily */
+ BANK_REG,
+ EXCLUSIVE_TAG,
+ EXCLUSIVE_STATE,
+ EXCLUSIVE_RESULT,
+
+ // c0 - Information registers
+ CP15_BASE,
+ CP15_C0 = CP15_BASE,
+ CP15_C0_C0 = CP15_C0,
+ CP15_MAIN_ID = CP15_C0_C0,
+ CP15_CACHE_TYPE,
+ CP15_TCM_STATUS,
+ CP15_TLB_TYPE,
+ CP15_CPU_ID,
+ CP15_C0_C1,
+ CP15_PROCESSOR_FEATURE_0 = CP15_C0_C1,
+ CP15_PROCESSOR_FEATURE_1,
+ CP15_DEBUG_FEATURE_0,
+ CP15_AUXILIARY_FEATURE_0,
+ CP15_MEMORY_MODEL_FEATURE_0,
+ CP15_MEMORY_MODEL_FEATURE_1,
+ CP15_MEMORY_MODEL_FEATURE_2,
+ CP15_MEMORY_MODEL_FEATURE_3,
+ CP15_C0_C2,
+ CP15_ISA_FEATURE_0 = CP15_C0_C2,
+ CP15_ISA_FEATURE_1,
+ CP15_ISA_FEATURE_2,
+ CP15_ISA_FEATURE_3,
+ CP15_ISA_FEATURE_4,
+
+ // c1 - Control registers
+ CP15_C1_C0,
+ CP15_CONTROL = CP15_C1_C0,
+ CP15_AUXILIARY_CONTROL,
+ CP15_COPROCESSOR_ACCESS_CONTROL,
+
+ // c2 - Translation table registers
+ CP15_C2,
+ CP15_C2_C0 = CP15_C2,
+ CP15_TRANSLATION_BASE = CP15_C2_C0,
+ CP15_TRANSLATION_BASE_TABLE_0 = CP15_TRANSLATION_BASE,
+ CP15_TRANSLATION_BASE_TABLE_1,
+ CP15_TRANSLATION_BASE_CONTROL,
+ CP15_DOMAIN_ACCESS_CONTROL,
+ CP15_RESERVED,
+
+ // c5 - Fault status registers
+ CP15_FAULT_STATUS,
+ CP15_INSTR_FAULT_STATUS,
+ CP15_COMBINED_DATA_FSR = CP15_FAULT_STATUS,
+ CP15_INST_FSR,
+
+ // c6 - Fault Address registers
+ CP15_FAULT_ADDRESS,
+ CP15_COMBINED_DATA_FAR = CP15_FAULT_ADDRESS,
+ CP15_WFAR,
+ CP15_IFAR,
+
+ // c7 - Cache operation registers
+ CP15_WAIT_FOR_INTERRUPT,
+ CP15_PHYS_ADDRESS,
+ CP15_INVALIDATE_INSTR_CACHE,
+ CP15_INVALIDATE_INSTR_CACHE_USING_MVA,
+ CP15_INVALIDATE_INSTR_CACHE_USING_INDEX,
+ CP15_FLUSH_PREFETCH_BUFFER,
+ CP15_FLUSH_BRANCH_TARGET_CACHE,
+ CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY,
+ CP15_INVALIDATE_DATA_CACHE,
+ CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
+ CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
+ CP15_INVALIDATE_DATA_AND_INSTR_CACHE,
+ CP15_CLEAN_DATA_CACHE,
+ CP15_CLEAN_DATA_CACHE_LINE_USING_MVA,
+ CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX,
+ CP15_DATA_SYNC_BARRIER,
+ CP15_DATA_MEMORY_BARRIER,
+ CP15_CLEAN_AND_INVALIDATE_DATA_CACHE,
+ CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
+ CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
+
+ // c8 - TLB operations
+ CP15_INVALIDATE_ITLB,
+ CP15_INVALIDATE_ITLB_SINGLE_ENTRY,
+ CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH,
+ CP15_INVALIDATE_ITLB_ENTRY_ON_MVA,
+ CP15_INVALIDATE_DTLB,
+ CP15_INVALIDATE_DTLB_SINGLE_ENTRY,
+ CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH,
+ CP15_INVALIDATE_DTLB_ENTRY_ON_MVA,
+ CP15_INVALIDATE_UTLB,
+ CP15_INVALIDATE_UTLB_SINGLE_ENTRY,
+ CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH,
+ CP15_INVALIDATE_UTLB_ENTRY_ON_MVA,
+
+ // c9 - Data cache lockdown register
+ CP15_DATA_CACHE_LOCKDOWN,
+
+ // c10 - TLB/Memory map registers
+ CP15_TLB_LOCKDOWN,
+ CP15_PRIMARY_REGION_REMAP,
+ CP15_NORMAL_REGION_REMAP,
+
+ // c13 - Thread related registers
+ CP15_PID,
+ CP15_CONTEXT_ID,
+ CP15_THREAD_UPRW, // Thread ID register - User/Privileged Read/Write
+ CP15_THREAD_URO, // Thread ID register - User Read Only (Privileged R/W)
+ CP15_THREAD_PRW, // Thread ID register - Privileged R/W only.
+
+ // c15 - Performance and TLB lockdown registers
+ CP15_PERFORMANCE_MONITOR_CONTROL,
+ CP15_CYCLE_COUNTER,
+ CP15_COUNT_0,
+ CP15_COUNT_1,
+ CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY,
+ CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY,
+ CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS,
+ CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS,
+ CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE,
+ CP15_TLB_DEBUG_CONTROL,
+
+ // Skyeye defined
+ CP15_TLB_FAULT_ADDR,
+ CP15_TLB_FAULT_STATUS,
+
+ // VFP registers
+ VFP_BASE,
+ VFP_FPSID = VFP_BASE,
+ VFP_FPSCR,
+ VFP_FPEXC,
+
+ MAX_REG_NUM,
+};
+
+#define CP15(idx) (idx - CP15_BASE)
+#define VFP_OFFSET(x) (x - VFP_BASE)
+
+
+#endif
--- /dev/null
+/*
+ * arm
+ * armcpu.h
+ *
+ * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ARM_CPU_H__
+#define __ARM_CPU_H__
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "arm/skyeye_common/armdefs.h"
+
+typedef struct ARM_CPU_State_s {
+ ARMul_State * core;
+ uint32_t core_num;
+ /* The core id that boot from
+ */
+ uint32_t boot_core_id;
+}ARM_CPU_State;
+
+//static ARM_CPU_State* get_current_cpu(){
+// machine_config_t* mach = get_current_mach();
+// /* Casting a conf_obj_t to ARM_CPU_State type */
+// ARM_CPU_State* cpu = (ARM_CPU_State*)mach->cpu_data->obj;
+//
+// return cpu;
+//}
+
+/**
+* @brief Get the core instance boot from
+*
+* @return
+*/
+//static ARMul_State* get_boot_core(){
+// ARM_CPU_State* cpu = get_current_cpu();
+// return &cpu->core[cpu->boot_core_id];
+//}
+/**
+* @brief Get the instance of running core
+*
+* @return the core instance
+*/
+//static ARMul_State* get_current_core(){
+// /* Casting a conf_obj_t to ARM_CPU_State type */
+// int id = Common::CurrentThreadId();
+// /* If thread is not in running mode, we should give the boot core */
+// if(get_thread_state(id) != Running_state){
+// return get_boot_core();
+// }
+// /* Judge if we are running in paralell or sequenial */
+// if(thread_exist(id)){
+// conf_object_t* conf_obj = get_current_exec_priv(id);
+// return (ARMul_State*)get_cast_conf_obj(conf_obj, "arm_core_t");
+// }
+//
+// return NULL;
+//}
+
+#define CURRENT_CORE get_current_core()
+
+#endif
+
--- /dev/null
+/* armdefs.h -- ARMulator common definitions: ARM6 Instruction Emulator.
+ Copyright (C) 1994 Advanced RISC Machines Ltd.
+
+ 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 of the License, 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 _ARMDEFS_H_
+#define _ARMDEFS_H_
+
+#include <unordered_map>
+#include <cerrno>
+#include <csignal>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "arm_regformat.h"
+#include "Common.h"
+#include "arm/skyeye_common/armmmu.h"
+#include "arm/skyeye_common/skyeye_defs.h"
+
+#if EMU_PLATFORM == PLATFORM_LINUX
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#if 0
+#if 0
+#define DIFF_STATE 1
+#define __FOLLOW_MODE__ 0
+#else
+#define DIFF_STATE 0
+#define __FOLLOW_MODE__ 1
+#endif
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE 1
+#endif
+
+#define LOW 0
+#define HIGH 1
+#define LOWHIGH 1
+#define HIGHLOW 2
+
+//#define DBCT_TEST_SPEED
+#define DBCT_TEST_SPEED_SEC 10
+
+#define ARM_BYTE_TYPE 0
+#define ARM_HALFWORD_TYPE 1
+#define ARM_WORD_TYPE 2
+
+//the define of cachetype
+#define NONCACHE 0
+#define DATACACHE 1
+#define INSTCACHE 2
+
+#define POS(i) ( (~(i)) >> 31 )
+#define NEG(i) ( (i) >> 31 )
+
+#ifndef __STDC__
+typedef char *VoidStar;
+#endif
+
+typedef u64 ARMdword; // must be 64 bits wide
+typedef u32 ARMword; // must be 32 bits wide
+typedef u16 ARMhword; // must be 16 bits wide
+typedef u8 ARMbyte; // must be 8 bits wide
+typedef struct ARMul_State ARMul_State;
+typedef struct ARMul_io ARMul_io;
+typedef struct ARMul_Energy ARMul_Energy;
+
+
+typedef unsigned ARMul_CPInits(ARMul_State* state);
+typedef unsigned ARMul_CPExits(ARMul_State* state);
+typedef unsigned ARMul_LDCs(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
+typedef unsigned ARMul_STCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
+typedef unsigned ARMul_MRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
+typedef unsigned ARMul_MCRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
+typedef unsigned ARMul_MRRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value1, ARMword* value2);
+typedef unsigned ARMul_MCRRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value1, ARMword value2);
+typedef unsigned ARMul_CDPs(ARMul_State* state, unsigned type, ARMword instr);
+typedef unsigned ARMul_CPReads(ARMul_State* state, unsigned reg, ARMword* value);
+typedef unsigned ARMul_CPWrites(ARMul_State* state, unsigned reg, ARMword value);
+
+
+//added by ksh,2004-3-5
+struct ARMul_io
+{
+ ARMword *instr; // to display the current interrupt state
+ ARMword *net_flag; // to judge if network is enabled
+ ARMword *net_int; // netcard interrupt
+
+ //ywc,2004-04-01
+ ARMword *ts_int;
+ ARMword *ts_is_enable;
+ ARMword *ts_addr_begin;
+ ARMword *ts_addr_end;
+ ARMword *ts_buffer;
+};
+
+/* added by ksh,2004-11-26,some energy profiling */
+struct ARMul_Energy
+{
+ int energy_prof; /* <tktan> BUG200103282109 : for energy profiling */
+ int enable_func_energy; /* <tktan> BUG200105181702 */
+ char *func_energy;
+ int func_display; /* <tktan> BUG200103311509 : for function call display */
+ int func_disp_start; /* <tktan> BUG200104191428 : to start func profiling */
+ char *start_func; /* <tktan> BUG200104191428 */
+
+ FILE *outfile; /* <tktan> BUG200105201531 : direct console to file */
+ long long tcycle, pcycle;
+ float t_energy;
+ void *cur_task; /* <tktan> BUG200103291737 */
+ long long t_mem_cycle, t_idle_cycle, t_uart_cycle;
+ long long p_mem_cycle, p_idle_cycle, p_uart_cycle;
+ long long p_io_update_tcycle;
+ /*record CCCR,to get current core frequency */
+ ARMword cccr;
+};
+#if 0
+#define MAX_BANK 8
+#define MAX_STR 1024
+
+typedef struct mem_bank
+{
+ ARMword (*read_byte) (ARMul_State* state, ARMword addr);
+ void (*write_byte) (ARMul_State* state, ARMword addr, ARMword data);
+ ARMword (*read_halfword) (ARMul_State* state, ARMword addr);
+ void (*write_halfword) (ARMul_State* state, ARMword addr, ARMword data);
+ ARMword (*read_word) (ARMul_State* state, ARMword addr);
+ void (*write_word) (ARMul_State* state, ARMword addr, ARMword data);
+ unsigned int addr, len;
+ char filename[MAX_STR];
+ unsigned type; //chy 2003-09-21: maybe io,ram,rom
+} mem_bank_t;
+typedef struct
+{
+ int bank_num;
+ int current_num; /*current num of bank */
+ mem_bank_t mem_banks[MAX_BANK];
+} mem_config_t;
+#endif
+#define VFP_REG_NUM 64
+#define bb_map std::unordered_map<u32, int>
+
+struct ARMul_State
+{
+ ARMword Emulate; /* to start and stop emulation */
+ unsigned EndCondition; /* reason for stopping */
+ unsigned ErrorCode; /* type of illegal instruction */
+
+ /* Order of the following register should not be modified */
+ ARMword Reg[16]; /* the current register file */
+ ARMword Cpsr; /* the current psr */
+ ARMword Spsr_copy;
+ ARMword phys_pc;
+ ARMword Reg_usr[2];
+ ARMword Reg_svc[2]; /* R13_SVC R14_SVC */
+ ARMword Reg_abort[2]; /* R13_ABORT R14_ABORT */
+ ARMword Reg_undef[2]; /* R13 UNDEF R14 UNDEF */
+ ARMword Reg_irq[2]; /* R13_IRQ R14_IRQ */
+ ARMword Reg_firq[7]; /* R8---R14 FIRQ */
+ ARMword Spsr[7]; /* the exception psr's */
+ ARMword Mode; /* the current mode */
+ ARMword Bank; /* the current register bank */
+ ARMword exclusive_tag; /* the address for which the local monitor is in exclusive access mode */
+ ARMword exclusive_state;
+ ARMword exclusive_result;
+ ARMword CP15[VFP_BASE - CP15_BASE];
+ ARMword VFP[3]; /* FPSID, FPSCR, and FPEXC */
+ /* VFPv2 and VFPv3-D16 has 16 doubleword registers (D0-D16 or S0-S31).
+ VFPv3-D32/ASIMD may have up to 32 doubleword registers (D0-D31),
+ and only 32 singleword registers are accessible (S0-S31). */
+ ARMword ExtReg[VFP_REG_NUM];
+ /* ---- End of the ordered registers ---- */
+
+ ARMword RegBank[7][16]; /* all the registers */
+ //chy:2003-08-19, used in arm xscale
+ /* 40 bit accumulator. We always keep this 64 bits wide,
+ and move only 40 bits out of it in an MRA insn. */
+ ARMdword Accumulator;
+
+ ARMword NFlag, ZFlag, CFlag, VFlag, IFFlags; /* dummy flags for speed */
+ unsigned long long int icounter, debug_icounter, kernel_icounter;
+ unsigned int shifter_carry_out;
+ //ARMword translate_pc;
+
+ /* add armv6 flags dyf:2010-08-09 */
+ ARMword GEFlag, EFlag, AFlag, QFlag;
+ //chy:2003-08-19, used in arm v5e|xscale
+ ARMword SFlag;
+#ifdef MODET
+ ARMword TFlag; /* Thumb state */
+#endif
+ ARMword instr, pc, temp; /* saved register state */
+ ARMword loaded, decoded; /* saved pipeline state */
+ //chy 2006-04-12 for ICE breakpoint
+ ARMword loaded_addr, decoded_addr; /* saved pipeline state addr*/
+ unsigned int NumScycles, NumNcycles, NumIcycles, NumCcycles, NumFcycles; /* emulated cycles used */
+ unsigned long long NumInstrs; /* the number of instructions executed */
+ unsigned NumInstrsToExecute;
+
+ ARMword currentexaddr;
+ ARMword currentexval;
+ ARMword currentexvald;
+ ARMword servaddr;
+
+ unsigned NextInstr;
+ unsigned VectorCatch; /* caught exception mask */
+ unsigned CallDebug; /* set to call the debugger */
+ unsigned CanWatch; /* set by memory interface if its willing to suffer the
+ overhead of checking for watchpoints on each memory
+ access */
+ unsigned int StopHandle;
+
+ char *CommandLine; /* Command Line from ARMsd */
+
+ ARMul_CPInits *CPInit[16]; /* coprocessor initialisers */
+ ARMul_CPExits *CPExit[16]; /* coprocessor finalisers */
+ ARMul_LDCs *LDC[16]; /* LDC instruction */
+ ARMul_STCs *STC[16]; /* STC instruction */
+ ARMul_MRCs *MRC[16]; /* MRC instruction */
+ ARMul_MCRs *MCR[16]; /* MCR instruction */
+ ARMul_MRRCs *MRRC[16]; /* MRRC instruction */
+ ARMul_MCRRs *MCRR[16]; /* MCRR instruction */
+ ARMul_CDPs *CDP[16]; /* CDP instruction */
+ ARMul_CPReads *CPRead[16]; /* Read CP register */
+ ARMul_CPWrites *CPWrite[16]; /* Write CP register */
+ unsigned char *CPData[16]; /* Coprocessor data */
+ unsigned char const *CPRegWords[16]; /* map of coprocessor register sizes */
+
+ unsigned EventSet; /* the number of events in the queue */
+ unsigned int Now; /* time to the nearest cycle */
+ struct EventNode **EventPtr; /* the event list */
+
+ unsigned Debug; /* show instructions as they are executed */
+ unsigned NresetSig; /* reset the processor */
+ unsigned NfiqSig;
+ unsigned NirqSig;
+
+ unsigned abortSig;
+ unsigned NtransSig;
+ unsigned bigendSig;
+ unsigned prog32Sig;
+ unsigned data32Sig;
+ unsigned syscallSig;
+
+/* 2004-05-09 chy
+----------------------------------------------------------
+read ARM Architecture Reference Manual
+2.6.5 Data Abort
+There are three Abort Model in ARM arch.
+
+Early Abort Model: used in some ARMv3 and earlier implementations. In this
+model, base register wirteback occurred for LDC,LDM,STC,STM instructions, and
+the base register was unchanged for all other instructions. (oldest)
+
+Base Restored Abort Model: If a Data Abort occurs in an instruction which
+specifies base register writeback, the value in the base register is
+unchanged. (strongarm, xscale)
+
+Base Updated Abort Model: If a Data Abort occurs in an instruction which
+specifies base register writeback, the base register writeback still occurs.
+(arm720T)
+
+read PART B
+chap2 The System Control Coprocessor CP15
+2.4 Register1:control register
+L(bit 6): in some ARMv3 and earlier implementations, the abort model of the
+processor could be configured:
+0=early Abort Model Selected(now obsolete)
+1=Late Abort Model selceted(same as Base Updated Abort Model)
+
+on later processors, this bit reads as 1 and ignores writes.
+-------------------------------------------------------------
+So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
+ if lateabtSig=0, then it means Base Restored Abort Model
+*/
+ unsigned lateabtSig;
+
+ ARMword Vector; /* synthesize aborts in cycle modes */
+ ARMword Aborted; /* sticky flag for aborts */
+ ARMword Reseted; /* sticky flag for Reset */
+ ARMword Inted, LastInted; /* sticky flags for interrupts */
+ ARMword Base; /* extra hand for base writeback */
+ ARMword AbortAddr; /* to keep track of Prefetch aborts */
+
+ const struct Dbg_HostosInterface *hostif;
+
+ int verbose; /* non-zero means print various messages like the banner */
+
+ int mmu_inited;
+ //mem_state_t mem;
+ /*remove io_state to skyeye_mach_*.c files */
+ //io_state_t io;
+ /* point to a interrupt pending register. now for skyeye-ne2k.c
+ * later should move somewhere. e.g machine_config_t*/
+
+
+ //chy: 2003-08-11, for different arm core type
+ unsigned is_v4; /* Are we emulating a v4 architecture (or higher) ? */
+ unsigned is_v5; /* Are we emulating a v5 architecture ? */
+ unsigned is_v5e; /* Are we emulating a v5e architecture ? */
+ unsigned is_v6; /* Are we emulating a v6 architecture ? */
+ unsigned is_v7; /* Are we emulating a v7 architecture ? */
+ unsigned is_XScale; /* Are we emulating an XScale architecture ? */
+ unsigned is_iWMMXt; /* Are we emulating an iWMMXt co-processor ? */
+ unsigned is_ep9312; /* Are we emulating a Cirrus Maverick co-processor ? */
+ //chy 2005-09-19
+ unsigned is_pxa27x; /* Are we emulating a Intel PXA27x co-processor ? */
+ //chy: seems only used in xscale's CP14
+ unsigned int LastTime; /* Value of last call to ARMul_Time() */
+ ARMword CP14R0_CCD; /* used to count 64 clock cycles with CP14 R0 bit 3 set */
+
+
+ //added by ksh:for handle different machs io 2004-3-5
+ ARMul_io mach_io;
+
+ /*added by ksh,2004-11-26,some energy profiling*/
+ ARMul_Energy energy;
+
+ //teawater add for next_dis 2004.10.27-----------------------
+ int disassemble;
+
+
+ //teawater add for arm2x86 2005.02.15-------------------------------------------
+ u32 trap;
+ u32 tea_break_addr;
+ u32 tea_break_ok;
+ int tea_pc;
+
+ //teawater add for arm2x86 2005.07.05-------------------------------------------
+ //arm_arm A2-18
+ int abort_model; //0 Base Restored Abort Model, 1 the Early Abort Model, 2 Base Updated Abort Model
+
+ //teawater change for return if running tb dirty 2005.07.09---------------------
+ void *tb_now;
+
+
+ //teawater add for record reg value to ./reg.txt 2005.07.10---------------------
+ FILE *tea_reg_fd;
+
+
+ /*added by ksh in 2005-10-1*/
+ cpu_config_t *cpu;
+ //mem_config_t *mem_bank;
+
+ /* added LPC remap function */
+ int vector_remap_flag;
+ u32 vector_remap_addr;
+ u32 vector_remap_size;
+
+ u32 step;
+ u32 cycle;
+ int stop_simulator;
+ conf_object_t *dyncom_cpu;
+//teawater add DBCT_TEST_SPEED 2005.10.04---------------------------------------
+#ifdef DBCT_TEST_SPEED
+ uint64_t instr_count;
+#endif //DBCT_TEST_SPEED
+// FILE * state_log;
+//diff log
+//#if DIFF_STATE
+ FILE * state_log;
+//#endif
+ /* monitored memory for exclusice access */
+ ARMword exclusive_tag_array[128];
+ /* 1 means exclusive access and 0 means open access */
+ ARMword exclusive_access_state;
+
+ memory_space_intf space;
+ u32 CurrInstr;
+ u32 last_pc; /* the last pc executed */
+ u32 last_instr; /* the last inst executed */
+ u32 WriteAddr[17];
+ u32 WriteData[17];
+ u32 WritePc[17];
+ u32 CurrWrite;
+
+ //memory map
+ KMemoryMap * m_MemoryMap;
+ KThread * m_currentThread;
+
+ //ichfly fixes
+ char *inst_buf;
+ int inst_bufftop;
+ int inst_buffsize;
+ bb_map *CreamCache;
+};
+#define DIFF_WRITE 0
+
+typedef ARMul_State arm_core_t;
+#define ResetPin NresetSig
+#define FIQPin NfiqSig
+#define IRQPin NirqSig
+#define AbortPin abortSig
+#define TransPin NtransSig
+#define BigEndPin bigendSig
+#define Prog32Pin prog32Sig
+#define Data32Pin data32Sig
+#define LateAbortPin lateabtSig
+
+/***************************************************************************\
+* Types of ARM we know about *
+\***************************************************************************/
+
+/* The bitflags */
+#define ARM_Fix26_Prop 0x01
+#define ARM_Nexec_Prop 0x02
+#define ARM_Debug_Prop 0x10
+#define ARM_Isync_Prop ARM_Debug_Prop
+#define ARM_Lock_Prop 0x20
+#define ARM_v4_Prop 0x40
+#define ARM_v5_Prop 0x80
+#define ARM_v6_Prop 0xc0
+
+#define ARM_v5e_Prop 0x100
+#define ARM_XScale_Prop 0x200
+#define ARM_ep9312_Prop 0x400
+#define ARM_iWMMXt_Prop 0x800
+#define ARM_PXA27X_Prop 0x1000
+#define ARM_v7_Prop 0x2000
+
+/* ARM2 family */
+#define ARM2 (ARM_Fix26_Prop)
+#define ARM2as ARM2
+#define ARM61 ARM2
+#define ARM3 ARM2
+
+#ifdef ARM60 /* previous definition in armopts.h */
+#undef ARM60
+#endif
+
+/* ARM6 family */
+#define ARM6 (ARM_Lock_Prop)
+#define ARM60 ARM6
+#define ARM600 ARM6
+#define ARM610 ARM6
+#define ARM620 ARM6
+
+
+/***************************************************************************\
+* Macros to extract instruction fields *
+\***************************************************************************/
+
+#define BIT(n) ( (ARMword)(instr>>(n))&1) /* bit n of instruction */
+#define BITS(m,n) ( (ARMword)(instr<<(31-(n))) >> ((31-(n))+(m)) ) /* bits m to n of instr */
+#define TOPBITS(n) (instr >> (n)) /* bits 31 to n of instr */
+
+/***************************************************************************\
+* The hardware vector addresses *
+\***************************************************************************/
+
+#define ARMResetV 0L
+#define ARMUndefinedInstrV 4L
+#define ARMSWIV 8L
+#define ARMPrefetchAbortV 12L
+#define ARMDataAbortV 16L
+#define ARMAddrExceptnV 20L
+#define ARMIRQV 24L
+#define ARMFIQV 28L
+#define ARMErrorV 32L /* This is an offset, not an address ! */
+
+#define ARMul_ResetV ARMResetV
+#define ARMul_UndefinedInstrV ARMUndefinedInstrV
+#define ARMul_SWIV ARMSWIV
+#define ARMul_PrefetchAbortV ARMPrefetchAbortV
+#define ARMul_DataAbortV ARMDataAbortV
+#define ARMul_AddrExceptnV ARMAddrExceptnV
+#define ARMul_IRQV ARMIRQV
+#define ARMul_FIQV ARMFIQV
+
+/***************************************************************************\
+* Mode and Bank Constants *
+\***************************************************************************/
+
+#define USER26MODE 0L
+#define FIQ26MODE 1L
+#define IRQ26MODE 2L
+#define SVC26MODE 3L
+#define USER32MODE 16L
+#define FIQ32MODE 17L
+#define IRQ32MODE 18L
+#define SVC32MODE 19L
+#define ABORT32MODE 23L
+#define UNDEF32MODE 27L
+//chy 2006-02-15 add system32 mode
+#define SYSTEM32MODE 31L
+
+#define ARM32BITMODE (state->Mode > 3)
+#define ARM26BITMODE (state->Mode <= 3)
+#define ARMMODE (state->Mode)
+#define ARMul_MODEBITS 0x1fL
+#define ARMul_MODE32BIT ARM32BITMODE
+#define ARMul_MODE26BIT ARM26BITMODE
+
+#define USERBANK 0
+#define FIQBANK 1
+#define IRQBANK 2
+#define SVCBANK 3
+#define ABORTBANK 4
+#define UNDEFBANK 5
+#define DUMMYBANK 6
+#define SYSTEMBANK USERBANK
+#define BANK_CAN_ACCESS_SPSR(bank) \
+ ((bank) != USERBANK && (bank) != SYSTEMBANK && (bank) != DUMMYBANK)
+
+
+/***************************************************************************\
+* Definitons of things in the emulator *
+\***************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void ARMul_EmulateInit();
+extern void ARMul_Reset(ARMul_State* state);
+#ifdef __cplusplus
+ }
+#endif
+extern ARMul_State *ARMul_NewState(ARMul_State* state);
+extern ARMword ARMul_DoProg(ARMul_State* state);
+extern ARMword ARMul_DoInstr(ARMul_State* state);
+/***************************************************************************\
+* Definitons of things for event handling *
+\***************************************************************************/
+
+extern void ARMul_ScheduleEvent(ARMul_State* state, unsigned int delay, unsigned(*func) ());
+extern void ARMul_EnvokeEvent(ARMul_State* state);
+extern unsigned int ARMul_Time(ARMul_State* state);
+
+/***************************************************************************\
+* Useful support routines *
+\***************************************************************************/
+
+extern ARMword ARMul_GetReg (ARMul_State* state, unsigned mode, unsigned reg);
+extern void ARMul_SetReg (ARMul_State* state, unsigned mode, unsigned reg, ARMword value);
+extern ARMword ARMul_GetPC(ARMul_State* state);
+extern ARMword ARMul_GetNextPC(ARMul_State* state);
+extern void ARMul_SetPC(ARMul_State* state, ARMword value);
+extern ARMword ARMul_GetR15(ARMul_State* state);
+extern void ARMul_SetR15(ARMul_State* state, ARMword value);
+
+extern ARMword ARMul_GetCPSR(ARMul_State* state);
+extern void ARMul_SetCPSR(ARMul_State* state, ARMword value);
+extern ARMword ARMul_GetSPSR(ARMul_State* state, ARMword mode);
+extern void ARMul_SetSPSR(ARMul_State* state, ARMword mode, ARMword value);
+
+/***************************************************************************\
+* Definitons of things to handle aborts *
+\***************************************************************************/
+
+extern void ARMul_Abort(ARMul_State* state, ARMword address);
+#ifdef MODET
+#define ARMul_ABORTWORD (state->TFlag ? 0xefffdfff : 0xefffffff) /* SWI -1 */
+#define ARMul_PREFETCHABORT(address) if (state->AbortAddr == 1) \
+ state->AbortAddr = (address & (state->TFlag ? ~1L : ~3L))
+#else
+#define ARMul_ABORTWORD 0xefffffff /* SWI -1 */
+#define ARMul_PREFETCHABORT(address) if (state->AbortAddr == 1) \
+ state->AbortAddr = (address & ~3L)
+#endif
+#define ARMul_DATAABORT(address) state->abortSig = HIGH ; \
+ state->Aborted = ARMul_DataAbortV ;
+#define ARMul_CLEARABORT state->abortSig = LOW
+
+/***************************************************************************\
+* Definitons of things in the memory interface *
+\***************************************************************************/
+
+extern unsigned ARMul_MemoryInit(ARMul_State* state, unsigned int initmemsize);
+extern void ARMul_MemoryExit(ARMul_State* state);
+
+extern ARMword ARMul_LoadInstrS(ARMul_State* state, ARMword address, ARMword isize);
+extern ARMword ARMul_LoadInstrN(ARMul_State* state, ARMword address, ARMword isize);
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern ARMword ARMul_ReLoadInstr(ARMul_State* state, ARMword address, ARMword isize);
+#ifdef __cplusplus
+ }
+#endif
+extern ARMword ARMul_LoadWordS(ARMul_State* state, ARMword address);
+extern ARMword ARMul_LoadWordN(ARMul_State* state, ARMword address);
+extern ARMword ARMul_LoadHalfWord(ARMul_State* state, ARMword address);
+extern ARMword ARMul_LoadByte(ARMul_State* state, ARMword address);
+
+extern void ARMul_StoreWordS(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_StoreWordN(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_StoreHalfWord(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_StoreByte(ARMul_State* state, ARMword address, ARMword data);
+
+extern ARMword ARMul_SwapWord(ARMul_State* state, ARMword address, ARMword data);
+extern ARMword ARMul_SwapByte(ARMul_State* state, ARMword address, ARMword data);
+
+extern void ARMul_Icycles(ARMul_State* state, unsigned number, ARMword address);
+extern void ARMul_Ccycles(ARMul_State* state, unsigned number, ARMword address);
+
+extern ARMword ARMul_ReadWord(ARMul_State* state, ARMword address);
+extern ARMword ARMul_ReadByte(ARMul_State* state, ARMword address);
+extern void ARMul_WriteWord(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_WriteByte(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_WriteDouble(ARMul_State* state, ARMword address, u64 data);
+
+extern ARMword ARMul_MemAccess(ARMul_State* state, ARMword, ARMword,
+ ARMword, ARMword, ARMword, ARMword, ARMword,
+ ARMword, ARMword, ARMword);
+
+/***************************************************************************\
+* Definitons of things in the co-processor interface *
+\***************************************************************************/
+
+#define ARMul_FIRST 0
+#define ARMul_TRANSFER 1
+#define ARMul_BUSY 2
+#define ARMul_DATA 3
+#define ARMul_INTERRUPT 4
+#define ARMul_DONE 0
+#define ARMul_CANT 1
+#define ARMul_INC 3
+
+#define ARMul_CP13_R0_FIQ 0x1
+#define ARMul_CP13_R0_IRQ 0x2
+#define ARMul_CP13_R8_PMUS 0x1
+
+#define ARMul_CP14_R0_ENABLE 0x0001
+#define ARMul_CP14_R0_CLKRST 0x0004
+#define ARMul_CP14_R0_CCD 0x0008
+#define ARMul_CP14_R0_INTEN0 0x0010
+#define ARMul_CP14_R0_INTEN1 0x0020
+#define ARMul_CP14_R0_INTEN2 0x0040
+#define ARMul_CP14_R0_FLAG0 0x0100
+#define ARMul_CP14_R0_FLAG1 0x0200
+#define ARMul_CP14_R0_FLAG2 0x0400
+#define ARMul_CP14_R10_MOE_IB 0x0004
+#define ARMul_CP14_R10_MOE_DB 0x0008
+#define ARMul_CP14_R10_MOE_BT 0x000c
+#define ARMul_CP15_R1_ENDIAN 0x0080
+#define ARMul_CP15_R1_ALIGN 0x0002
+#define ARMul_CP15_R5_X 0x0400
+#define ARMul_CP15_R5_ST_ALIGN 0x0001
+#define ARMul_CP15_R5_IMPRE 0x0406
+#define ARMul_CP15_R5_MMU_EXCPT 0x0400
+#define ARMul_CP15_DBCON_M 0x0100
+#define ARMul_CP15_DBCON_E1 0x000c
+#define ARMul_CP15_DBCON_E0 0x0003
+
+extern unsigned ARMul_CoProInit(ARMul_State* state);
+extern void ARMul_CoProExit(ARMul_State* state);
+extern void ARMul_CoProAttach (ARMul_State* state, unsigned number,
+ ARMul_CPInits* init, ARMul_CPExits* exit,
+ ARMul_LDCs* ldc, ARMul_STCs* stc,
+ ARMul_MRCs* mrc, ARMul_MCRs* mcr,
+ ARMul_MRRCs* mrrc, ARMul_MCRRs* mcrr,
+ ARMul_CDPs* cdp,
+ ARMul_CPReads* read, ARMul_CPWrites* write);
+extern void ARMul_CoProDetach(ARMul_State* state, unsigned number);
+
+/***************************************************************************\
+* Definitons of things in the host environment *
+\***************************************************************************/
+
+extern unsigned ARMul_OSInit(ARMul_State* state);
+extern void ARMul_OSExit(ARMul_State* state);
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+extern unsigned ARMul_OSHandleSWI(ARMul_State* state, ARMword number);
+#ifdef __cplusplus
+}
+#endif
+
+
+extern ARMword ARMul_OSLastErrorP(ARMul_State* state);
+
+extern ARMword ARMul_Debug(ARMul_State* state, ARMword pc, ARMword instr);
+extern unsigned ARMul_OSException(ARMul_State* state, ARMword vector, ARMword pc);
+extern int rdi_log;
+
+enum ConditionCode {
+ EQ = 0,
+ NE = 1,
+ CS = 2,
+ CC = 3,
+ MI = 4,
+ PL = 5,
+ VS = 6,
+ VC = 7,
+ HI = 8,
+ LS = 9,
+ GE = 10,
+ LT = 11,
+ GT = 12,
+ LE = 13,
+ AL = 14,
+ NV = 15,
+};
+
+#ifndef NFLAG
+#define NFLAG state->NFlag
+#endif //NFLAG
+
+#ifndef ZFLAG
+#define ZFLAG state->ZFlag
+#endif //ZFLAG
+
+#ifndef CFLAG
+#define CFLAG state->CFlag
+#endif //CFLAG
+
+#ifndef VFLAG
+#define VFLAG state->VFlag
+#endif //VFLAG
+
+#ifndef IFLAG
+#define IFLAG (state->IFFlags >> 1)
+#endif //IFLAG
+
+#ifndef FFLAG
+#define FFLAG (state->IFFlags & 1)
+#endif //FFLAG
+
+#ifndef IFFLAGS
+#define IFFLAGS state->IFFlags
+#endif //VFLAG
+
+#define FLAG_MASK 0xf0000000
+#define NBIT_SHIFT 31
+#define ZBIT_SHIFT 30
+#define CBIT_SHIFT 29
+#define VBIT_SHIFT 28
+
+#define SKYEYE_OUTREGS(fd) { fprintf ((fd), "R %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,C %x,S %x,%x,%x,%x,%x,%x,%x,M %x,B %x,E %x,I %x,P %x,T %x,L %x,D %x,",\
+ state->Reg[0],state->Reg[1],state->Reg[2],state->Reg[3], \
+ state->Reg[4],state->Reg[5],state->Reg[6],state->Reg[7], \
+ state->Reg[8],state->Reg[9],state->Reg[10],state->Reg[11], \
+ state->Reg[12],state->Reg[13],state->Reg[14],state->Reg[15], \
+ state->Cpsr, state->Spsr[0], state->Spsr[1], state->Spsr[2],\
+ state->Spsr[3],state->Spsr[4], state->Spsr[5], state->Spsr[6],\
+ state->Mode,state->Bank,state->ErrorCode,state->instr,state->pc,\
+ state->temp,state->loaded,state->decoded);}
+
+#define SKYEYE_OUTMOREREGS(fd) { fprintf ((fd),"\
+RUs %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\
+RF %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\
+RI %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\
+RS %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\
+RA %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\
+RUn %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n",\
+ state->RegBank[0][0],state->RegBank[0][1],state->RegBank[0][2],state->RegBank[0][3], \
+ state->RegBank[0][4],state->RegBank[0][5],state->RegBank[0][6],state->RegBank[0][7], \
+ state->RegBank[0][8],state->RegBank[0][9],state->RegBank[0][10],state->RegBank[0][11], \
+ state->RegBank[0][12],state->RegBank[0][13],state->RegBank[0][14],state->RegBank[0][15], \
+ state->RegBank[1][0],state->RegBank[1][1],state->RegBank[1][2],state->RegBank[1][3], \
+ state->RegBank[1][4],state->RegBank[1][5],state->RegBank[1][6],state->RegBank[1][7], \
+ state->RegBank[1][8],state->RegBank[1][9],state->RegBank[1][10],state->RegBank[1][11], \
+ state->RegBank[1][12],state->RegBank[1][13],state->RegBank[1][14],state->RegBank[1][15], \
+ state->RegBank[2][0],state->RegBank[2][1],state->RegBank[2][2],state->RegBank[2][3], \
+ state->RegBank[2][4],state->RegBank[2][5],state->RegBank[2][6],state->RegBank[2][7], \
+ state->RegBank[2][8],state->RegBank[2][9],state->RegBank[2][10],state->RegBank[2][11], \
+ state->RegBank[2][12],state->RegBank[2][13],state->RegBank[2][14],state->RegBank[2][15], \
+ state->RegBank[3][0],state->RegBank[3][1],state->RegBank[3][2],state->RegBank[3][3], \
+ state->RegBank[3][4],state->RegBank[3][5],state->RegBank[3][6],state->RegBank[3][7], \
+ state->RegBank[3][8],state->RegBank[3][9],state->RegBank[3][10],state->RegBank[3][11], \
+ state->RegBank[3][12],state->RegBank[3][13],state->RegBank[3][14],state->RegBank[3][15], \
+ state->RegBank[4][0],state->RegBank[4][1],state->RegBank[4][2],state->RegBank[4][3], \
+ state->RegBank[4][4],state->RegBank[4][5],state->RegBank[4][6],state->RegBank[4][7], \
+ state->RegBank[4][8],state->RegBank[4][9],state->RegBank[4][10],state->RegBank[4][11], \
+ state->RegBank[4][12],state->RegBank[4][13],state->RegBank[4][14],state->RegBank[4][15], \
+ state->RegBank[5][0],state->RegBank[5][1],state->RegBank[5][2],state->RegBank[5][3], \
+ state->RegBank[5][4],state->RegBank[5][5],state->RegBank[5][6],state->RegBank[5][7], \
+ state->RegBank[5][8],state->RegBank[5][9],state->RegBank[5][10],state->RegBank[5][11], \
+ state->RegBank[5][12],state->RegBank[5][13],state->RegBank[5][14],state->RegBank[5][15] \
+ );}
+
+
+#define SA1110 0x6901b110
+#define SA1100 0x4401a100
+#define PXA250 0x69052100
+#define PXA270 0x69054110
+//#define PXA250 0x69052903
+// 0x69052903; //PXA250 B1 from intel 278522-001.pdf
+
+extern bool AddOverflow(ARMword, ARMword, ARMword);
+extern bool SubOverflow(ARMword, ARMword, ARMword);
+
+extern void ARMul_UndefInstr(ARMul_State*, ARMword);
+extern void ARMul_FixCPSR(ARMul_State*, ARMword, ARMword);
+extern void ARMul_FixSPSR(ARMul_State*, ARMword, ARMword);
+extern void ARMul_ConsolePrint(ARMul_State*, const char*, ...);
+extern void ARMul_SelectProcessor(ARMul_State*, unsigned);
+
+extern u32 AddWithCarry(u32, u32, u32, bool*, bool*);
+extern bool ARMul_AddOverflowQ(ARMword, ARMword);
+
+extern u8 ARMul_SignedSaturatedAdd8(u8, u8);
+extern u8 ARMul_SignedSaturatedSub8(u8, u8);
+extern u16 ARMul_SignedSaturatedAdd16(u16, u16);
+extern u16 ARMul_SignedSaturatedSub16(u16, u16);
+
+extern u8 ARMul_UnsignedSaturatedAdd8(u8, u8);
+extern u16 ARMul_UnsignedSaturatedAdd16(u16, u16);
+extern u8 ARMul_UnsignedSaturatedSub8(u8, u8);
+extern u16 ARMul_UnsignedSaturatedSub16(u16, u16);
+extern u8 ARMul_UnsignedAbsoluteDifference(u8, u8);
+extern u32 ARMul_SignedSatQ(s32, u8, bool*);
+extern u32 ARMul_UnsignedSatQ(s32, u8, bool*);
+
+extern u32 ReadCP15Register(ARMul_State* cpu, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2);
+extern void WriteCP15Register(ARMul_State* cpu, u32 value, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2);
+
+
+
+#define DIFF_LOG 0
+#define SAVE_LOG 0
+
+#endif /* _ARMDEFS_H_ */
--- /dev/null
+/* armemu.h -- ARMulator emulation macros: ARM6 Instruction Emulator.
+ Copyright (C) 1994 Advanced RISC Machines Ltd.
+
+ 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 of the License, 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 __ARMEMU_H__
+#define __ARMEMU_H__
+
+
+#include "arm/skyeye_common/armdefs.h"
+//#include "skyeye.h"
+
+//extern ARMword isize;
+
+/* Shift Opcodes. */
+#define LSL 0
+#define LSR 1
+#define ASR 2
+#define ROR 3
+
+/* Macros to twiddle the status flags and mode. */
+#define NBIT ((unsigned)1L << 31)
+#define ZBIT (1L << 30)
+#define CBIT (1L << 29)
+#define VBIT (1L << 28)
+#define QBIT (1L << 27)
+#define IBIT (1L << 7)
+#define FBIT (1L << 6)
+#define IFBITS (3L << 6)
+#define R15IBIT (1L << 27)
+#define R15FBIT (1L << 26)
+#define R15IFBITS (3L << 26)
+
+#ifdef MODET /* Thumb support. */
+/* ??? This bit is actually in the low order bit of the PC in the hardware.
+ It isn't clear if the simulator needs to model that or not. */
+#define TBIT (1L << 5)
+#define TFLAG state->TFlag
+#define SETT state->TFlag = 1
+#define CLEART state->TFlag = 0
+#define ASSIGNT(res) state->TFlag = res
+#define INSN_SIZE (TFLAG ? 2 : 4)
+#else
+#define INSN_SIZE 4
+#endif
+
+/*add armv6 CPSR feature*/
+#define EFLAG state->EFlag
+#define SETE state->EFlag = 1
+#define CLEARE state->EFlag = 0
+#define ASSIGNE(res) state->NFlag = res
+
+#define AFLAG state->AFlag
+#define SETA state->AFlag = 1
+#define CLEARA state->AFlag = 0
+#define ASSIGNA(res) state->NFlag = res
+
+#define QFLAG state->QFlag
+#define SETQ state->QFlag = 1
+#define CLEARQ state->AFlag = 0
+#define ASSIGNQ(res) state->QFlag = res
+
+/* add end */
+
+#define NFLAG state->NFlag
+#define SETN state->NFlag = 1
+#define CLEARN state->NFlag = 0
+#define ASSIGNN(res) state->NFlag = res
+
+#define ZFLAG state->ZFlag
+#define SETZ state->ZFlag = 1
+#define CLEARZ state->ZFlag = 0
+#define ASSIGNZ(res) state->ZFlag = res
+
+#define CFLAG state->CFlag
+#define SETC state->CFlag = 1
+#define CLEARC state->CFlag = 0
+#define ASSIGNC(res) state->CFlag = res
+
+#define VFLAG state->VFlag
+#define SETV state->VFlag = 1
+#define CLEARV state->VFlag = 0
+#define ASSIGNV(res) state->VFlag = res
+
+#define SFLAG state->SFlag
+#define SETS state->SFlag = 1
+#define CLEARS state->SFlag = 0
+#define ASSIGNS(res) state->SFlag = res
+
+#define IFLAG (state->IFFlags >> 1)
+#define FFLAG (state->IFFlags & 1)
+#define IFFLAGS state->IFFlags
+#define ASSIGNINT(res) state->IFFlags = (((res) >> 6) & 3)
+#define ASSIGNR15INT(res) state->IFFlags = (((res) >> 26) & 3) ;
+
+#define PSR_FBITS (0xff000000L)
+#define PSR_SBITS (0x00ff0000L)
+#define PSR_XBITS (0x0000ff00L)
+#define PSR_CBITS (0x000000ffL)
+
+#if defined MODE32 || defined MODET
+#define CCBITS (0xf8000000L)
+#else
+#define CCBITS (0xf0000000L)
+#endif
+
+#define INTBITS (0xc0L)
+
+#if defined MODET && defined MODE32
+#define PCBITS (0xffffffffL)
+#else
+#define PCBITS (0xfffffffcL)
+#endif
+
+#define MODEBITS (0x1fL)
+#define R15INTBITS (3L << 26)
+
+#if defined MODET && defined MODE32
+#define R15PCBITS (0x03ffffffL)
+#else
+#define R15PCBITS (0x03fffffcL)
+#endif
+
+#define R15PCMODEBITS (0x03ffffffL)
+#define R15MODEBITS (0x3L)
+
+#ifdef MODE32
+#define PCMASK PCBITS
+#define PCWRAP(pc) (pc)
+#else
+#define PCMASK R15PCBITS
+#define PCWRAP(pc) ((pc) & R15PCBITS)
+#endif
+
+#define PC (state->Reg[15] & PCMASK)
+#define R15CCINTMODE (state->Reg[15] & (CCBITS | R15INTBITS | R15MODEBITS))
+#define R15INT (state->Reg[15] & R15INTBITS)
+#define R15INTPC (state->Reg[15] & (R15INTBITS | R15PCBITS))
+#define R15INTPCMODE (state->Reg[15] & (R15INTBITS | R15PCBITS | R15MODEBITS))
+#define R15INTMODE (state->Reg[15] & (R15INTBITS | R15MODEBITS))
+#define R15PC (state->Reg[15] & R15PCBITS)
+#define R15PCMODE (state->Reg[15] & (R15PCBITS | R15MODEBITS))
+#define R15MODE (state->Reg[15] & R15MODEBITS)
+
+#define ECC ((NFLAG << 31) | (ZFLAG << 30) | (CFLAG << 29) | (VFLAG << 28) | (QFLAG << 27))
+#define EINT (IFFLAGS << 6)
+#define ER15INT (IFFLAGS << 26)
+#define EMODE (state->Mode)
+#define EGEBITS (state->GEFlag & 0x000F0000)
+
+#ifdef MODET
+#define CPSR (ECC | EGEBITS | (EFLAG << 9) | (AFLAG << 8) | EINT | (TFLAG << 5) | EMODE)
+#else
+#define CPSR (ECC | EINT | EMODE)
+#endif
+
+#ifdef MODE32
+#define PATCHR15
+#else
+#define PATCHR15 state->Reg[15] = ECC | ER15INT | EMODE | R15PC
+#endif
+
+#define GETSPSR(bank) (ARMul_GetSPSR (state, EMODE))
+#define SETPSR_F(d,s) d = ((d) & ~PSR_FBITS) | ((s) & PSR_FBITS)
+#define SETPSR_S(d,s) d = ((d) & ~PSR_SBITS) | ((s) & PSR_SBITS)
+#define SETPSR_X(d,s) d = ((d) & ~PSR_XBITS) | ((s) & PSR_XBITS)
+#define SETPSR_C(d,s) d = ((d) & ~PSR_CBITS) | ((s) & PSR_CBITS)
+
+#define SETR15PSR(s) \
+ do \
+ { \
+ if (state->Mode == USER26MODE) \
+ { \
+ state->Reg[15] = ((s) & CCBITS) | R15PC | ER15INT | EMODE; \
+ ASSIGNN ((state->Reg[15] & NBIT) != 0); \
+ ASSIGNZ ((state->Reg[15] & ZBIT) != 0); \
+ ASSIGNC ((state->Reg[15] & CBIT) != 0); \
+ ASSIGNV ((state->Reg[15] & VBIT) != 0); \
+ } \
+ else \
+ { \
+ state->Reg[15] = R15PC | ((s) & (CCBITS | R15INTBITS | R15MODEBITS)); \
+ ARMul_R15Altered (state); \
+ } \
+ } \
+ while (0)
+
+#define SETABORT(i, m, d) \
+ do \
+ { \
+ int SETABORT_mode = (m); \
+ \
+ ARMul_SetSPSR (state, SETABORT_mode, ARMul_GetCPSR (state)); \
+ ARMul_SetCPSR (state, ((ARMul_GetCPSR (state) & ~(EMODE | TBIT)) \
+ | (i) | SETABORT_mode)); \
+ state->Reg[14] = temp - (d); \
+ } \
+ while (0)
+
+#ifndef MODE32
+#define VECTORS 0x20
+#define LEGALADDR 0x03ffffff
+#define VECTORACCESS(address) (address < VECTORS && ARMul_MODE26BIT && state->prog32Sig)
+#define ADDREXCEPT(address) (address > LEGALADDR && !state->data32Sig)
+#endif
+
+#define INTERNALABORT(address) \
+ do \
+ { \
+ if (address < VECTORS) \
+ state->Aborted = ARMul_DataAbortV; \
+ else \
+ state->Aborted = ARMul_AddrExceptnV; \
+ } \
+ while (0)
+
+#ifdef MODE32
+#define TAKEABORT ARMul_Abort (state, ARMul_DataAbortV)
+#else
+#define TAKEABORT \
+ do \
+ { \
+ if (state->Aborted == ARMul_AddrExceptnV) \
+ ARMul_Abort (state, ARMul_AddrExceptnV); \
+ else \
+ ARMul_Abort (state, ARMul_DataAbortV); \
+ } \
+ while (0)
+#endif
+
+#define CPTAKEABORT \
+ do \
+ { \
+ if (!state->Aborted) \
+ ARMul_Abort (state, ARMul_UndefinedInstrV); \
+ else if (state->Aborted == ARMul_AddrExceptnV) \
+ ARMul_Abort (state, ARMul_AddrExceptnV); \
+ else \
+ ARMul_Abort (state, ARMul_DataAbortV); \
+ } \
+ while (0);
+
+
+/* Different ways to start the next instruction. */
+#define SEQ 0
+#define NONSEQ 1
+#define PCINCEDSEQ 2
+#define PCINCEDNONSEQ 3
+#define PRIMEPIPE 4
+#define RESUME 8
+
+/************************************/
+/* shenoubang 2012-3-11 */
+/* for armv7 DBG DMB DSB instr*/
+/************************************/
+#define MBReqTypes_Writes 0
+#define MBReqTypes_All 1
+
+#define NORMALCYCLE state->NextInstr = 0
+#define BUSUSEDN state->NextInstr |= 1 /* The next fetch will be an N cycle. */
+#define BUSUSEDINCPCS \
+ do \
+ { \
+ if (! state->is_v4) \
+ { \
+ /* A standard PC inc and an S cycle. */ \
+ state->Reg[15] += INSN_SIZE; \
+ state->NextInstr = (state->NextInstr & 0xff) | 2; \
+ } \
+ } \
+ while (0)
+
+#define BUSUSEDINCPCN \
+ do \
+ { \
+ if (state->is_v4) \
+ BUSUSEDN; \
+ else \
+ { \
+ /* A standard PC inc and an N cycle. */ \
+ state->Reg[15] += INSN_SIZE; \
+ state->NextInstr |= 3; \
+ } \
+ } \
+ while (0)
+
+#define INCPC \
+ do \
+ { \
+ /* A standard PC inc. */ \
+ state->Reg[15] += INSN_SIZE; \
+ state->NextInstr |= 2; \
+ } \
+ while (0)
+
+#define FLUSHPIPE state->NextInstr |= PRIMEPIPE
+
+/* Cycle based emulation. */
+
+#define OUTPUTCP(i,a,b)
+#define NCYCLE
+#define SCYCLE
+#define ICYCLE
+#define CCYCLE
+#define NEXTCYCLE(c)
+
+/* Macros to extract parts of instructions. */
+#define DESTReg (BITS (12, 15))
+#define LHSReg (BITS (16, 19))
+#define RHSReg (BITS ( 0, 3))
+
+#define DEST (state->Reg[DESTReg])
+
+#ifdef MODE32
+#ifdef MODET
+#define LHS ((LHSReg == 15) ? (state->Reg[15] & 0xFFFFFFFC) : (state->Reg[LHSReg]))
+#define RHS ((RHSReg == 15) ? (state->Reg[15] & 0xFFFFFFFC) : (state->Reg[RHSReg]))
+#else
+#define LHS (state->Reg[LHSReg])
+#define RHS (state->Reg[RHSReg])
+#endif
+#else
+#define LHS ((LHSReg == 15) ? R15PC : (state->Reg[LHSReg]))
+#define RHS ((RHSReg == 15) ? R15PC : (state->Reg[RHSReg]))
+#endif
+
+#define MULDESTReg (BITS (16, 19))
+#define MULLHSReg (BITS ( 0, 3))
+#define MULRHSReg (BITS ( 8, 11))
+#define MULACCReg (BITS (12, 15))
+
+#define DPImmRHS (ARMul_ImmedTable[BITS(0, 11)])
+#define DPSImmRHS temp = BITS(0,11) ; \
+ rhs = ARMul_ImmedTable[temp] ; \
+ if (temp > 255) /* There was a shift. */ \
+ ASSIGNC (rhs >> 31) ;
+
+#ifdef MODE32
+#define DPRegRHS ((BITS (4,11) == 0) ? state->Reg[RHSReg] \
+ : GetDPRegRHS (state, instr))
+#define DPSRegRHS ((BITS (4,11) == 0) ? state->Reg[RHSReg] \
+ : GetDPSRegRHS (state, instr))
+#else
+#define DPRegRHS ((BITS (0, 11) < 15) ? state->Reg[RHSReg] \
+ : GetDPRegRHS (state, instr))
+#define DPSRegRHS ((BITS (0, 11) < 15) ? state->Reg[RHSReg] \
+ : GetDPSRegRHS (state, instr))
+#endif
+
+#define LSBase state->Reg[LHSReg]
+#define LSImmRHS (BITS(0,11))
+
+#ifdef MODE32
+#define LSRegRHS ((BITS (4, 11) == 0) ? state->Reg[RHSReg] \
+ : GetLSRegRHS (state, instr))
+#else
+#define LSRegRHS ((BITS (0, 11) < 15) ? state->Reg[RHSReg] \
+ : GetLSRegRHS (state, instr))
+#endif
+
+#define LSMNumRegs ((ARMword) ARMul_BitList[BITS (0, 7)] + \
+ (ARMword) ARMul_BitList[BITS (8, 15)] )
+#define LSMBaseFirst ((LHSReg == 0 && BIT (0)) || \
+ (BIT (LHSReg) && BITS (0, LHSReg - 1) == 0))
+
+#define SWAPSRC (state->Reg[RHSReg])
+
+#define LSCOff (BITS (0, 7) << 2)
+#define CPNum BITS (8, 11)
+
+/* Determine if access to coprocessor CP is permitted.
+ The XScale has a register in CP15 which controls access to CP0 - CP13. */
+//chy 2003-09-03, new CP_ACCESS_ALLOWED
+/*
+#define CP_ACCESS_ALLOWED(STATE, CP) \
+ ( ((CP) >= 14) \
+ || (! (STATE)->is_XScale) \
+ || (read_cp15_reg (15, 0, 1) & (1 << (CP))))
+*/
+#define CP_ACCESS_ALLOWED(STATE, CP) \
+ ( ((CP) >= 14) ) \
+
+/* Macro to rotate n right by b bits. */
+#define ROTATER(n, b) (((n) >> (b)) | ((n) << (32 - (b))))
+
+/* Macros to store results of instructions. */
+#define WRITEDEST(d) \
+ do \
+ { \
+ if (DESTReg == 15) \
+ WriteR15 (state, d); \
+ else \
+ DEST = d; \
+ } \
+ while (0)
+
+#define WRITESDEST(d) \
+ do \
+ { \
+ if (DESTReg == 15) \
+ WriteSR15 (state, d); \
+ else \
+ { \
+ DEST = d; \
+ ARMul_NegZero (state, d); \
+ } \
+ } \
+ while (0)
+
+#define WRITEDESTB(d) \
+ do \
+ { \
+ if (DESTReg == 15){ \
+ WriteR15Branch (state, d); \
+ } \
+ else{ \
+ DEST = d; \
+ } \
+ } \
+ while (0)
+
+#define BYTETOBUS(data) ((data & 0xff) | \
+ ((data & 0xff) << 8) | \
+ ((data & 0xff) << 16) | \
+ ((data & 0xff) << 24))
+
+#define BUSTOBYTE(address, data) \
+ do \
+ { \
+ if (state->bigendSig) \
+ temp = (data >> (((address ^ 3) & 3) << 3)) & 0xff; \
+ else \
+ temp = (data >> ((address & 3) << 3)) & 0xff; \
+ } \
+ while (0)
+
+#define LOADMULT(instr, address, wb) LoadMult (state, instr, address, wb)
+#define LOADSMULT(instr, address, wb) LoadSMult (state, instr, address, wb)
+#define STOREMULT(instr, address, wb) StoreMult (state, instr, address, wb)
+#define STORESMULT(instr, address, wb) StoreSMult (state, instr, address, wb)
+
+#define POSBRANCH ((instr & 0x7fffff) << 2)
+#define NEGBRANCH ((0xff000000 |(instr & 0xffffff)) << 2)
+
+
+/* Values for Emulate. */
+#define STOP 0 /* stop */
+#define CHANGEMODE 1 /* change mode */
+#define ONCE 2 /* execute just one interation */
+#define RUN 3 /* continuous execution */
+
+/* Stuff that is shared across modes. */
+extern unsigned ARMul_MultTable[]; /* Number of I cycles for a mult. */
+extern ARMword ARMul_ImmedTable[]; /* Immediate DP LHS values. */
+extern char ARMul_BitList[]; /* Number of bits in a byte table. */
+
+#define EVENTLISTSIZE 1024L
+
+/* Thumb support. */
+typedef enum
+{
+ t_undefined, /* Undefined Thumb instruction. */
+ t_decoded, /* Instruction decoded to ARM equivalent. */
+ t_branch, /* Thumb branch (already processed). */
+ t_uninitialized
+}
+tdstate;
+
+/*********************************************************************************
+ * Check all the possible undef or unpredict behavior, Some of them probably is
+ * out-of-updated with the newer ISA.
+ * -- Michael.Kang
+ ********************************************************************************/
+#define UNDEF_WARNING LOG("undefined or unpredicted behavior for arm instruction.");
+
+/* Macros to scrutinize instructions. */
+#define UNDEF_Test UNDEF_WARNING
+//#define UNDEF_Test
+
+//#define UNDEF_Shift UNDEF_WARNING
+#define UNDEF_Shift
+
+//#define UNDEF_MSRPC UNDEF_WARNING
+#define UNDEF_MSRPC
+
+//#define UNDEF_MRSPC UNDEF_WARNING
+#define UNDEF_MRSPC
+
+#define UNDEF_MULPCDest UNDEF_WARNING
+//#define UNDEF_MULPCDest
+
+#define UNDEF_MULDestEQOp1 UNDEF_WARNING
+//#define UNDEF_MULDestEQOp1
+
+//#define UNDEF_LSRBPC UNDEF_WARNING
+#define UNDEF_LSRBPC
+
+//#define UNDEF_LSRBaseEQOffWb UNDEF_WARNING
+#define UNDEF_LSRBaseEQOffWb
+
+//#define UNDEF_LSRBaseEQDestWb UNDEF_WARNING
+#define UNDEF_LSRBaseEQDestWb
+
+//#define UNDEF_LSRPCBaseWb UNDEF_WARNING
+#define UNDEF_LSRPCBaseWb
+
+//#define UNDEF_LSRPCOffWb UNDEF_WARNING
+#define UNDEF_LSRPCOffWb
+
+//#define UNDEF_LSMNoRegs UNDEF_WARNING
+#define UNDEF_LSMNoRegs
+
+//#define UNDEF_LSMPCBase UNDEF_WARNING
+#define UNDEF_LSMPCBase
+
+//#define UNDEF_LSMUserBankWb UNDEF_WARNING
+#define UNDEF_LSMUserBankWb
+
+//#define UNDEF_LSMBaseInListWb UNDEF_WARNING
+#define UNDEF_LSMBaseInListWb
+
+#define UNDEF_SWPPC UNDEF_WARNING
+//#define UNDEF_SWPPC
+
+#define UNDEF_CoProHS UNDEF_WARNING
+//#define UNDEF_CoProHS
+
+#define UNDEF_MCRPC UNDEF_WARNING
+//#define UNDEF_MCRPC
+
+//#define UNDEF_LSCPCBaseWb UNDEF_WARNING
+#define UNDEF_LSCPCBaseWb
+
+#define UNDEF_UndefNotBounced UNDEF_WARNING
+//#define UNDEF_UndefNotBounced
+
+#define UNDEF_ShortInt UNDEF_WARNING
+//#define UNDEF_ShortInt
+
+#define UNDEF_IllegalMode UNDEF_WARNING
+//#define UNDEF_IllegalMode
+
+#define UNDEF_Prog32SigChange UNDEF_WARNING
+//#define UNDEF_Prog32SigChange
+
+#define UNDEF_Data32SigChange UNDEF_WARNING
+//#define UNDEF_Data32SigChange
+
+/* Prototypes for exported functions. */
+extern unsigned ARMul_NthReg (ARMword, unsigned);
+
+/* Prototypes for exported functions. */
+#ifdef __cplusplus
+ extern "C" {
+#endif
+extern ARMword ARMul_Emulate26 (ARMul_State *);
+extern ARMword ARMul_Emulate32 (ARMul_State *);
+#ifdef __cplusplus
+ }
+#endif
+extern unsigned IntPending (ARMul_State *);
+extern void ARMul_CPSRAltered (ARMul_State *);
+extern void ARMul_R15Altered (ARMul_State *);
+extern ARMword ARMul_GetPC (ARMul_State *);
+extern ARMword ARMul_GetNextPC (ARMul_State *);
+extern ARMword ARMul_GetR15 (ARMul_State *);
+extern ARMword ARMul_GetCPSR (ARMul_State *);
+extern void ARMul_EnvokeEvent (ARMul_State *);
+extern unsigned int ARMul_Time (ARMul_State *);
+extern void ARMul_NegZero (ARMul_State *, ARMword);
+extern void ARMul_SetPC (ARMul_State *, ARMword);
+extern void ARMul_SetR15 (ARMul_State *, ARMword);
+extern void ARMul_SetCPSR (ARMul_State *, ARMword);
+extern ARMword ARMul_GetSPSR (ARMul_State *, ARMword);
+extern void ARMul_Abort26 (ARMul_State *, ARMword);
+extern void ARMul_Abort32 (ARMul_State *, ARMword);
+extern ARMword ARMul_MRC (ARMul_State *, ARMword);
+extern void ARMul_MRRC (ARMul_State *, ARMword, ARMword *, ARMword *);
+extern void ARMul_CDP (ARMul_State *, ARMword);
+extern void ARMul_LDC (ARMul_State *, ARMword, ARMword);
+extern void ARMul_STC (ARMul_State *, ARMword, ARMword);
+extern void ARMul_MCR (ARMul_State *, ARMword, ARMword);
+extern void ARMul_MCRR (ARMul_State *, ARMword, ARMword, ARMword);
+extern void ARMul_SetSPSR (ARMul_State *, ARMword, ARMword);
+extern ARMword ARMul_SwitchMode (ARMul_State *, ARMword, ARMword);
+extern ARMword ARMul_Align (ARMul_State *, ARMword, ARMword);
+extern ARMword ARMul_SwitchMode (ARMul_State *, ARMword, ARMword);
+extern void ARMul_MSRCpsr (ARMul_State *, ARMword, ARMword);
+extern void ARMul_SubOverflow (ARMul_State *, ARMword, ARMword, ARMword);
+extern void ARMul_AddOverflow (ARMul_State *, ARMword, ARMword, ARMword);
+extern void ARMul_SubCarry (ARMul_State *, ARMword, ARMword, ARMword);
+extern void ARMul_AddCarry (ARMul_State *, ARMword, ARMword, ARMword);
+extern tdstate ARMul_ThumbDecode (ARMul_State *, ARMword, ARMword, ARMword *);
+extern ARMword ARMul_GetReg (ARMul_State *, unsigned, unsigned);
+extern void ARMul_SetReg (ARMul_State *, unsigned, unsigned, ARMword);
+extern void ARMul_ScheduleEvent (ARMul_State *, unsigned int,
+ unsigned (*)(ARMul_State *));
+/* Coprocessor support functions. */
+extern unsigned ARMul_CoProInit (ARMul_State *);
+extern void ARMul_CoProExit (ARMul_State *);
+extern void ARMul_CoProAttach (ARMul_State *, unsigned, ARMul_CPInits *,
+ ARMul_CPExits *, ARMul_LDCs *, ARMul_STCs *,
+ ARMul_MRCs *, ARMul_MCRs *, ARMul_MRRCs *, ARMul_MCRRs *,
+ ARMul_CDPs *, ARMul_CPReads *, ARMul_CPWrites *);
+extern void ARMul_CoProDetach (ARMul_State *, unsigned);
+extern ARMword read_cp15_reg (unsigned, unsigned, unsigned);
+
+extern unsigned DSPLDC4 (ARMul_State *, unsigned, ARMword, ARMword);
+extern unsigned DSPMCR4 (ARMul_State *, unsigned, ARMword, ARMword);
+extern unsigned DSPMRC4 (ARMul_State *, unsigned, ARMword, ARMword *);
+extern unsigned DSPSTC4 (ARMul_State *, unsigned, ARMword, ARMword *);
+extern unsigned DSPCDP4 (ARMul_State *, unsigned, ARMword);
+extern unsigned DSPMCR5 (ARMul_State *, unsigned, ARMword, ARMword);
+extern unsigned DSPMRC5 (ARMul_State *, unsigned, ARMword, ARMword *);
+extern unsigned DSPLDC5 (ARMul_State *, unsigned, ARMword, ARMword);
+extern unsigned DSPSTC5 (ARMul_State *, unsigned, ARMword, ARMword *);
+extern unsigned DSPCDP5 (ARMul_State *, unsigned, ARMword);
+extern unsigned DSPMCR6 (ARMul_State *, unsigned, ARMword, ARMword);
+extern unsigned DSPMRC6 (ARMul_State *, unsigned, ARMword, ARMword *);
+extern unsigned DSPCDP6 (ARMul_State *, unsigned, ARMword);
+
+
+#endif
--- /dev/null
+/*
+ armmmu.c - Memory Management Unit emulation.
+ ARMulator extensions for the ARM7100 family.
+ Copyright (C) 1999 Ben Williamson
+
+ 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 of the License, 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 _ARMMMU_H_
+#define _ARMMMU_H_
+
+
+#define WORD_SHT 2
+#define WORD_SIZE (1<<WORD_SHT)
+/* The MMU is accessible with MCR and MRC operations to copro 15: */
+
+#define MMU_COPRO (15)
+
+/* Register numbers in the MMU: */
+
+typedef enum mmu_regnum_t
+{
+ MMU_ID = 0,
+ MMU_CONTROL = 1,
+ MMU_TRANSLATION_TABLE_BASE = 2,
+ MMU_DOMAIN_ACCESS_CONTROL = 3,
+ MMU_FAULT_STATUS = 5,
+ MMU_FAULT_ADDRESS = 6,
+ MMU_CACHE_OPS = 7,
+ MMU_TLB_OPS = 8,
+ MMU_CACHE_LOCKDOWN = 9,
+ MMU_TLB_LOCKDOWN = 10,
+ MMU_PID = 13,
+
+ /*MMU_V4 */
+ MMU_V4_CACHE_OPS = 7,
+ MMU_V4_TLB_OPS = 8,
+
+ /*MMU_V3 */
+ MMU_V3_FLUSH_TLB = 5,
+ MMU_V3_FLUSH_TLB_ENTRY = 6,
+ MMU_V3_FLUSH_CACHE = 7,
+
+ /*MMU Intel SA-1100 */
+ MMU_SA_RB_OPS = 9,
+ MMU_SA_DEBUG = 14,
+ MMU_SA_CP15_R15 = 15,
+ //chy 2003-08-24
+ /*Intel xscale CP15 */
+ XSCALE_CP15_CACHE_TYPE = 0,
+ XSCALE_CP15_AUX_CONTROL = 1,
+ XSCALE_CP15_COPRO_ACCESS = 15,
+
+} mmu_regnum_t;
+
+/* Bits in the control register */
+
+#define CONTROL_MMU (1<<0)
+#define CONTROL_ALIGN_FAULT (1<<1)
+#define CONTROL_CACHE (1<<2)
+#define CONTROL_DATA_CACHE (1<<2)
+#define CONTROL_WRITE_BUFFER (1<<3)
+#define CONTROL_BIG_ENDIAN (1<<7)
+#define CONTROL_SYSTEM (1<<8)
+#define CONTROL_ROM (1<<9)
+#define CONTROL_UNDEFINED (1<<10)
+#define CONTROL_BRANCH_PREDICT (1<<11)
+#define CONTROL_INSTRUCTION_CACHE (1<<12)
+#define CONTROL_VECTOR (1<<13)
+#define CONTROL_RR (1<<14)
+#define CONTROL_L4 (1<<15)
+#define CONTROL_XP (1<<23)
+#define CONTROL_EE (1<<25)
+
+/*Macro defines for MMU state*/
+#define MMU_CTL (state->mmu.control)
+#define MMU_Enabled (state->mmu.control & CONTROL_MMU)
+#define MMU_Disabled (!(MMU_Enabled))
+#define MMU_Aligned (state->mmu.control & CONTROL_ALIGN_FAULT)
+
+#define MMU_ICacheEnabled (MMU_CTL & CONTROL_INSTRUCTION_CACHE)
+#define MMU_ICacheDisabled (!(MMU_ICacheDisabled))
+
+#define MMU_DCacheEnabled (MMU_CTL & CONTROL_DATA_CACHE)
+#define MMU_DCacheDisabled (!(MMU_DCacheEnabled))
+
+#define MMU_CacheEnabled (MMU_CTL & CONTROL_CACHE)
+#define MMU_CacheDisabled (!(MMU_CacheEnabled))
+
+#define MMU_WBEnabled (MMU_CTL & CONTROL_WRITE_BUFFER)
+#define MMU_WBDisabled (!(MMU_WBEnabled))
+
+/*virt_addr exchange according to CP15.R13(process id virtul mapping)*/
+#define PID_VA_MAP_MASK 0xfe000000
+//#define mmu_pid_va_map(va) ({\
+// ARMword ret; \
+// if ((va) & PID_VA_MAP_MASK)\
+// ret = (va); \
+// else \
+// ret = ((va) | (state->mmu.process_id & PID_VA_MAP_MASK));\
+// ret;\
+//})
+#define mmu_pid_va_map(va) ((va) & PID_VA_MAP_MASK) ? (va) : ((va) | (state->mmu.process_id & PID_VA_MAP_MASK))
+
+/* FS[3:0] in the fault status register: */
+
+typedef enum fault_t
+{
+ NO_FAULT = 0x0,
+ ALIGNMENT_FAULT = 0x1,
+
+ SECTION_TRANSLATION_FAULT = 0x5,
+ PAGE_TRANSLATION_FAULT = 0x7,
+ SECTION_DOMAIN_FAULT = 0x9,
+ PAGE_DOMAIN_FAULT = 0xB,
+ SECTION_PERMISSION_FAULT = 0xD,
+ SUBPAGE_PERMISSION_FAULT = 0xF,
+
+ /* defined by skyeye */
+ TLB_READ_MISS = 0x30,
+ TLB_WRITE_MISS = 0x40,
+
+} fault_t;
+
+#endif /* _ARMMMU_H_ */
--- /dev/null
+/* armos.h -- ARMulator OS definitions: ARM6 Instruction Emulator.
+ Copyright (C) 1994 Advanced RISC Machines Ltd.
+
+ 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 of the License, 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. */
+
+#include <stdint.h>
+
+#if FAST_MEMORY
+/* in user mode, mmap_base will be on initial brk,
+ set at the first mmap request */
+#define mmap_base -1
+#else
+#define mmap_base 0x50000000
+#endif
+static long mmap_next_base = mmap_base;
+
+//static mmap_area_t* new_mmap_area(int sim_addr, int len);
+static char mmap_mem_write(short size, int addr, uint32_t value);
+static char mmap_mem_read(short size, int addr, uint32_t * value);
+
+/***************************************************************************\
+* SWI numbers *
+\***************************************************************************/
+
+#define SWI_Syscall 0x0
+#define SWI_Exit 0x1
+#define SWI_Read 0x3
+#define SWI_Write 0x4
+#define SWI_Open 0x5
+#define SWI_Close 0x6
+#define SWI_Seek 0x13
+#define SWI_Rename 0x26
+#define SWI_Break 0x11
+
+#define SWI_Times 0x2b
+#define SWI_Brk 0x2d
+
+#define SWI_Mmap 0x5a
+#define SWI_Munmap 0x5b
+#define SWI_Mmap2 0xc0
+
+#define SWI_GetUID32 0xc7
+#define SWI_GetGID32 0xc8
+#define SWI_GetEUID32 0xc9
+#define SWI_GetEGID32 0xca
+
+#define SWI_ExitGroup 0xf8
+
+#if 0
+#define SWI_Time 0xd
+#define SWI_Clock 0x61
+#define SWI_Time 0x63
+#define SWI_Remove 0x64
+#define SWI_Rename 0x65
+#define SWI_Flen 0x6c
+#endif
+
+#define SWI_Uname 0x7a
+#define SWI_Fcntl 0xdd
+#define SWI_Fstat64 0xc5
+#define SWI_Gettimeofday 0x4e
+#define SWI_Set_tls 0xf0005
+
+#define SWI_Breakpoint 0x180000 /* see gdb's tm-arm.h */
+
+/***************************************************************************\
+* SWI structures *
+\***************************************************************************/
+
+/* Arm binaries (for now) only support 32 bit, and expect to receive
+ 32-bit compliant structure in return of a systen call. Because
+ we use host system calls to emulate system calls, the returned
+ structure can be 32-bit compliant or 64-bit compliant, depending
+ on the OS running skyeye. Therefore, we need a fixed size structure
+ adapted to arm.*/
+
+/* Borrowed from qemu */
+struct target_stat64 {
+ unsigned short st_dev;
+ unsigned char __pad0[10];
+ uint32_t __st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+ uint32_t st_uid;
+ uint32_t st_gid;
+ unsigned short st_rdev;
+ unsigned char __pad3[10];
+ unsigned char __pad31[4];
+ long long st_size;
+ uint32_t st_blksize;
+ unsigned char __pad32[4];
+ uint32_t st_blocks;
+ uint32_t __pad4;
+ uint32_t st32_atime;
+ uint32_t __pad5;
+ uint32_t st32_mtime;
+ uint32_t __pad6;
+ uint32_t st32_ctime;
+ uint32_t __pad7;
+ unsigned long long st_ino;
+};// __attribute__((packed));
+
+struct target_tms32 {
+ uint32_t tms_utime;
+ uint32_t tms_stime;
+ uint32_t tms_cutime;
+ uint32_t tms_cstime;
+};
+
+struct target_timeval32 {
+ uint32_t tv_sec; /* seconds */
+ uint32_t tv_usec; /* microseconds */
+};
+
+struct target_timezone32 {
+ int32_t tz_minuteswest; /* minutes west of Greenwich */
+ int32_t tz_dsttime; /* type of DST correction */
+};
+
--- /dev/null
+#ifndef CORE_ARM_SKYEYE_DEFS_H_
+#define CORE_ARM_SKYEYE_DEFS_H_
+
+#include "Common.h"
+
+#define MODE32
+#define MODET
+
+typedef struct
+{
+ const char *cpu_arch_name; /*cpu architecture version name.e.g. armv4t */
+ const char *cpu_name; /*cpu name. e.g. arm7tdmi or arm720t */
+ u32 cpu_val; /*CPU value; also call MMU ID or processor id;see
+ ARM Architecture Reference Manual B2-6 */
+ u32 cpu_mask; /*cpu_val's mask. */
+ u32 cachetype; /*this cpu has what kind of cache */
+} cpu_config_t;
+
+typedef struct conf_object_s{
+ char* objname;
+ void* obj;
+ char* class_name;
+}conf_object_t;
+
+typedef enum{
+ /* No exception */
+ No_exp = 0,
+ /* Memory allocation exception */
+ Malloc_exp,
+ /* File open exception */
+ File_open_exp,
+ /* DLL open exception */
+ Dll_open_exp,
+ /* Invalid argument exception */
+ Invarg_exp,
+ /* Invalid module exception */
+ Invmod_exp,
+ /* wrong format exception for config file parsing */
+ Conf_format_exp,
+ /* some reference excess the predefiend range. Such as the index out of array range */
+ Excess_range_exp,
+ /* Can not find the desirable result */
+ Not_found_exp,
+
+ /* Unknown exception */
+ Unknown_exp
+}exception_t;
+
+typedef enum {
+ Align = 0,
+ UnAlign
+}align_t;
+
+typedef enum {
+ Little_endian = 0,
+ Big_endian
+}endian_t;
+//typedef int exception_t;
+
+typedef enum{
+ Phys_addr = 0,
+ Virt_addr
+}addr_type_t;
+
+typedef exception_t(*read_byte_t)(conf_object_t* target, u32 addr, void *buf, size_t count);
+typedef exception_t(*write_byte_t)(conf_object_t* target, u32 addr, const void *buf, size_t count);
+
+typedef struct memory_space{
+ conf_object_t* conf_obj;
+ read_byte_t read;
+ write_byte_t write;
+}memory_space_intf;
+
+
+/*
+ * a running instance for a specific archteciture.
+ */
+typedef struct generic_arch_s
+{
+ char* arch_name;
+ void (*init) (void);
+ void (*reset) (void);
+ void (*step_once) (void);
+ void (*set_pc)(u32 addr);
+ u32 (*get_pc)(void);
+ u32 (*get_step)(void);
+ //chy 2004-04-15
+ //int (*ICE_write_byte) (u32 addr, uint8_t v);
+ //int (*ICE_read_byte)(u32 addr, uint8_t *pv);
+ u32 (*get_regval_by_id)(int id);
+ u32 (*get_regnum)(void);
+ char* (*get_regname_by_id)(int id);
+ exception_t (*set_regval_by_id)(int id, u32 value);
+ /*
+ * read a data by virtual address.
+ */
+ exception_t (*mmu_read)(short size, u32 addr, u32 * value);
+ /*
+ * write a data by a virtual address.
+ */
+ exception_t (*mmu_write)(short size, u32 addr, u32 value);
+ /**
+ * get a signal from external
+ */
+ //exception_t (*signal)(interrupt_signal_t* signal);
+
+ endian_t endianess;
+ align_t alignment;
+} generic_arch_t;
+
+typedef u32 addr_t;
+
+#endif
--- /dev/null
+/*
+ skyeye_types.h - some data types definition for skyeye debugger
+ Copyright (C) 2003 Skyeye Develop Group
+ for help please send mail to <skyeye-developer@lists.sf.linuxforum.net>
+
+ 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 of the License, 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
+
+*/
+/*
+ * 12/16/2006 Michael.Kang <blackfin.kang@gmail.com>
+ */
+
+#pragma once
+
+#include <cstdint>
+
+typedef uint32_t address_t;
+typedef uint32_t physical_address_t;
+typedef uint32_t generic_address_t;
--- /dev/null
+/*
+ * arch/arm/include/asm/vfp.h
+ *
+ * VFP register definitions.
+ * First, the standard VFP set.
+ */
+
+#pragma once
+
+// FPSID Information
+// Note that these are used as values and not as flags.
+enum : u32 {
+ VFP_FPSID_IMPLMEN = 0, // Implementation code. Should be the same as cp15 0 c0 0
+ VFP_FPSID_SW = 0, // Software emulation bit value
+ VFP_FPSID_SUBARCH = 0x2, // Subarchitecture version number
+ VFP_FPSID_PARTNUM = 0x1, // Part number
+ VFP_FPSID_VARIANT = 0x1, // Variant number
+ VFP_FPSID_REVISION = 0x1 // Revision number
+};
+
+// FPEXC bits
+enum : u32 {
+ FPEXC_EX = (1U << 31U),
+ FPEXC_EN = (1 << 30),
+ FPEXC_DEX = (1 << 29),
+ FPEXC_FP2V = (1 << 28),
+ FPEXC_VV = (1 << 27),
+ FPEXC_TFV = (1 << 26),
+ FPEXC_LENGTH_BIT = (8),
+ FPEXC_LENGTH_MASK = (7 << FPEXC_LENGTH_BIT),
+ FPEXC_IDF = (1 << 7),
+ FPEXC_IXF = (1 << 4),
+ FPEXC_UFF = (1 << 3),
+ FPEXC_OFF = (1 << 2),
+ FPEXC_DZF = (1 << 1),
+ FPEXC_IOF = (1 << 0),
+ FPEXC_TRAP_MASK = (FPEXC_IDF|FPEXC_IXF|FPEXC_UFF|FPEXC_OFF|FPEXC_DZF|FPEXC_IOF)
+};
+
+// FPSCR Flags
+enum : u32 {
+ FPSCR_NFLAG = (1U << 31U), // Negative condition flag
+ FPSCR_ZFLAG = (1 << 30), // Zero condition flag
+ FPSCR_CFLAG = (1 << 29), // Carry condition flag
+ FPSCR_VFLAG = (1 << 28), // Overflow condition flag
+
+ FPSCR_QC = (1 << 27), // Cumulative saturation bit
+ FPSCR_AHP = (1 << 26), // Alternative half-precision control bit
+ FPSCR_DEFAULT_NAN = (1 << 25), // Default NaN mode control bit
+ FPSCR_FLUSH_TO_ZERO = (1 << 24), // Flush-to-zero mode control bit
+ FPSCR_RMODE_MASK = (3 << 22), // Rounding Mode bit mask
+ FPSCR_STRIDE_MASK = (3 << 20), // Vector stride bit mask
+ FPSCR_LENGTH_MASK = (7 << 16), // Vector length bit mask
+
+ FPSCR_IDE = (1 << 15), // Input Denormal exception trap enable.
+ FPSCR_IXE = (1 << 12), // Inexact exception trap enable
+ FPSCR_UFE = (1 << 11), // Undeflow exception trap enable
+ FPSCR_OFE = (1 << 10), // Overflow exception trap enable
+ FPSCR_DZE = (1 << 9), // Division by Zero exception trap enable
+ FPSCR_IOE = (1 << 8), // Invalid Operation exception trap enable
+
+ FPSCR_IDC = (1 << 7), // Input Denormal cumulative exception bit
+ FPSCR_IXC = (1 << 4), // Inexact cumulative exception bit
+ FPSCR_UFC = (1 << 3), // Undeflow cumulative exception bit
+ FPSCR_OFC = (1 << 2), // Overflow cumulative exception bit
+ FPSCR_DZC = (1 << 1), // Division by Zero cumulative exception bit
+ FPSCR_IOC = (1 << 0), // Invalid Operation cumulative exception bit
+};
+
+// FPSCR bit offsets
+enum : u32 {
+ FPSCR_RMODE_BIT = 22,
+ FPSCR_STRIDE_BIT = 20,
+ FPSCR_LENGTH_BIT = 16,
+};
+
+// FPSCR rounding modes
+enum : u32 {
+ FPSCR_ROUND_NEAREST = (0 << 22),
+ FPSCR_ROUND_PLUSINF = (1 << 22),
+ FPSCR_ROUND_MINUSINF = (2 << 22),
+ FPSCR_ROUND_TOZERO = (3 << 22)
+};
--- /dev/null
+/*
+ armvfp.c - ARM VFPv3 emulation unit
+ Copyright (C) 2003 Skyeye Develop Group
+ for help please send mail to <skyeye-developer@lists.gro.clinux.org>
+
+ 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 of the License, 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
+*/
+
+/* Note: this file handles interface with arm core and vfp registers */
+
+#include "Kernel.h"
+
+#include "arm/skyeye_common/armdefs.h"
+#include "arm/skyeye_common/vfp/asm_vfp.h"
+#include "arm/skyeye_common/vfp/vfp.h"
+
+#define BITS(s, a, b) ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1))
+#define BIT(s, n) ((s >> (n)) & 1)
+
+unsigned VFPInit(ARMul_State* state)
+{
+ state->VFP[VFP_OFFSET(VFP_FPSID)] = VFP_FPSID_IMPLMEN<<24 | VFP_FPSID_SW<<23 | VFP_FPSID_SUBARCH<<16 |
+ VFP_FPSID_PARTNUM<<8 | VFP_FPSID_VARIANT<<4 | VFP_FPSID_REVISION;
+ state->VFP[VFP_OFFSET(VFP_FPEXC)] = 0;
+ state->VFP[VFP_OFFSET(VFP_FPSCR)] = 0;
+
+ return 0;
+}
+
+unsigned VFPMRC(ARMul_State* state, unsigned type, u32 instr, u32* value)
+{
+ /* MRC<c> <coproc>,<opc1>,<Rt>,<CRn>,<CRm>{,<opc2>} */
+ int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
+ int OPC_1 = BITS(instr, 21, 23);
+ int Rt = BITS(instr, 12, 15);
+ int CRn = BITS(instr, 16, 19);
+ int CRm = BITS(instr, 0, 3);
+ int OPC_2 = BITS(instr, 5, 7);
+
+ /* TODO check access permission */
+
+ /* CRn/opc1 CRm/opc2 */
+
+ if (CoProc == 10 || CoProc == 11)
+ {
+ if (OPC_1 == 0x0 && CRm == 0 && (OPC_2 & 0x3) == 0)
+ {
+ /* VMOV r to s */
+ /* Transfering Rt is not mandatory, as the value of interest is pointed by value */
+ VMOVBRS(state, BIT(instr, 20), Rt, BIT(instr, 7)|CRn<<1, value);
+ return ARMul_DONE;
+ }
+
+ if (OPC_1 == 0x7 && CRm == 0 && OPC_2 == 0)
+ {
+ VMRS(state, CRn, Rt, value);
+ return ARMul_DONE;
+ }
+ }
+ LOG("Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, CRn %x, CRm %x, OPC_2 %x\n",
+ instr, CoProc, OPC_1, Rt, CRn, CRm, OPC_2);
+
+ return ARMul_CANT;
+}
+
+unsigned VFPMCR(ARMul_State* state, unsigned type, u32 instr, u32 value)
+{
+ /* MCR<c> <coproc>,<opc1>,<Rt>,<CRn>,<CRm>{,<opc2>} */
+ int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
+ int OPC_1 = BITS(instr, 21, 23);
+ int Rt = BITS(instr, 12, 15);
+ int CRn = BITS(instr, 16, 19);
+ int CRm = BITS(instr, 0, 3);
+ int OPC_2 = BITS(instr, 5, 7);
+
+ /* TODO check access permission */
+
+ /* CRn/opc1 CRm/opc2 */
+ if (CoProc == 10 || CoProc == 11)
+ {
+ if (OPC_1 == 0x0 && CRm == 0 && (OPC_2 & 0x3) == 0)
+ {
+ /* VMOV s to r */
+ /* Transfering Rt is not mandatory, as the value of interest is pointed by value */
+ VMOVBRS(state, BIT(instr, 20), Rt, BIT(instr, 7)|CRn<<1, &value);
+ return ARMul_DONE;
+ }
+
+ if (OPC_1 == 0x7 && CRm == 0 && OPC_2 == 0)
+ {
+ VMSR(state, CRn, Rt);
+ return ARMul_DONE;
+ }
+
+ if ((OPC_1 & 0x4) == 0 && CoProc == 11 && CRm == 0)
+ {
+ VFP_DEBUG_UNIMPLEMENTED(VMOVBRC);
+ return ARMul_DONE;
+ }
+
+ if (CoProc == 11 && CRm == 0)
+ {
+ VFP_DEBUG_UNIMPLEMENTED(VMOVBCR);
+ return ARMul_DONE;
+ }
+ }
+ LOG("Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, CRn %x, CRm %x, OPC_2 %x\n",
+ instr, CoProc, OPC_1, Rt, CRn, CRm, OPC_2);
+
+ return ARMul_CANT;
+}
+
+unsigned VFPMRRC(ARMul_State* state, unsigned type, u32 instr, u32* value1, u32* value2)
+{
+ /* MCRR<c> <coproc>,<opc1>,<Rt>,<Rt2>,<CRm> */
+ int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
+ int OPC_1 = BITS(instr, 4, 7);
+ int Rt = BITS(instr, 12, 15);
+ int Rt2 = BITS(instr, 16, 19);
+ int CRm = BITS(instr, 0, 3);
+
+ if (CoProc == 10 || CoProc == 11)
+ {
+ if (CoProc == 10 && (OPC_1 & 0xD) == 1)
+ {
+ VMOVBRRSS(state, BIT(instr, 20), Rt, Rt2, BIT(instr, 5)<<4|CRm, value1, value2);
+ return ARMul_DONE;
+ }
+
+ if (CoProc == 11 && (OPC_1 & 0xD) == 1)
+ {
+ /* Transfering Rt and Rt2 is not mandatory, as the value of interest is pointed by value1 and value2 */
+ VMOVBRRD(state, BIT(instr, 20), Rt, Rt2, BIT(instr, 5)<<4|CRm, value1, value2);
+ return ARMul_DONE;
+ }
+ }
+ LOG("Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, Rt2 %x, CRm %x\n",
+ instr, CoProc, OPC_1, Rt, Rt2, CRm);
+
+ return ARMul_CANT;
+}
+
+unsigned VFPMCRR(ARMul_State* state, unsigned type, u32 instr, u32 value1, u32 value2)
+{
+ /* MCRR<c> <coproc>,<opc1>,<Rt>,<Rt2>,<CRm> */
+ int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
+ int OPC_1 = BITS(instr, 4, 7);
+ int Rt = BITS(instr, 12, 15);
+ int Rt2 = BITS(instr, 16, 19);
+ int CRm = BITS(instr, 0, 3);
+
+ /* TODO check access permission */
+
+ /* CRn/opc1 CRm/opc2 */
+
+ if (CoProc == 11 || CoProc == 10)
+ {
+ if (CoProc == 10 && (OPC_1 & 0xD) == 1)
+ {
+ VMOVBRRSS(state, BIT(instr, 20), Rt, Rt2, BIT(instr, 5)<<4|CRm, &value1, &value2);
+ return ARMul_DONE;
+ }
+
+ if (CoProc == 11 && (OPC_1 & 0xD) == 1)
+ {
+ /* Transfering Rt and Rt2 is not mandatory, as the value of interest is pointed by value1 and value2 */
+ VMOVBRRD(state, BIT(instr, 20), Rt, Rt2, BIT(instr, 5)<<4|CRm, &value1, &value2);
+ return ARMul_DONE;
+ }
+ }
+ LOG("Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, Rt2 %x, CRm %x\n",
+ instr, CoProc, OPC_1, Rt, Rt2, CRm);
+
+ return ARMul_CANT;
+}
+
+unsigned VFPSTC(ARMul_State* state, unsigned type, u32 instr, u32 * value)
+{
+ /* STC{L}<c> <coproc>,<CRd>,[<Rn>],<option> */
+ int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
+ int CRd = BITS(instr, 12, 15);
+ int Rn = BITS(instr, 16, 19);
+ int imm8 = BITS(instr, 0, 7);
+ int P = BIT(instr, 24);
+ int U = BIT(instr, 23);
+ int D = BIT(instr, 22);
+ int W = BIT(instr, 21);
+
+ /* TODO check access permission */
+
+ /* VSTM */
+ if ( (P|U|D|W) == 0 ) {
+ LOG("In %s, UNDEFINED\n", __FUNCTION__);
+ exit(-1);
+ }
+ if (CoProc == 10 || CoProc == 11) {
+#if 1
+ if (P == 0 && U == 0 && W == 0) {
+ LOG("VSTM Related encodings\n");
+ exit(-1);
+ }
+ if (P == U && W == 1) {
+ LOG("UNDEFINED\n");
+ exit(-1);
+ }
+#endif
+
+ if (P == 1 && W == 0)
+ {
+ return VSTR(state, type, instr, value);
+ }
+
+ if (P == 1 && U == 0 && W == 1 && Rn == 0xD)
+ {
+ return VPUSH(state, type, instr, value);
+ }
+
+ return VSTM(state, type, instr, value);
+ }
+ LOG("Can't identify %x, CoProc %x, CRd %x, Rn %x, imm8 %x, P %x, U %x, D %x, W %x\n",
+ instr, CoProc, CRd, Rn, imm8, P, U, D, W);
+
+ return ARMul_CANT;
+}
+
+unsigned VFPLDC(ARMul_State* state, unsigned type, u32 instr, u32 value)
+{
+ /* LDC{L}<c> <coproc>,<CRd>,[<Rn>] */
+ int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
+ int CRd = BITS(instr, 12, 15);
+ int Rn = BITS(instr, 16, 19);
+ int imm8 = BITS(instr, 0, 7);
+ int P = BIT(instr, 24);
+ int U = BIT(instr, 23);
+ int D = BIT(instr, 22);
+ int W = BIT(instr, 21);
+
+ /* TODO check access permission */
+
+ if ( (P|U|D|W) == 0 ) {
+ LOG("In %s, UNDEFINED\n", __FUNCTION__);
+ exit(-1);
+ }
+ if (CoProc == 10 || CoProc == 11)
+ {
+ if (P == 1 && W == 0)
+ {
+ return VLDR(state, type, instr, value);
+ }
+
+ if (P == 0 && U == 1 && W == 1 && Rn == 0xD)
+ {
+ return VPOP(state, type, instr, value);
+ }
+
+ return VLDM(state, type, instr, value);
+ }
+ LOG("Can't identify %x, CoProc %x, CRd %x, Rn %x, imm8 %x, P %x, U %x, D %x, W %x\n",
+ instr, CoProc, CRd, Rn, imm8, P, U, D, W);
+
+ return ARMul_CANT;
+}
+
+unsigned VFPCDP(ARMul_State* state, unsigned type, u32 instr)
+{
+ /* CDP<c> <coproc>,<opc1>,<CRd>,<CRn>,<CRm>,<opc2> */
+ int CoProc = BITS(instr, 8, 11); /* 10 or 11 */
+ int OPC_1 = BITS(instr, 20, 23);
+ int CRd = BITS(instr, 12, 15);
+ int CRn = BITS(instr, 16, 19);
+ int CRm = BITS(instr, 0, 3);
+ int OPC_2 = BITS(instr, 5, 7);
+
+ /* TODO check access permission */
+
+ /* CRn/opc1 CRm/opc2 */
+
+ if (CoProc == 10 || CoProc == 11)
+ {
+ if ((OPC_1 & 0xB) == 0xB && BITS(instr, 4, 7) == 0)
+ {
+ unsigned int single = BIT(instr, 8) == 0;
+ unsigned int d = (single ? BITS(instr, 12,15)<<1 | BIT(instr, 22) : BITS(instr, 12,15) | BIT(instr, 22)<<4);
+ unsigned int imm;
+ instr = BITS(instr, 16, 19) << 4 | BITS(instr, 0, 3); // FIXME dirty workaround to get a correct imm
+
+ if (single)
+ imm = BIT(instr, 7)<<31 | (BIT(instr, 6)==0)<<30 | (BIT(instr, 6) ? 0x1f : 0)<<25 | BITS(instr, 0, 5)<<19;
+ else
+ imm = BIT(instr, 7)<<31 | (BIT(instr, 6)==0)<<30 | (BIT(instr, 6) ? 0xff : 0)<<22 | BITS(instr, 0, 5)<<16;
+
+ VMOVI(state, single, d, imm);
+ return ARMul_DONE;
+ }
+
+ if ((OPC_1 & 0xB) == 0xB && CRn == 0 && (OPC_2 & 0x6) == 0x2)
+ {
+ unsigned int single = BIT(instr, 8) == 0;
+ unsigned int d = (single ? BITS(instr, 12,15)<<1 | BIT(instr, 22) : BITS(instr, 12,15) | BIT(instr, 22)<<4);
+ unsigned int m = (single ? BITS(instr, 0, 3)<<1 | BIT(instr, 5) : BITS(instr, 0, 3) | BIT(instr, 5)<<4);
+ VMOVR(state, single, d, m);
+ return ARMul_DONE;
+ }
+
+ int exceptions = 0;
+ if (CoProc == 10)
+ exceptions = vfp_single_cpdo(state, instr, state->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ exceptions = vfp_double_cpdo(state, instr, state->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ vfp_raise_exceptions(state, exceptions, instr, state->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ return ARMul_DONE;
+ }
+ LOG("Can't identify %x\n", instr);
+ return ARMul_CANT;
+}
+
+/* ----------- MRC ------------ */
+void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value)
+{
+ if (to_arm)
+ {
+ *value = state->ExtReg[n];
+ }
+ else
+ {
+ state->ExtReg[n] = *value;
+ }
+}
+void VMRS(ARMul_State* state, ARMword reg, ARMword Rt, ARMword* value)
+{
+ if (reg == 1)
+ {
+ if (Rt != 15)
+ {
+ *value = state->VFP[VFP_OFFSET(VFP_FPSCR)];
+ }
+ else
+ {
+ *value = state->VFP[VFP_OFFSET(VFP_FPSCR)] ;
+ }
+ }
+ else
+ {
+ switch (reg)
+ {
+ case 0:
+ *value = state->VFP[VFP_OFFSET(VFP_FPSID)];
+ break;
+ case 6:
+ /* MVFR1, VFPv3 only ? */
+ LOG("\tr%d <= MVFR1 unimplemented\n", Rt);
+ break;
+ case 7:
+ /* MVFR0, VFPv3 only? */
+ LOG("\tr%d <= MVFR0 unimplemented\n", Rt);
+ break;
+ case 8:
+ *value = state->VFP[VFP_OFFSET(VFP_FPEXC)];
+ break;
+ default:
+ LOG("\tSUBARCHITECTURE DEFINED\n");
+ break;
+ }
+ }
+}
+void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2)
+{
+ if (to_arm)
+ {
+ *value2 = state->ExtReg[n*2+1];
+ *value1 = state->ExtReg[n*2];
+ }
+ else
+ {
+ state->ExtReg[n*2+1] = *value2;
+ state->ExtReg[n*2] = *value1;
+ }
+}
+void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2)
+{
+ if (to_arm)
+ {
+ *value1 = state->ExtReg[n+0];
+ *value2 = state->ExtReg[n+1];
+ }
+ else
+ {
+ state->ExtReg[n+0] = *value1;
+ state->ExtReg[n+1] = *value2;
+ }
+}
+
+/* ----------- MCR ------------ */
+void VMSR(ARMul_State* state, ARMword reg, ARMword Rt)
+{
+ if (reg == 1)
+ {
+ state->VFP[VFP_OFFSET(VFP_FPSCR)] = state->Reg[Rt];
+ }
+ else if (reg == 8)
+ {
+ state->VFP[VFP_OFFSET(VFP_FPEXC)] = state->Reg[Rt];
+ }
+}
+
+/* Memory operation are not inlined, as old Interpreter and Fast interpreter
+ don't have the same memory operation interface.
+ Old interpreter framework does one access to coprocessor per data, and
+ handles already data write, as well as address computation,
+ which is not the case for Fast interpreter. Therefore, implementation
+ of vfp instructions in old interpreter and fast interpreter are separate. */
+
+/* ----------- STC ------------ */
+int VSTR(ARMul_State* state, int type, ARMword instr, ARMword* value)
+{
+ static int i = 0;
+ static int single_reg, add, d, n, imm32, regs;
+ if (type == ARMul_FIRST)
+ {
+ single_reg = BIT(instr, 8) == 0; // Double precision
+ add = BIT(instr, 23);
+ imm32 = BITS(instr, 0,7)<<2; // may not be used
+ d = single_reg ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); /* Base register */
+ n = BITS(instr, 16, 19); // destination register
+
+ i = 0;
+ regs = 1;
+
+ return ARMul_DONE;
+ }
+ else if (type == ARMul_DATA)
+ {
+ if (single_reg)
+ {
+ *value = state->ExtReg[d+i];
+ i++;
+ if (i < regs)
+ return ARMul_INC;
+ else
+ return ARMul_DONE;
+ }
+ else
+ {
+ /* FIXME Careful of endianness, may need to rework this */
+ *value = state->ExtReg[d*2+i];
+ i++;
+ if (i < regs*2)
+ return ARMul_INC;
+ else
+ return ARMul_DONE;
+ }
+ }
+
+ return -1;
+}
+int VPUSH(ARMul_State* state, int type, ARMword instr, ARMword* value)
+{
+ static int i = 0;
+ static int single_regs, d, imm32, regs;
+ if (type == ARMul_FIRST)
+ {
+ single_regs = BIT(instr, 8) == 0; // Single precision
+ d = single_regs ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register
+ imm32 = BITS(instr, 0,7)<<2; // may not be used
+ regs = single_regs ? BITS(instr, 0, 7) : BITS(instr, 1, 7); // FSTMX if regs is odd
+
+ state->Reg[R13] = state->Reg[R13] - imm32;
+
+ i = 0;
+
+ return ARMul_DONE;
+ }
+ else if (type == ARMul_DATA)
+ {
+ if (single_regs)
+ {
+ *value = state->ExtReg[d + i];
+ i++;
+ if (i < regs)
+ return ARMul_INC;
+ else
+ return ARMul_DONE;
+ }
+ else
+ {
+ /* FIXME Careful of endianness, may need to rework this */
+ *value = state->ExtReg[d*2 + i];
+ i++;
+ if (i < regs*2)
+ return ARMul_INC;
+ else
+ return ARMul_DONE;
+ }
+ }
+
+ return -1;
+}
+int VSTM(ARMul_State* state, int type, ARMword instr, ARMword* value)
+{
+ static int i = 0;
+ static int single_regs, add, wback, d, n, imm32, regs;
+ if (type == ARMul_FIRST)
+ {
+ single_regs = BIT(instr, 8) == 0; // Single precision
+ add = BIT(instr, 23);
+ wback = BIT(instr, 21); // write-back
+ d = single_regs ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register
+ n = BITS(instr, 16, 19); // destination register
+ imm32 = BITS(instr, 0,7) * 4; // may not be used
+ regs = single_regs ? BITS(instr, 0, 7) : BITS(instr, 0, 7)>>1; // FSTMX if regs is odd
+
+ if (wback) {
+ state->Reg[n] = (add ? state->Reg[n] + imm32 : state->Reg[n] - imm32);
+ }
+
+ i = 0;
+
+ return ARMul_DONE;
+ }
+ else if (type == ARMul_DATA)
+ {
+ if (single_regs)
+ {
+ *value = state->ExtReg[d + i];
+ i++;
+ if (i < regs)
+ return ARMul_INC;
+ else
+ return ARMul_DONE;
+ }
+ else
+ {
+ /* FIXME Careful of endianness, may need to rework this */
+ *value = state->ExtReg[d*2 + i];
+ i++;
+ if (i < regs*2)
+ return ARMul_INC;
+ else
+ return ARMul_DONE;
+ }
+ }
+
+ return -1;
+}
+
+/* ----------- LDC ------------ */
+int VPOP(ARMul_State* state, int type, ARMword instr, ARMword value)
+{
+ static int i = 0;
+ static int single_regs, d, imm32, regs;
+ if (type == ARMul_FIRST)
+ {
+ single_regs = BIT(instr, 8) == 0; // Single precision
+ d = single_regs ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register
+ imm32 = BITS(instr, 0, 7)<<2; // may not be used
+ regs = single_regs ? BITS(instr, 0, 7) : BITS(instr, 1, 7); // FLDMX if regs is odd
+
+ state->Reg[R13] = state->Reg[R13] + imm32;
+
+ i = 0;
+
+ return ARMul_DONE;
+ }
+ else if (type == ARMul_TRANSFER)
+ {
+ return ARMul_DONE;
+ }
+ else if (type == ARMul_DATA)
+ {
+ if (single_regs)
+ {
+ state->ExtReg[d + i] = value;
+ i++;
+ if (i < regs)
+ return ARMul_INC;
+ else
+ return ARMul_DONE;
+ }
+ else
+ {
+ /* FIXME Careful of endianness, may need to rework this */
+ state->ExtReg[d*2 + i] = value;
+ i++;
+ if (i < regs*2)
+ return ARMul_INC;
+ else
+ return ARMul_DONE;
+ }
+ }
+
+ return -1;
+}
+int VLDR(ARMul_State* state, int type, ARMword instr, ARMword value)
+{
+ static int i = 0;
+ static int single_reg, add, d, n, imm32, regs;
+ if (type == ARMul_FIRST)
+ {
+ single_reg = BIT(instr, 8) == 0; // Double precision
+ add = BIT(instr, 23);
+ imm32 = BITS(instr, 0, 7)<<2; // may not be used
+ d = single_reg ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register
+ n = BITS(instr, 16, 19); // destination register
+
+ i = 0;
+ regs = 1;
+
+ return ARMul_DONE;
+ }
+ else if (type == ARMul_TRANSFER)
+ {
+ return ARMul_DONE;
+ }
+ else if (type == ARMul_DATA)
+ {
+ if (single_reg)
+ {
+ state->ExtReg[d+i] = value;
+ i++;
+ if (i < regs)
+ return ARMul_INC;
+ else
+ return ARMul_DONE;
+ }
+ else
+ {
+ /* FIXME Careful of endianness, may need to rework this */
+ state->ExtReg[d*2+i] = value;
+ i++;
+ if (i < regs*2)
+ return ARMul_INC;
+ else
+ return ARMul_DONE;
+ }
+ }
+
+ return -1;
+}
+int VLDM(ARMul_State* state, int type, ARMword instr, ARMword value)
+{
+ static int i = 0;
+ static int single_regs, add, wback, d, n, imm32, regs;
+ if (type == ARMul_FIRST)
+ {
+ single_regs = BIT(instr, 8) == 0; // Single precision
+ add = BIT(instr, 23);
+ wback = BIT(instr, 21); // write-back
+ d = single_regs ? BITS(instr, 12, 15)<<1|BIT(instr, 22) : BIT(instr, 22)<<4|BITS(instr, 12, 15); // Base register
+ n = BITS(instr, 16, 19); // destination register
+ imm32 = BITS(instr, 0, 7) * 4; // may not be used
+ regs = single_regs ? BITS(instr, 0, 7) : BITS(instr, 0, 7)>>1; // FLDMX if regs is odd
+
+ if (wback) {
+ state->Reg[n] = (add ? state->Reg[n] + imm32 : state->Reg[n] - imm32);
+ }
+
+ i = 0;
+
+ return ARMul_DONE;
+ }
+ else if (type == ARMul_DATA)
+ {
+ if (single_regs)
+ {
+ state->ExtReg[d + i] = value;
+ i++;
+ if (i < regs)
+ return ARMul_INC;
+ else
+ return ARMul_DONE;
+ }
+ else
+ {
+ /* FIXME Careful of endianness, may need to rework this */
+ state->ExtReg[d*2 + i] = value;
+ i++;
+ if (i < regs*2)
+ return ARMul_INC;
+ else
+ return ARMul_DONE;
+ }
+ }
+
+ return -1;
+}
+
+/* ----------- CDP ------------ */
+void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm)
+{
+ if (single)
+ {
+ state->ExtReg[d] = imm;
+ }
+ else
+ {
+ /* Check endian please */
+ state->ExtReg[d*2+1] = imm;
+ state->ExtReg[d*2] = 0;
+ }
+}
+void VMOVR(ARMul_State* state, ARMword single, ARMword d, ARMword m)
+{
+ if (single)
+ {
+ state->ExtReg[d] = state->ExtReg[m];
+ }
+ else
+ {
+ /* Check endian please */
+ state->ExtReg[d*2+1] = state->ExtReg[m*2+1];
+ state->ExtReg[d*2] = state->ExtReg[m*2];
+ }
+}
+
+/* Miscellaneous functions */
+int32_t vfp_get_float(ARMul_State* state, unsigned int reg)
+{
+ //LOG("VFP get float: s%d=[%08x]\n", reg, state->ExtReg[reg]);
+ return state->ExtReg[reg];
+}
+
+void vfp_put_float(ARMul_State* state, int32_t val, unsigned int reg)
+{
+ //LOG( "VFP put float: s%d <= [%08x]\n", reg, val);
+ state->ExtReg[reg] = val;
+}
+
+uint64_t vfp_get_double(ARMul_State* state, unsigned int reg)
+{
+ uint64_t result = ((uint64_t) state->ExtReg[reg*2+1])<<32 | state->ExtReg[reg*2];
+ //LOG("VFP get double: s[%d-%d]=[%016llx]\n", reg * 2 + 1, reg * 2, result);
+ return result;
+}
+
+void vfp_put_double(ARMul_State* state, uint64_t val, unsigned int reg)
+{
+ //LOG("VFP put double: s[%d-%d] <= [%08x-%08x]\n", reg * 2 + 1, reg * 2, (uint32_t)(val >> 32), (uint32_t)(val & 0xffffffff));
+ state->ExtReg[reg*2] = (uint32_t) (val & 0xffffffff);
+ state->ExtReg[reg*2+1] = (uint32_t) (val>>32);
+}
+
+/*
+ * Process bitmask of exception conditions. (from vfpmodule.c)
+ */
+void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpscr)
+{
+ //LOG("VFP: raising exceptions %08x\n", exceptions);
+
+ if (exceptions == VFP_EXCEPTION_ERROR) {
+ LOG("unhandled bounce %x\n", inst);
+ exit(-1);
+ return;
+ }
+
+ /*
+ * If any of the status flags are set, update the FPSCR.
+ * Comparison instructions always return at least one of
+ * these flags set.
+ */
+ if (exceptions & (FPSCR_NFLAG|FPSCR_ZFLAG|FPSCR_CFLAG|FPSCR_VFLAG))
+ fpscr &= ~(FPSCR_NFLAG|FPSCR_ZFLAG|FPSCR_CFLAG|FPSCR_VFLAG);
+
+ fpscr |= exceptions;
+
+ state->VFP[VFP_OFFSET(VFP_FPSCR)] = fpscr;
+}
--- /dev/null
+/*
+ vfp/vfp.h - ARM VFPv3 emulation unit - vfp interface
+ Copyright (C) 2003 Skyeye Develop Group
+ for help please send mail to <skyeye-developer@lists.gro.clinux.org>
+
+ 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 of the License, 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
+*/
+
+#pragma once
+
+#include "arm/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */
+
+#define VFP_DEBUG_UNIMPLEMENTED(x) LOG("in func %s, " #x " unimplemented\n", __FUNCTION__); exit(-1);
+#define VFP_DEBUG_UNTESTED(x) LOG("in func %s, " #x " untested\n", __FUNCTION__);
+#define CHECK_VFP_ENABLED
+#define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); //if (ret == -1) {printf("VFP CDP FAILURE %x\n", inst_cream->instr); exit(-1);}
+
+unsigned VFPInit(ARMul_State* state);
+unsigned VFPMRC(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
+unsigned VFPMCR(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
+unsigned VFPMRRC(ARMul_State* state, unsigned type, ARMword instr, ARMword* value1, ARMword* value2);
+unsigned VFPMCRR(ARMul_State* state, unsigned type, ARMword instr, ARMword value1, ARMword value2);
+unsigned VFPSTC(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
+unsigned VFPLDC(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
+unsigned VFPCDP(ARMul_State* state, unsigned type, ARMword instr);
+
+s32 vfp_get_float(ARMul_State* state, u32 reg);
+void vfp_put_float(ARMul_State* state, s32 val, u32 reg);
+u64 vfp_get_double(ARMul_State* state, u32 reg);
+void vfp_put_double(ARMul_State* state, u64 val, u32 reg);
+void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpscr);
+u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr);
+u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr);
+
+// MRC
+void VMRS(ARMul_State* state, ARMword reg, ARMword Rt, ARMword* value);
+void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value);
+void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2);
+void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2);
+void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm);
+void VMOVR(ARMul_State* state, ARMword single, ARMword d, ARMword imm);
+
+// MCR
+void VMSR(ARMul_State* state, ARMword reg, ARMword Rt);
+
+// STC
+int VSTM(ARMul_State* state, int type, ARMword instr, ARMword* value);
+int VPUSH(ARMul_State* state, int type, ARMword instr, ARMword* value);
+int VSTR(ARMul_State* state, int type, ARMword instr, ARMword* value);
+
+// LDC
+int VLDM(ARMul_State* state, int type, ARMword instr, ARMword value);
+int VPOP(ARMul_State* state, int type, ARMword instr, ARMword value);
+int VLDR(ARMul_State* state, int type, ARMword instr, ARMword value);
--- /dev/null
+/*
+ vfp/vfp.h - ARM VFPv3 emulation unit - SoftFloat lib helper
+ Copyright (C) 2003 Skyeye Develop Group
+ for help please send mail to <skyeye-developer@lists.gro.clinux.org>
+
+ 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 of the License, 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
+*/
+
+/*
+ * The following code is derivative from Linux Android kernel vfp
+ * floating point support.
+ *
+ * Copyright (C) 2004 ARM Limited.
+ * Written by Deep Blue Solutions Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#pragma once
+
+#include <cstdio>
+#include "arm/skyeye_common/armdefs.h"
+
+#define pr_info //printf
+#define pr_debug //printf
+
+#define do_div(n, base) {n/=base;}
+
+enum : u32 {
+ FOP_MASK = 0x00b00040,
+ FOP_FMAC = 0x00000000,
+ FOP_FNMAC = 0x00000040,
+ FOP_FMSC = 0x00100000,
+ FOP_FNMSC = 0x00100040,
+ FOP_FMUL = 0x00200000,
+ FOP_FNMUL = 0x00200040,
+ FOP_FADD = 0x00300000,
+ FOP_FSUB = 0x00300040,
+ FOP_FDIV = 0x00800000,
+ FOP_EXT = 0x00b00040
+};
+
+#define FOP_TO_IDX(inst) ((inst & 0x00b00000) >> 20 | (inst & (1 << 6)) >> 4)
+
+enum : u32 {
+ FEXT_MASK = 0x000f0080,
+ FEXT_FCPY = 0x00000000,
+ FEXT_FABS = 0x00000080,
+ FEXT_FNEG = 0x00010000,
+ FEXT_FSQRT = 0x00010080,
+ FEXT_FCMP = 0x00040000,
+ FEXT_FCMPE = 0x00040080,
+ FEXT_FCMPZ = 0x00050000,
+ FEXT_FCMPEZ = 0x00050080,
+ FEXT_FCVT = 0x00070080,
+ FEXT_FUITO = 0x00080000,
+ FEXT_FSITO = 0x00080080,
+ FEXT_FTOUI = 0x000c0000,
+ FEXT_FTOUIZ = 0x000c0080,
+ FEXT_FTOSI = 0x000d0000,
+ FEXT_FTOSIZ = 0x000d0080
+};
+
+#define FEXT_TO_IDX(inst) ((inst & 0x000f0000) >> 15 | (inst & (1 << 7)) >> 7)
+
+#define vfp_get_sd(inst) ((inst & 0x0000f000) >> 11 | (inst & (1 << 22)) >> 22)
+#define vfp_get_dd(inst) ((inst & 0x0000f000) >> 12 | (inst & (1 << 22)) >> 18)
+#define vfp_get_sm(inst) ((inst & 0x0000000f) << 1 | (inst & (1 << 5)) >> 5)
+#define vfp_get_dm(inst) ((inst & 0x0000000f) | (inst & (1 << 5)) >> 1)
+#define vfp_get_sn(inst) ((inst & 0x000f0000) >> 15 | (inst & (1 << 7)) >> 7)
+#define vfp_get_dn(inst) ((inst & 0x000f0000) >> 16 | (inst & (1 << 7)) >> 3)
+
+#define vfp_single(inst) (((inst) & 0x0000f00) == 0xa00)
+
+static inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift)
+{
+ if (shift) {
+ if (shift < 32)
+ val = val >> shift | ((val << (32 - shift)) != 0);
+ else
+ val = val != 0;
+ }
+ return val;
+}
+
+static inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift)
+{
+ if (shift) {
+ if (shift < 64)
+ val = val >> shift | ((val << (64 - shift)) != 0);
+ else
+ val = val != 0;
+ }
+ return val;
+}
+
+static inline u32 vfp_hi64to32jamming(u64 val)
+{
+ u32 v;
+ u32 highval = val >> 32;
+ u32 lowval = val & 0xffffffff;
+
+ if (lowval >= 1)
+ v = highval | 1;
+ else
+ v = highval;
+
+ return v;
+}
+
+static inline void add128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
+{
+ *resl = nl + ml;
+ *resh = nh + mh;
+ if (*resl < nl)
+ *resh += 1;
+}
+
+static inline void sub128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
+{
+ *resl = nl - ml;
+ *resh = nh - mh;
+ if (*resl > nl)
+ *resh -= 1;
+}
+
+static inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m)
+{
+ u32 nh, nl, mh, ml;
+ u64 rh, rma, rmb, rl;
+
+ nl = static_cast<u32>(n);
+ ml = static_cast<u32>(m);
+ rl = (u64)nl * ml;
+
+ nh = n >> 32;
+ rma = (u64)nh * ml;
+
+ mh = m >> 32;
+ rmb = (u64)nl * mh;
+ rma += rmb;
+
+ rh = (u64)nh * mh;
+ rh += ((u64)(rma < rmb) << 32) + (rma >> 32);
+
+ rma <<= 32;
+ rl += rma;
+ rh += (rl < rma);
+
+ *resl = rl;
+ *resh = rh;
+}
+
+static inline void shift64left(u64* resh, u64* resl, u64 n)
+{
+ *resh = n >> 63;
+ *resl = n << 1;
+}
+
+static inline u64 vfp_hi64multiply64(u64 n, u64 m)
+{
+ u64 rh, rl;
+ mul64to128(&rh, &rl, n, m);
+ return rh | (rl != 0);
+}
+
+static inline u64 vfp_estimate_div128to64(u64 nh, u64 nl, u64 m)
+{
+ u64 mh, ml, remh, reml, termh, terml, z;
+
+ if (nh >= m)
+ return ~0ULL;
+ mh = m >> 32;
+ if (mh << 32 <= nh) {
+ z = 0xffffffff00000000ULL;
+ } else {
+ z = nh;
+ do_div(z, mh);
+ z <<= 32;
+ }
+ mul64to128(&termh, &terml, m, z);
+ sub128(&remh, &reml, nh, nl, termh, terml);
+ ml = m << 32;
+ while ((s64)remh < 0) {
+ z -= 0x100000000ULL;
+ add128(&remh, &reml, remh, reml, mh, ml);
+ }
+ remh = (remh << 32) | (reml >> 32);
+ if (mh << 32 <= remh) {
+ z |= 0xffffffff;
+ } else {
+ do_div(remh, mh);
+ z |= remh;
+ }
+ return z;
+}
+
+// Operations on unpacked elements
+#define vfp_sign_negate(sign) (sign ^ 0x8000)
+
+// Single-precision
+struct vfp_single {
+ s16 exponent;
+ u16 sign;
+ u32 significand;
+};
+
+// VFP_SINGLE_MANTISSA_BITS - number of bits in the mantissa
+// VFP_SINGLE_EXPONENT_BITS - number of bits in the exponent
+// VFP_SINGLE_LOW_BITS - number of low bits in the unpacked significand
+// which are not propagated to the float upon packing.
+#define VFP_SINGLE_MANTISSA_BITS (23)
+#define VFP_SINGLE_EXPONENT_BITS (8)
+#define VFP_SINGLE_LOW_BITS (32 - VFP_SINGLE_MANTISSA_BITS - 2)
+#define VFP_SINGLE_LOW_BITS_MASK ((1 << VFP_SINGLE_LOW_BITS) - 1)
+
+// The bit in an unpacked float which indicates that it is a quiet NaN
+#define VFP_SINGLE_SIGNIFICAND_QNAN (1 << (VFP_SINGLE_MANTISSA_BITS - 1 + VFP_SINGLE_LOW_BITS))
+
+// Operations on packed single-precision numbers
+#define vfp_single_packed_sign(v) ((v) & 0x80000000)
+#define vfp_single_packed_negate(v) ((v) ^ 0x80000000)
+#define vfp_single_packed_abs(v) ((v) & ~0x80000000)
+#define vfp_single_packed_exponent(v) (((v) >> VFP_SINGLE_MANTISSA_BITS) & ((1 << VFP_SINGLE_EXPONENT_BITS) - 1))
+#define vfp_single_packed_mantissa(v) ((v) & ((1 << VFP_SINGLE_MANTISSA_BITS) - 1))
+
+// Unpack a single-precision float. Note that this returns the magnitude
+// of the single-precision float mantissa with the 1. if necessary,
+// aligned to bit 30.
+static inline void vfp_single_unpack(vfp_single* s, s32 val)
+{
+ u32 significand;
+
+ s->sign = vfp_single_packed_sign(val) >> 16,
+ s->exponent = vfp_single_packed_exponent(val);
+
+ significand = (u32) val;
+ significand = (significand << (32 - VFP_SINGLE_MANTISSA_BITS)) >> 2;
+ if (s->exponent && s->exponent != 255)
+ significand |= 0x40000000;
+ s->significand = significand;
+}
+
+// Re-pack a single-precision float. This assumes that the float is
+// already normalised such that the MSB is bit 30, _not_ bit 31.
+static inline s32 vfp_single_pack(vfp_single* s)
+{
+ u32 val = (s->sign << 16) +
+ (s->exponent << VFP_SINGLE_MANTISSA_BITS) +
+ (s->significand >> VFP_SINGLE_LOW_BITS);
+ return (s32)val;
+}
+
+enum : u32 {
+ VFP_NUMBER = (1 << 0),
+ VFP_ZERO = (1 << 1),
+ VFP_DENORMAL = (1 << 2),
+ VFP_INFINITY = (1 << 3),
+ VFP_NAN = (1 << 4),
+ VFP_NAN_SIGNAL = (1 << 5),
+
+ VFP_QNAN = (VFP_NAN),
+ VFP_SNAN = (VFP_NAN|VFP_NAN_SIGNAL)
+};
+
+static inline int vfp_single_type(vfp_single* s)
+{
+ int type = VFP_NUMBER;
+ if (s->exponent == 255) {
+ if (s->significand == 0)
+ type = VFP_INFINITY;
+ else if (s->significand & VFP_SINGLE_SIGNIFICAND_QNAN)
+ type = VFP_QNAN;
+ else
+ type = VFP_SNAN;
+ } else if (s->exponent == 0) {
+ if (s->significand == 0)
+ type |= VFP_ZERO;
+ else
+ type |= VFP_DENORMAL;
+ }
+ return type;
+}
+
+
+u32 vfp_single_normaliseround(ARMul_State* state, int sd, vfp_single* vs, u32 fpscr, u32 exceptions, const char* func);
+
+// Double-precision
+struct vfp_double {
+ s16 exponent;
+ u16 sign;
+ u64 significand;
+};
+
+// VFP_REG_ZERO is a special register number for vfp_get_double
+// which returns (double)0.0. This is useful for the compare with
+// zero instructions.
+#ifdef CONFIG_VFPv3
+#define VFP_REG_ZERO 32
+#else
+#define VFP_REG_ZERO 16
+#endif
+
+#define VFP_DOUBLE_MANTISSA_BITS (52)
+#define VFP_DOUBLE_EXPONENT_BITS (11)
+#define VFP_DOUBLE_LOW_BITS (64 - VFP_DOUBLE_MANTISSA_BITS - 2)
+#define VFP_DOUBLE_LOW_BITS_MASK ((1 << VFP_DOUBLE_LOW_BITS) - 1)
+
+// The bit in an unpacked double which indicates that it is a quiet NaN
+#define VFP_DOUBLE_SIGNIFICAND_QNAN (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1 + VFP_DOUBLE_LOW_BITS))
+
+// Operations on packed single-precision numbers
+#define vfp_double_packed_sign(v) ((v) & (1ULL << 63))
+#define vfp_double_packed_negate(v) ((v) ^ (1ULL << 63))
+#define vfp_double_packed_abs(v) ((v) & ~(1ULL << 63))
+#define vfp_double_packed_exponent(v) (((v) >> VFP_DOUBLE_MANTISSA_BITS) & ((1 << VFP_DOUBLE_EXPONENT_BITS) - 1))
+#define vfp_double_packed_mantissa(v) ((v) & ((1ULL << VFP_DOUBLE_MANTISSA_BITS) - 1))
+
+// Unpack a double-precision float. Note that this returns the magnitude
+// of the double-precision float mantissa with the 1. if necessary,
+// aligned to bit 62.
+static inline void vfp_double_unpack(vfp_double* s, s64 val)
+{
+ u64 significand;
+
+ s->sign = vfp_double_packed_sign(val) >> 48;
+ s->exponent = vfp_double_packed_exponent(val);
+
+ significand = (u64) val;
+ significand = (significand << (64 - VFP_DOUBLE_MANTISSA_BITS)) >> 2;
+ if (s->exponent && s->exponent != 2047)
+ significand |= (1ULL << 62);
+ s->significand = significand;
+}
+
+// Re-pack a double-precision float. This assumes that the float is
+// already normalised such that the MSB is bit 30, _not_ bit 31.
+static inline s64 vfp_double_pack(vfp_double* s)
+{
+ u64 val = ((u64)s->sign << 48) +
+ ((u64)s->exponent << VFP_DOUBLE_MANTISSA_BITS) +
+ (s->significand >> VFP_DOUBLE_LOW_BITS);
+ return (s64)val;
+}
+
+static inline int vfp_double_type(vfp_double* s)
+{
+ int type = VFP_NUMBER;
+ if (s->exponent == 2047) {
+ if (s->significand == 0)
+ type = VFP_INFINITY;
+ else if (s->significand & VFP_DOUBLE_SIGNIFICAND_QNAN)
+ type = VFP_QNAN;
+ else
+ type = VFP_SNAN;
+ } else if (s->exponent == 0) {
+ if (s->significand == 0)
+ type |= VFP_ZERO;
+ else
+ type |= VFP_DENORMAL;
+ }
+ return type;
+}
+
+u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand);
+
+// A special flag to tell the normalisation code not to normalise.
+#define VFP_NAN_FLAG 0x100
+
+// A bit pattern used to indicate the initial (unset) value of the
+// exception mask, in case nothing handles an instruction. This
+// doesn't include the NAN flag, which get masked out before
+// we check for an error.
+#define VFP_EXCEPTION_ERROR ((u32)-1 & ~VFP_NAN_FLAG)
+
+// A flag to tell vfp instruction type.
+// OP_SCALAR - This operation always operates in scalar mode
+// OP_SD - The instruction exceptionally writes to a single precision result.
+// OP_DD - The instruction exceptionally writes to a double precision result.
+// OP_SM - The instruction exceptionally reads from a single precision operand.
+enum : u32 {
+ OP_SCALAR = (1 << 0),
+ OP_SD = (1 << 1),
+ OP_DD = (1 << 1),
+ OP_SM = (1 << 2)
+};
+
+struct op {
+ u32 (* const fn)(ARMul_State* state, int dd, int dn, int dm, u32 fpscr);
+ u32 flags;
+};
+
+static inline u32 fls(ARMword x)
+{
+ int r = 32;
+
+ if (!x)
+ return 0;
+ if (!(x & 0xffff0000u)) {
+ x <<= 16;
+ r -= 16;
+ }
+ if (!(x & 0xff000000u)) {
+ x <<= 8;
+ r -= 8;
+ }
+ if (!(x & 0xf0000000u)) {
+ x <<= 4;
+ r -= 4;
+ }
+ if (!(x & 0xc0000000u)) {
+ x <<= 2;
+ r -= 2;
+ }
+ if (!(x & 0x80000000u)) {
+ x <<= 1;
+ r -= 1;
+ }
+ return r;
+
+}
+
+u32 vfp_double_multiply(vfp_double* vdd, vfp_double* vdn, vfp_double* vdm, u32 fpscr);
+u32 vfp_double_add(vfp_double* vdd, vfp_double* vdn, vfp_double *vdm, u32 fpscr);
+u32 vfp_double_normaliseround(ARMul_State* state, int dd, vfp_double* vd, u32 fpscr, u32 exceptions, const char* func);
--- /dev/null
+/*
+ vfp/vfpdouble.c - ARM VFPv3 emulation unit - SoftFloat double instruction
+ Copyright (C) 2003 Skyeye Develop Group
+ for help please send mail to <skyeye-developer@lists.gro.clinux.org>
+
+ 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 of the License, 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
+*/
+
+/*
+ * This code is derived in part from :
+ * - Android kernel
+ * - John R. Housers softfloat library, which
+ * carries the following notice:
+ *
+ * ===========================================================================
+ * This C source file is part of the SoftFloat IEC/IEEE Floating-point
+ * Arithmetic Package, Release 2.
+ *
+ * Written by John R. Hauser. This work was made possible in part by the
+ * International Computer Science Institute, located at Suite 600, 1947 Center
+ * Street, Berkeley, California 94704. Funding was partially provided by the
+ * National Science Foundation under grant MIP-9311980. The original version
+ * of this code was written as part of a project to build a fixed-point vector
+ * processor in collaboration with the University of California at Berkeley,
+ * overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+ * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+ * arithmetic/softfloat.html'.
+ *
+ * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
+ * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+ * TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
+ * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+ * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+ *
+ * Derivative works are acceptable, even for commercial purposes, so long as
+ * (1) they include prominent notice that the work is derivative, and (2) they
+ * include prominent notice akin to these three paragraphs for those parts of
+ * this code that are retained.
+ * ===========================================================================
+ */
+
+#include "Kernel.h"
+
+#include "arm/skyeye_common/vfp/vfp.h"
+#include "arm/skyeye_common/vfp/vfp_helper.h"
+#include "arm/skyeye_common/vfp/asm_vfp.h"
+
+#define BITS(s, a, b) ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1))
+#define BIT(s, n) ((s >> (n)) & 1)
+
+static struct vfp_double vfp_double_default_qnan = {
+ 2047,
+ 0,
+ VFP_DOUBLE_SIGNIFICAND_QNAN,
+};
+
+static void vfp_double_dump(const char *str, struct vfp_double *d)
+{
+ LOG("VFP: %s: sign=%d exponent=%d significand=%016llx\n",
+ str, d->sign != 0, d->exponent, d->significand);
+}
+
+static void vfp_double_normalise_denormal(struct vfp_double *vd)
+{
+ int bits = 31 - fls((ARMword)(vd->significand >> 32));
+ if (bits == 31)
+ bits = 63 - fls((ARMword)vd->significand);
+
+ vfp_double_dump("normalise_denormal: in", vd);
+
+ if (bits) {
+ vd->exponent -= bits - 1;
+ vd->significand <<= bits;
+ }
+
+ vfp_double_dump("normalise_denormal: out", vd);
+}
+
+u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func)
+{
+ u64 significand, incr;
+ int exponent, shift, underflow;
+ u32 rmode;
+
+ vfp_double_dump("pack: in", vd);
+
+ /*
+ * Infinities and NaNs are a special case.
+ */
+ if (vd->exponent == 2047 && (vd->significand == 0 || exceptions))
+ goto pack;
+
+ /*
+ * Special-case zero.
+ */
+ if (vd->significand == 0) {
+ vd->exponent = 0;
+ goto pack;
+ }
+
+ exponent = vd->exponent;
+ significand = vd->significand;
+
+ shift = 32 - fls((ARMword)(significand >> 32));
+ if (shift == 32)
+ shift = 64 - fls((ARMword)significand);
+ if (shift) {
+ exponent -= shift;
+ significand <<= shift;
+ }
+
+#if 1
+ vd->exponent = exponent;
+ vd->significand = significand;
+ vfp_double_dump("pack: normalised", vd);
+#endif
+
+ /*
+ * Tiny number?
+ */
+ underflow = exponent < 0;
+ if (underflow) {
+ significand = vfp_shiftright64jamming(significand, -exponent);
+ exponent = 0;
+#if 1
+ vd->exponent = exponent;
+ vd->significand = significand;
+ vfp_double_dump("pack: tiny number", vd);
+#endif
+ if (!(significand & ((1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1)))
+ underflow = 0;
+ }
+
+ /*
+ * Select rounding increment.
+ */
+ incr = 0;
+ rmode = fpscr & FPSCR_RMODE_MASK;
+
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ incr = 1ULL << VFP_DOUBLE_LOW_BITS;
+ if ((significand & (1ULL << (VFP_DOUBLE_LOW_BITS + 1))) == 0)
+ incr -= 1;
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
+ incr = 0;
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0))
+ incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1;
+
+ LOG("VFP: rounding increment = 0x%08llx\n", incr);
+
+ /*
+ * Is our rounding going to overflow?
+ */
+ if ((significand + incr) < significand) {
+ exponent += 1;
+ significand = (significand >> 1) | (significand & 1);
+ incr >>= 1;
+#if 1
+ vd->exponent = exponent;
+ vd->significand = significand;
+ vfp_double_dump("pack: overflow", vd);
+#endif
+ }
+
+ /*
+ * If any of the low bits (which will be shifted out of the
+ * number) are non-zero, the result is inexact.
+ */
+ if (significand & ((1 << (VFP_DOUBLE_LOW_BITS + 1)) - 1))
+ exceptions |= FPSCR_IXC;
+
+ /*
+ * Do our rounding.
+ */
+ significand += incr;
+
+ /*
+ * Infinity?
+ */
+ if (exponent >= 2046) {
+ exceptions |= FPSCR_OFC | FPSCR_IXC;
+ if (incr == 0) {
+ vd->exponent = 2045;
+ vd->significand = 0x7fffffffffffffffULL;
+ } else {
+ vd->exponent = 2047; /* infinity */
+ vd->significand = 0;
+ }
+ } else {
+ if (significand >> (VFP_DOUBLE_LOW_BITS + 1) == 0)
+ exponent = 0;
+ if (exponent || significand > 0x8000000000000000ULL)
+ underflow = 0;
+ if (underflow)
+ exceptions |= FPSCR_UFC;
+ vd->exponent = exponent;
+ vd->significand = significand >> 1;
+ }
+
+pack:
+ vfp_double_dump("pack: final", vd);
+ {
+ s64 d = vfp_double_pack(vd);
+ LOG("VFP: %s: d(d%d)=%016llx exceptions=%08x\n", func,
+ dd, d, exceptions);
+ vfp_put_double(state, d, dd);
+ }
+ return exceptions;
+}
+
+/*
+ * Propagate the NaN, setting exceptions if it is signalling.
+ * 'n' is always a NaN. 'm' may be a number, NaN or infinity.
+ */
+static u32
+vfp_propagate_nan(struct vfp_double *vdd, struct vfp_double *vdn,
+ struct vfp_double *vdm, u32 fpscr)
+{
+ struct vfp_double *nan;
+ int tn, tm = 0;
+
+ tn = vfp_double_type(vdn);
+
+ if (vdm)
+ tm = vfp_double_type(vdm);
+
+ if (fpscr & FPSCR_DEFAULT_NAN)
+ /*
+ * Default NaN mode - always returns a quiet NaN
+ */
+ nan = &vfp_double_default_qnan;
+ else {
+ /*
+ * Contemporary mode - select the first signalling
+ * NAN, or if neither are signalling, the first
+ * quiet NAN.
+ */
+ if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN))
+ nan = vdn;
+ else
+ nan = vdm;
+ /*
+ * Make the NaN quiet.
+ */
+ nan->significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
+ }
+
+ *vdd = *nan;
+
+ /*
+ * If one was a signalling NAN, raise invalid operation.
+ */
+ return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : VFP_NAN_FLAG;
+}
+
+/*
+ * Extended operations
+ */
+static u32 vfp_double_fabs(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ vfp_put_double(state, vfp_double_packed_abs(vfp_get_double(state, dm)), dd);
+ return 0;
+}
+
+static u32 vfp_double_fcpy(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ vfp_put_double(state, vfp_get_double(state, dm), dd);
+ return 0;
+}
+
+static u32 vfp_double_fneg(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ vfp_put_double(state, vfp_double_packed_negate(vfp_get_double(state, dm)), dd);
+ return 0;
+}
+
+static u32 vfp_double_fsqrt(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ vfp_double vdm, vdd, *vdp;
+ int ret, tm;
+
+ vfp_double_unpack(&vdm, vfp_get_double(state, dm));
+ tm = vfp_double_type(&vdm);
+ if (tm & (VFP_NAN|VFP_INFINITY)) {
+ vdp = &vdd;
+
+ if (tm & VFP_NAN)
+ ret = vfp_propagate_nan(vdp, &vdm, NULL, fpscr);
+ else if (vdm.sign == 0) {
+sqrt_copy:
+ vdp = &vdm;
+ ret = 0;
+ } else {
+sqrt_invalid:
+ vdp = &vfp_double_default_qnan;
+ ret = FPSCR_IOC;
+ }
+ vfp_put_double(state, vfp_double_pack(vdp), dd);
+ return ret;
+ }
+
+ /*
+ * sqrt(+/- 0) == +/- 0
+ */
+ if (tm & VFP_ZERO)
+ goto sqrt_copy;
+
+ /*
+ * Normalise a denormalised number
+ */
+ if (tm & VFP_DENORMAL)
+ vfp_double_normalise_denormal(&vdm);
+
+ /*
+ * sqrt(<0) = invalid
+ */
+ if (vdm.sign)
+ goto sqrt_invalid;
+
+ vfp_double_dump("sqrt", &vdm);
+
+ /*
+ * Estimate the square root.
+ */
+ vdd.sign = 0;
+ vdd.exponent = ((vdm.exponent - 1023) >> 1) + 1023;
+ vdd.significand = (u64)vfp_estimate_sqrt_significand(vdm.exponent, vdm.significand >> 32) << 31;
+
+ vfp_double_dump("sqrt estimate1", &vdd);
+
+ vdm.significand >>= 1 + (vdm.exponent & 1);
+ vdd.significand += 2 + vfp_estimate_div128to64(vdm.significand, 0, vdd.significand);
+
+ vfp_double_dump("sqrt estimate2", &vdd);
+
+ /*
+ * And now adjust.
+ */
+ if ((vdd.significand & VFP_DOUBLE_LOW_BITS_MASK) <= 5) {
+ if (vdd.significand < 2) {
+ vdd.significand = ~0ULL;
+ } else {
+ u64 termh, terml, remh, reml;
+ vdm.significand <<= 2;
+ mul64to128(&termh, &terml, vdd.significand, vdd.significand);
+ sub128(&remh, &reml, vdm.significand, 0, termh, terml);
+ while ((s64)remh < 0) {
+ vdd.significand -= 1;
+ shift64left(&termh, &terml, vdd.significand);
+ terml |= 1;
+ add128(&remh, &reml, remh, reml, termh, terml);
+ }
+ vdd.significand |= (remh | reml) != 0;
+ }
+ }
+ vdd.significand = vfp_shiftright64jamming(vdd.significand, 1);
+
+ return vfp_double_normaliseround(state, dd, &vdd, fpscr, 0, "fsqrt");
+}
+
+/*
+ * Equal := ZC
+ * Less than := N
+ * Greater than := C
+ * Unordered := CV
+ */
+static u32 vfp_compare(ARMul_State* state, int dd, int signal_on_qnan, int dm, u32 fpscr)
+{
+ s64 d, m;
+ u32 ret = 0;
+
+ LOG("In %s, state=0x%p, fpscr=0x%x\n", __FUNCTION__, state, fpscr);
+ m = vfp_get_double(state, dm);
+ if (vfp_double_packed_exponent(m) == 2047 && vfp_double_packed_mantissa(m)) {
+ ret |= FPSCR_CFLAG | FPSCR_VFLAG;
+ if (signal_on_qnan || !(vfp_double_packed_mantissa(m) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1))))
+ /*
+ * Signalling NaN, or signalling on quiet NaN
+ */
+ ret |= FPSCR_IOC;
+ }
+
+ d = vfp_get_double(state, dd);
+ if (vfp_double_packed_exponent(d) == 2047 && vfp_double_packed_mantissa(d)) {
+ ret |= FPSCR_CFLAG | FPSCR_VFLAG;
+ if (signal_on_qnan || !(vfp_double_packed_mantissa(d) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1))))
+ /*
+ * Signalling NaN, or signalling on quiet NaN
+ */
+ ret |= FPSCR_IOC;
+ }
+
+ if (ret == 0) {
+ //printf("In %s, d=%lld, m =%lld\n ", __FUNCTION__, d, m);
+ if (d == m || vfp_double_packed_abs(d | m) == 0) {
+ /*
+ * equal
+ */
+ ret |= FPSCR_ZFLAG | FPSCR_CFLAG;
+ //printf("In %s,1 ret=0x%x\n", __FUNCTION__, ret);
+ } else if (vfp_double_packed_sign(d ^ m)) {
+ /*
+ * different signs
+ */
+ if (vfp_double_packed_sign(d))
+ /*
+ * d is negative, so d < m
+ */
+ ret |= FPSCR_NFLAG;
+ else
+ /*
+ * d is positive, so d > m
+ */
+ ret |= FPSCR_CFLAG;
+ } else if ((vfp_double_packed_sign(d) != 0) ^ (d < m)) {
+ /*
+ * d < m
+ */
+ ret |= FPSCR_NFLAG;
+ } else if ((vfp_double_packed_sign(d) != 0) ^ (d > m)) {
+ /*
+ * d > m
+ */
+ ret |= FPSCR_CFLAG;
+ }
+ }
+ LOG("In %s, state=0x%p, ret=0x%x\n", __FUNCTION__, state, ret);
+
+ return ret;
+}
+
+static u32 vfp_double_fcmp(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ return vfp_compare(state, dd, 0, dm, fpscr);
+}
+
+static u32 vfp_double_fcmpe(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ return vfp_compare(state, dd, 1, dm, fpscr);
+}
+
+static u32 vfp_double_fcmpz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ return vfp_compare(state, dd, 0, VFP_REG_ZERO, fpscr);
+}
+
+static u32 vfp_double_fcmpez(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ return vfp_compare(state, dd, 1, VFP_REG_ZERO, fpscr);
+}
+
+static u32 vfp_double_fcvts(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
+{
+ struct vfp_double vdm;
+ struct vfp_single vsd;
+ int tm;
+ u32 exceptions = 0;
+
+ LOG("In %s\n", __FUNCTION__);
+ vfp_double_unpack(&vdm, vfp_get_double(state, dm));
+
+ tm = vfp_double_type(&vdm);
+
+ /*
+ * If we have a signalling NaN, signal invalid operation.
+ */
+ if (tm == VFP_SNAN)
+ exceptions = FPSCR_IOC;
+
+ if (tm & VFP_DENORMAL)
+ vfp_double_normalise_denormal(&vdm);
+
+ vsd.sign = vdm.sign;
+ vsd.significand = vfp_hi64to32jamming(vdm.significand);
+
+ /*
+ * If we have an infinity or a NaN, the exponent must be 255
+ */
+ if (tm & (VFP_INFINITY|VFP_NAN)) {
+ vsd.exponent = 255;
+ if (tm == VFP_QNAN)
+ vsd.significand |= VFP_SINGLE_SIGNIFICAND_QNAN;
+ goto pack_nan;
+ } else if (tm & VFP_ZERO)
+ vsd.exponent = 0;
+ else
+ vsd.exponent = vdm.exponent - (1023 - 127);
+
+ return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fcvts");
+
+pack_nan:
+ vfp_put_float(state, vfp_single_pack(&vsd), sd);
+ return exceptions;
+}
+
+static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
+{
+ struct vfp_double vdm;
+ u32 m = vfp_get_float(state, dm);
+
+ LOG("In %s\n", __FUNCTION__);
+ vdm.sign = 0;
+ vdm.exponent = 1023 + 63 - 1;
+ vdm.significand = (u64)m;
+
+ return vfp_double_normaliseround(state, dd, &vdm, fpscr, 0, "fuito");
+}
+
+static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
+{
+ struct vfp_double vdm;
+ u32 m = vfp_get_float(state, dm);
+
+ LOG("In %s\n", __FUNCTION__);
+ vdm.sign = (m & 0x80000000) >> 16;
+ vdm.exponent = 1023 + 63 - 1;
+ vdm.significand = vdm.sign ? -m : m;
+
+ return vfp_double_normaliseround(state, dd, &vdm, fpscr, 0, "fsito");
+}
+
+static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
+{
+ struct vfp_double vdm;
+ u32 d, exceptions = 0;
+ int rmode = fpscr & FPSCR_RMODE_MASK;
+ int tm;
+
+ LOG("In %s\n", __FUNCTION__);
+ vfp_double_unpack(&vdm, vfp_get_double(state, dm));
+
+ /*
+ * Do we have a denormalised number?
+ */
+ tm = vfp_double_type(&vdm);
+ if (tm & VFP_DENORMAL)
+ exceptions |= FPSCR_IDC;
+
+ if (tm & VFP_NAN)
+ vdm.sign = 1;
+
+ if (vdm.exponent >= 1023 + 32) {
+ d = vdm.sign ? 0 : 0xffffffff;
+ exceptions = FPSCR_IOC;
+ } else if (vdm.exponent >= 1023 - 1) {
+ int shift = 1023 + 63 - vdm.exponent;
+ u64 rem, incr = 0;
+
+ /*
+ * 2^0 <= m < 2^32-2^8
+ */
+ d = (ARMword)((vdm.significand << 1) >> shift);
+ rem = vdm.significand << (65 - shift);
+
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ incr = 0x8000000000000000ULL;
+ if ((d & 1) == 0)
+ incr -= 1;
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
+ incr = 0;
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) {
+ incr = ~0ULL;
+ }
+
+ if ((rem + incr) < rem) {
+ if (d < 0xffffffff)
+ d += 1;
+ else
+ exceptions |= FPSCR_IOC;
+ }
+
+ if (d && vdm.sign) {
+ d = 0;
+ exceptions |= FPSCR_IOC;
+ } else if (rem)
+ exceptions |= FPSCR_IXC;
+ } else {
+ d = 0;
+ if (vdm.exponent | vdm.significand) {
+ exceptions |= FPSCR_IXC;
+ if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
+ d = 1;
+ else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) {
+ d = 0;
+ exceptions |= FPSCR_IOC;
+ }
+ }
+ }
+
+ LOG("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+
+ vfp_put_float(state, d, sd);
+
+ return exceptions;
+}
+
+static u32 vfp_double_ftouiz(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ return vfp_double_ftoui(state, sd, unused, dm, FPSCR_ROUND_TOZERO);
+}
+
+static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
+{
+ struct vfp_double vdm;
+ u32 d, exceptions = 0;
+ int rmode = fpscr & FPSCR_RMODE_MASK;
+ int tm;
+
+ LOG("In %s\n", __FUNCTION__);
+ vfp_double_unpack(&vdm, vfp_get_double(state, dm));
+ vfp_double_dump("VDM", &vdm);
+
+ /*
+ * Do we have denormalised number?
+ */
+ tm = vfp_double_type(&vdm);
+ if (tm & VFP_DENORMAL)
+ exceptions |= FPSCR_IDC;
+
+ if (tm & VFP_NAN) {
+ d = 0;
+ exceptions |= FPSCR_IOC;
+ } else if (vdm.exponent >= 1023 + 32) {
+ d = 0x7fffffff;
+ if (vdm.sign)
+ d = ~d;
+ exceptions |= FPSCR_IOC;
+ } else if (vdm.exponent >= 1023 - 1) {
+ int shift = 1023 + 63 - vdm.exponent; /* 58 */
+ u64 rem, incr = 0;
+
+ d = (ARMword)((vdm.significand << 1) >> shift);
+ rem = vdm.significand << (65 - shift);
+
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ incr = 0x8000000000000000ULL;
+ if ((d & 1) == 0)
+ incr -= 1;
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
+ incr = 0;
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) {
+ incr = ~0ULL;
+ }
+
+ if ((rem + incr) < rem && d < 0xffffffff)
+ d += 1;
+ if (d > (0x7fffffffU + (vdm.sign != 0))) {
+ d = (0x7fffffffU + (vdm.sign != 0));
+ exceptions |= FPSCR_IOC;
+ } else if (rem)
+ exceptions |= FPSCR_IXC;
+
+ if (vdm.sign)
+ d = -d;
+ } else {
+ d = 0;
+ if (vdm.exponent | vdm.significand) {
+ exceptions |= FPSCR_IXC;
+ if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
+ d = 1;
+ else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign)
+ d = -1;
+ }
+ }
+
+ LOG("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+
+ vfp_put_float(state, (s32)d, sd);
+
+ return exceptions;
+}
+
+static u32 vfp_double_ftosiz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ return vfp_double_ftosi(state, dd, unused, dm, FPSCR_ROUND_TOZERO);
+}
+
+static struct op fops_ext[] = {
+ { vfp_double_fcpy, 0 }, //0x00000000 - FEXT_FCPY
+ { vfp_double_fabs, 0 }, //0x00000001 - FEXT_FABS
+ { vfp_double_fneg, 0 }, //0x00000002 - FEXT_FNEG
+ { vfp_double_fsqrt, 0 }, //0x00000003 - FEXT_FSQRT
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { vfp_double_fcmp, OP_SCALAR }, //0x00000008 - FEXT_FCMP
+ { vfp_double_fcmpe, OP_SCALAR }, //0x00000009 - FEXT_FCMPE
+ { vfp_double_fcmpz, OP_SCALAR }, //0x0000000A - FEXT_FCMPZ
+ { vfp_double_fcmpez, OP_SCALAR }, //0x0000000B - FEXT_FCMPEZ
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { vfp_double_fcvts, OP_SCALAR|OP_DD }, //0x0000000F - FEXT_FCVT
+ { vfp_double_fuito, OP_SCALAR|OP_SM }, //0x00000010 - FEXT_FUITO
+ { vfp_double_fsito, OP_SCALAR|OP_SM }, //0x00000011 - FEXT_FSITO
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { vfp_double_ftoui, OP_SCALAR|OP_SD }, //0x00000018 - FEXT_FTOUI
+ { vfp_double_ftouiz, OP_SCALAR|OP_SD }, //0x00000019 - FEXT_FTOUIZ
+ { vfp_double_ftosi, OP_SCALAR|OP_SD }, //0x0000001A - FEXT_FTOSI
+ { vfp_double_ftosiz, OP_SCALAR|OP_SD }, //0x0000001B - FEXT_FTOSIZ
+};
+
+static u32
+vfp_double_fadd_nonnumber(struct vfp_double *vdd, struct vfp_double *vdn,
+ struct vfp_double *vdm, u32 fpscr)
+{
+ struct vfp_double *vdp;
+ u32 exceptions = 0;
+ int tn, tm;
+
+ tn = vfp_double_type(vdn);
+ tm = vfp_double_type(vdm);
+
+ if (tn & tm & VFP_INFINITY) {
+ /*
+ * Two infinities. Are they different signs?
+ */
+ if (vdn->sign ^ vdm->sign) {
+ /*
+ * different signs -> invalid
+ */
+ exceptions = FPSCR_IOC;
+ vdp = &vfp_double_default_qnan;
+ } else {
+ /*
+ * same signs -> valid
+ */
+ vdp = vdn;
+ }
+ } else if (tn & VFP_INFINITY && tm & VFP_NUMBER) {
+ /*
+ * One infinity and one number -> infinity
+ */
+ vdp = vdn;
+ } else {
+ /*
+ * 'n' is a NaN of some type
+ */
+ return vfp_propagate_nan(vdd, vdn, vdm, fpscr);
+ }
+ *vdd = *vdp;
+ return exceptions;
+}
+
+u32 vfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn,struct vfp_double *vdm, u32 fpscr)
+{
+ u32 exp_diff;
+ u64 m_sig;
+
+ if (vdn->significand & (1ULL << 63) ||
+ vdm->significand & (1ULL << 63)) {
+ LOG("VFP: bad FP values in %s\n", __func__);
+ vfp_double_dump("VDN", vdn);
+ vfp_double_dump("VDM", vdm);
+ }
+
+ /*
+ * Ensure that 'n' is the largest magnitude number. Note that
+ * if 'n' and 'm' have equal exponents, we do not swap them.
+ * This ensures that NaN propagation works correctly.
+ */
+ if (vdn->exponent < vdm->exponent) {
+ struct vfp_double *t = vdn;
+ vdn = vdm;
+ vdm = t;
+ }
+
+ /*
+ * Is 'n' an infinity or a NaN? Note that 'm' may be a number,
+ * infinity or a NaN here.
+ */
+ if (vdn->exponent == 2047)
+ return vfp_double_fadd_nonnumber(vdd, vdn, vdm, fpscr);
+
+ /*
+ * We have two proper numbers, where 'vdn' is the larger magnitude.
+ *
+ * Copy 'n' to 'd' before doing the arithmetic.
+ */
+ *vdd = *vdn;
+
+ /*
+ * Align 'm' with the result.
+ */
+ exp_diff = vdn->exponent - vdm->exponent;
+ m_sig = vfp_shiftright64jamming(vdm->significand, exp_diff);
+
+ /*
+ * If the signs are different, we are really subtracting.
+ */
+ if (vdn->sign ^ vdm->sign) {
+ m_sig = vdn->significand - m_sig;
+ if ((s64)m_sig < 0) {
+ vdd->sign = vfp_sign_negate(vdd->sign);
+ m_sig = -m_sig;
+ } else if (m_sig == 0) {
+ vdd->sign = (fpscr & FPSCR_RMODE_MASK) ==
+ FPSCR_ROUND_MINUSINF ? 0x8000 : 0;
+ }
+ } else {
+ m_sig += vdn->significand;
+ }
+ vdd->significand = m_sig;
+
+ return 0;
+}
+
+u32
+vfp_double_multiply(struct vfp_double *vdd, struct vfp_double *vdn,
+ struct vfp_double *vdm, u32 fpscr)
+{
+ vfp_double_dump("VDN", vdn);
+ vfp_double_dump("VDM", vdm);
+
+ /*
+ * Ensure that 'n' is the largest magnitude number. Note that
+ * if 'n' and 'm' have equal exponents, we do not swap them.
+ * This ensures that NaN propagation works correctly.
+ */
+ if (vdn->exponent < vdm->exponent) {
+ struct vfp_double *t = vdn;
+ vdn = vdm;
+ vdm = t;
+ LOG("VFP: swapping M <-> N\n");
+ }
+
+ vdd->sign = vdn->sign ^ vdm->sign;
+
+ /*
+ * If 'n' is an infinity or NaN, handle it. 'm' may be anything.
+ */
+ if (vdn->exponent == 2047) {
+ if (vdn->significand || (vdm->exponent == 2047 && vdm->significand))
+ return vfp_propagate_nan(vdd, vdn, vdm, fpscr);
+ if ((vdm->exponent | vdm->significand) == 0) {
+ *vdd = vfp_double_default_qnan;
+ return FPSCR_IOC;
+ }
+ vdd->exponent = vdn->exponent;
+ vdd->significand = 0;
+ return 0;
+ }
+
+ /*
+ * If 'm' is zero, the result is always zero. In this case,
+ * 'n' may be zero or a number, but it doesn't matter which.
+ */
+ if ((vdm->exponent | vdm->significand) == 0) {
+ vdd->exponent = 0;
+ vdd->significand = 0;
+ return 0;
+ }
+
+ /*
+ * We add 2 to the destination exponent for the same reason
+ * as the addition case - though this time we have +1 from
+ * each input operand.
+ */
+ vdd->exponent = vdn->exponent + vdm->exponent - 1023 + 2;
+ vdd->significand = vfp_hi64multiply64(vdn->significand, vdm->significand);
+
+ vfp_double_dump("VDD", vdd);
+ return 0;
+}
+
+#define NEG_MULTIPLY (1 << 0)
+#define NEG_SUBTRACT (1 << 1)
+
+static u32
+vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, int dm, u32 fpscr, u32 negate, const char *func)
+{
+ struct vfp_double vdd, vdp, vdn, vdm;
+ u32 exceptions;
+
+ vfp_double_unpack(&vdn, vfp_get_double(state, dn));
+ if (vdn.exponent == 0 && vdn.significand)
+ vfp_double_normalise_denormal(&vdn);
+
+ vfp_double_unpack(&vdm, vfp_get_double(state, dm));
+ if (vdm.exponent == 0 && vdm.significand)
+ vfp_double_normalise_denormal(&vdm);
+
+ exceptions = vfp_double_multiply(&vdp, &vdn, &vdm, fpscr);
+ if (negate & NEG_MULTIPLY)
+ vdp.sign = vfp_sign_negate(vdp.sign);
+
+ vfp_double_unpack(&vdn, vfp_get_double(state, dd));
+ if (vdn.exponent == 0 && vdn.significand != 0)
+ vfp_double_normalise_denormal(&vdn);
+
+ if (negate & NEG_SUBTRACT)
+ vdn.sign = vfp_sign_negate(vdn.sign);
+
+ exceptions |= vfp_double_add(&vdd, &vdn, &vdp, fpscr);
+
+ return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, func);
+}
+
+/*
+ * Standard operations
+ */
+
+/*
+ * sd = sd + (sn * sm)
+ */
+static u32 vfp_double_fmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, 0, "fmac");
+}
+
+/*
+ * sd = sd - (sn * sm)
+ */
+static u32 vfp_double_fnmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_MULTIPLY, "fnmac");
+}
+
+/*
+ * sd = -sd + (sn * sm)
+ */
+static u32 vfp_double_fmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_SUBTRACT, "fmsc");
+}
+
+/*
+ * sd = -sd - (sn * sm)
+ */
+static u32 vfp_double_fnmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
+{
+ LOG("In %s\n", __FUNCTION__);
+ return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
+}
+
+/*
+ * sd = sn * sm
+ */
+static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
+{
+ struct vfp_double vdd, vdn, vdm;
+ u32 exceptions;
+
+ LOG("In %s\n", __FUNCTION__);
+ vfp_double_unpack(&vdn, vfp_get_double(state, dn));
+ if (vdn.exponent == 0 && vdn.significand)
+ vfp_double_normalise_denormal(&vdn);
+
+ vfp_double_unpack(&vdm, vfp_get_double(state, dm));
+ if (vdm.exponent == 0 && vdm.significand)
+ vfp_double_normalise_denormal(&vdm);
+
+ exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
+ return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fmul");
+}
+
+/*
+ * sd = -(sn * sm)
+ */
+static u32 vfp_double_fnmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
+{
+ struct vfp_double vdd, vdn, vdm;
+ u32 exceptions;
+
+ LOG("In %s\n", __FUNCTION__);
+ vfp_double_unpack(&vdn, vfp_get_double(state, dn));
+ if (vdn.exponent == 0 && vdn.significand)
+ vfp_double_normalise_denormal(&vdn);
+
+ vfp_double_unpack(&vdm, vfp_get_double(state, dm));
+ if (vdm.exponent == 0 && vdm.significand)
+ vfp_double_normalise_denormal(&vdm);
+
+ exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
+ vdd.sign = vfp_sign_negate(vdd.sign);
+
+ return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fnmul");
+}
+
+/*
+ * sd = sn + sm
+ */
+static u32 vfp_double_fadd(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
+{
+ struct vfp_double vdd, vdn, vdm;
+ u32 exceptions;
+
+ LOG("In %s\n", __FUNCTION__);
+ vfp_double_unpack(&vdn, vfp_get_double(state, dn));
+ if (vdn.exponent == 0 && vdn.significand)
+ vfp_double_normalise_denormal(&vdn);
+
+ vfp_double_unpack(&vdm, vfp_get_double(state, dm));
+ if (vdm.exponent == 0 && vdm.significand)
+ vfp_double_normalise_denormal(&vdm);
+
+ exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr);
+
+ return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fadd");
+}
+
+/*
+ * sd = sn - sm
+ */
+static u32 vfp_double_fsub(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
+{
+ struct vfp_double vdd, vdn, vdm;
+ u32 exceptions;
+
+ LOG("In %s\n", __FUNCTION__);
+ vfp_double_unpack(&vdn, vfp_get_double(state, dn));
+ if (vdn.exponent == 0 && vdn.significand)
+ vfp_double_normalise_denormal(&vdn);
+
+ vfp_double_unpack(&vdm, vfp_get_double(state, dm));
+ if (vdm.exponent == 0 && vdm.significand)
+ vfp_double_normalise_denormal(&vdm);
+
+ /*
+ * Subtraction is like addition, but with a negated operand.
+ */
+ vdm.sign = vfp_sign_negate(vdm.sign);
+
+ exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr);
+
+ return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fsub");
+}
+
+/*
+ * sd = sn / sm
+ */
+static u32 vfp_double_fdiv(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
+{
+ struct vfp_double vdd, vdn, vdm;
+ u32 exceptions = 0;
+ int tm, tn;
+
+ LOG("In %s\n", __FUNCTION__);
+ vfp_double_unpack(&vdn, vfp_get_double(state, dn));
+ vfp_double_unpack(&vdm, vfp_get_double(state, dm));
+
+ vdd.sign = vdn.sign ^ vdm.sign;
+
+ tn = vfp_double_type(&vdn);
+ tm = vfp_double_type(&vdm);
+
+ /*
+ * Is n a NAN?
+ */
+ if (tn & VFP_NAN)
+ goto vdn_nan;
+
+ /*
+ * Is m a NAN?
+ */
+ if (tm & VFP_NAN)
+ goto vdm_nan;
+
+ /*
+ * If n and m are infinity, the result is invalid
+ * If n and m are zero, the result is invalid
+ */
+ if (tm & tn & (VFP_INFINITY|VFP_ZERO))
+ goto invalid;
+
+ /*
+ * If n is infinity, the result is infinity
+ */
+ if (tn & VFP_INFINITY)
+ goto infinity;
+
+ /*
+ * If m is zero, raise div0 exceptions
+ */
+ if (tm & VFP_ZERO)
+ goto divzero;
+
+ /*
+ * If m is infinity, or n is zero, the result is zero
+ */
+ if (tm & VFP_INFINITY || tn & VFP_ZERO)
+ goto zero;
+
+ if (tn & VFP_DENORMAL)
+ vfp_double_normalise_denormal(&vdn);
+ if (tm & VFP_DENORMAL)
+ vfp_double_normalise_denormal(&vdm);
+
+ /*
+ * Ok, we have two numbers, we can perform division.
+ */
+ vdd.exponent = vdn.exponent - vdm.exponent + 1023 - 1;
+ vdm.significand <<= 1;
+ if (vdm.significand <= (2 * vdn.significand)) {
+ vdn.significand >>= 1;
+ vdd.exponent++;
+ }
+ vdd.significand = vfp_estimate_div128to64(vdn.significand, 0, vdm.significand);
+ if ((vdd.significand & 0x1ff) <= 2) {
+ u64 termh, terml, remh, reml;
+ mul64to128(&termh, &terml, vdm.significand, vdd.significand);
+ sub128(&remh, &reml, vdn.significand, 0, termh, terml);
+ while ((s64)remh < 0) {
+ vdd.significand -= 1;
+ add128(&remh, &reml, remh, reml, 0, vdm.significand);
+ }
+ vdd.significand |= (reml != 0);
+ }
+ return vfp_double_normaliseround(state, dd, &vdd, fpscr, 0, "fdiv");
+
+vdn_nan:
+ exceptions = vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr);
+pack:
+ vfp_put_double(state, vfp_double_pack(&vdd), dd);
+ return exceptions;
+
+vdm_nan:
+ exceptions = vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr);
+ goto pack;
+
+zero:
+ vdd.exponent = 0;
+ vdd.significand = 0;
+ goto pack;
+
+divzero:
+ exceptions = FPSCR_DZC;
+infinity:
+ vdd.exponent = 2047;
+ vdd.significand = 0;
+ goto pack;
+
+invalid:
+ vfp_put_double(state, vfp_double_pack(&vfp_double_default_qnan), dd);
+ return FPSCR_IOC;
+}
+
+static struct op fops[] = {
+ { vfp_double_fmac, 0 },
+ { vfp_double_fmsc, 0 },
+ { vfp_double_fmul, 0 },
+ { vfp_double_fadd, 0 },
+ { vfp_double_fnmac, 0 },
+ { vfp_double_fnmsc, 0 },
+ { vfp_double_fnmul, 0 },
+ { vfp_double_fsub, 0 },
+ { vfp_double_fdiv, 0 },
+};
+
+#define FREG_BANK(x) ((x) & 0x0c)
+#define FREG_IDX(x) ((x) & 3)
+
+u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
+{
+ u32 op = inst & FOP_MASK;
+ u32 exceptions = 0;
+ unsigned int dest;
+ unsigned int dn = vfp_get_dn(inst);
+ unsigned int dm;
+ unsigned int vecitr, veclen, vecstride;
+ struct op *fop;
+
+ LOG("In %s\n", __FUNCTION__);
+ vecstride = (1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK));
+
+ fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)];
+
+ /*
+ * fcvtds takes an sN register number as destination, not dN.
+ * It also always operates on scalars.
+ */
+ if (fop->flags & OP_SD)
+ dest = vfp_get_sd(inst);
+ else
+ dest = vfp_get_dd(inst);
+
+ /*
+ * f[us]ito takes a sN operand, not a dN operand.
+ */
+ if (fop->flags & OP_SM)
+ dm = vfp_get_sm(inst);
+ else
+ dm = vfp_get_dm(inst);
+
+ /*
+ * If destination bank is zero, vector length is always '1'.
+ * ARM DDI0100F C5.1.3, C5.3.2.
+ */
+ if ((fop->flags & OP_SCALAR) || (FREG_BANK(dest) == 0))
+ veclen = 0;
+ else
+ veclen = fpscr & FPSCR_LENGTH_MASK;
+
+ LOG("VFP: vecstride=%u veclen=%u\n", vecstride,
+ (veclen >> FPSCR_LENGTH_BIT) + 1);
+
+ if (!fop->fn) {
+ printf("VFP: could not find double op %d\n", FEXT_TO_IDX(inst));
+ goto invalid;
+ }
+
+ for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) {
+ u32 except;
+ char type;
+
+ type = (fop->flags & OP_SD) ? 's' : 'd';
+ if (op == FOP_EXT)
+ {
+ LOG("VFP: itr%d (%c%u) = op[%u] (d%u)\n",
+ vecitr >> FPSCR_LENGTH_BIT,
+ type, dest, dn, dm);
+ }
+ else
+ {
+ LOG("VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)\n",
+ vecitr >> FPSCR_LENGTH_BIT,
+ type, dest, dn, FOP_TO_IDX(op), dm);
+ }
+
+ except = fop->fn(state, dest, dn, dm, fpscr);
+ LOG("VFP: itr%d: exceptions=%08x\n",
+ vecitr >> FPSCR_LENGTH_BIT, except);
+
+ exceptions |= except;
+
+ /*
+ * CHECK: It appears to be undefined whether we stop when
+ * we encounter an exception. We continue.
+ */
+ dest = FREG_BANK(dest) + ((FREG_IDX(dest) + vecstride) & 3);
+ dn = FREG_BANK(dn) + ((FREG_IDX(dn) + vecstride) & 3);
+ if (FREG_BANK(dm) != 0)
+ dm = FREG_BANK(dm) + ((FREG_IDX(dm) + vecstride) & 3);
+ }
+ return exceptions;
+
+invalid:
+ return ~0;
+}
--- /dev/null
+// Copyright 2012 Michael Kang, 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+/* Notice: this file should not be compiled as is, and is meant to be
+ included in other files only. */
+
+/* ----------------------------------------------------------------------- */
+/* CDP instructions */
+/* cond 1110 opc1 CRn- CRd- copr op20 CRm- CDP */
+
+/* ----------------------------------------------------------------------- */
+/* VMLA */
+/* cond 1110 0D00 Vn-- Vd-- 101X N0M0 Vm-- */
+
+#define InBigEndianMode(x) 0
+
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vmla_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vmla_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vmla)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vmla_inst));
+ vmla_inst *inst_cream = (vmla_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VMLA_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vmla_inst *inst_cream = (vmla_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vmla_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VNMLS */
+/* cond 1110 0D00 Vn-- Vd-- 101X N1M0 Vm-- */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vmls_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vmls_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vmls)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vmls_inst));
+ vmls_inst *inst_cream = (vmls_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VMLS_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vmls_inst *inst_cream = (vmls_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vmls_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VNMLA */
+/* cond 1110 0D01 Vn-- Vd-- 101X N1M0 Vm-- */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vnmla_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vnmla_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vnmla)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vnmla_inst));
+ vnmla_inst *inst_cream = (vnmla_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VNMLA_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vnmla_inst *inst_cream = (vnmla_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vnmla_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VNMLS */
+/* cond 1110 0D01 Vn-- Vd-- 101X N0M0 Vm-- */
+
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vnmls_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vnmls_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vnmls)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vnmls_inst));
+ vnmls_inst *inst_cream = (vnmls_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VNMLS_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vnmls_inst *inst_cream = (vnmls_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vnmls_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VNMUL */
+/* cond 1110 0D10 Vn-- Vd-- 101X N0M0 Vm-- */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vnmul_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vnmul_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vnmul)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vnmul_inst));
+ vnmul_inst *inst_cream = (vnmul_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VNMUL_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vnmul_inst *inst_cream = (vnmul_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vnmul_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VMUL */
+/* cond 1110 0D10 Vn-- Vd-- 101X N0M0 Vm-- */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vmul_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vmul_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vmul)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vmul_inst));
+ vmul_inst *inst_cream = (vmul_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VMUL_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vmul_inst *inst_cream = (vmul_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vmul_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VADD */
+/* cond 1110 0D11 Vn-- Vd-- 101X N0M0 Vm-- */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vadd_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vadd_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vadd)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vadd_inst));
+ vadd_inst *inst_cream = (vadd_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VADD_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vadd_inst *inst_cream = (vadd_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vadd_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VSUB */
+/* cond 1110 0D11 Vn-- Vd-- 101X N1M0 Vm-- */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vsub_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vsub_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vsub)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vsub_inst));
+ vsub_inst *inst_cream = (vsub_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VSUB_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vsub_inst *inst_cream = (vsub_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vsub_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VDIV */
+/* cond 1110 1D00 Vn-- Vd-- 101X N0M0 Vm-- */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vdiv_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vdiv_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vdiv)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vdiv_inst));
+ vdiv_inst *inst_cream = (vdiv_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VDIV_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vdiv_inst *inst_cream = (vdiv_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vdiv_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VMOVI move immediate */
+/* cond 1110 1D11 im4H Vd-- 101X 0000 im4L */
+/* cond 1110 opc1 CRn- CRd- copr op20 CRm- CDP */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vmovi_inst {
+ unsigned int single;
+ unsigned int d;
+ unsigned int imm;
+} vmovi_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovi)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vmovi_inst));
+ vmovi_inst *inst_cream = (vmovi_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->single = BIT(inst, 8) == 0;
+ inst_cream->d = (inst_cream->single ? BITS(inst,12,15)<<1 | BIT(inst,22) : BITS(inst,12,15) | BIT(inst,22)<<4);
+ unsigned int imm8 = BITS(inst, 16, 19) << 4 | BITS(inst, 0, 3);
+ if (inst_cream->single)
+ inst_cream->imm = BIT(imm8, 7)<<31 | (BIT(imm8, 6)==0)<<30 | (BIT(imm8, 6) ? 0x1f : 0)<<25 | BITS(imm8, 0, 5)<<19;
+ else
+ inst_cream->imm = BIT(imm8, 7)<<31 | (BIT(imm8, 6)==0)<<30 | (BIT(imm8, 6) ? 0xff : 0)<<22 | BITS(imm8, 0, 5)<<16;
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VMOVI_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vmovi_inst *inst_cream = (vmovi_inst *)inst_base->component;
+
+ VMOVI(cpu, inst_cream->single, inst_cream->d, inst_cream->imm);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vmovi_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VMOVR move register */
+/* cond 1110 1D11 0000 Vd-- 101X 01M0 Vm-- */
+/* cond 1110 opc1 CRn- CRd- copr op20 CRm- CDP */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vmovr_inst {
+ unsigned int single;
+ unsigned int d;
+ unsigned int m;
+} vmovr_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovr)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vmovr_inst));
+ vmovr_inst *inst_cream = (vmovr_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->single = BIT(inst, 8) == 0;
+ inst_cream->d = (inst_cream->single ? BITS(inst,12,15)<<1 | BIT(inst,22) : BITS(inst,12,15) | BIT(inst,22)<<4);
+ inst_cream->m = (inst_cream->single ? BITS(inst, 0, 3)<<1 | BIT(inst, 5) : BITS(inst, 0, 3) | BIT(inst, 5)<<4);
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VMOVR_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vmovr_inst *inst_cream = (vmovr_inst *)inst_base->component;
+
+ VMOVR(cpu, inst_cream->single, inst_cream->d, inst_cream->m);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vmovr_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VABS */
+/* cond 1110 1D11 0000 Vd-- 101X 11M0 Vm-- */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vabs_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vabs_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vabs)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vabs_inst));
+ vabs_inst *inst_cream = (vabs_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VABS_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vabs_inst *inst_cream = (vabs_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vabs_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VNEG */
+/* cond 1110 1D11 0001 Vd-- 101X 11M0 Vm-- */
+
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vneg_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vneg_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vneg)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vneg_inst));
+ vneg_inst *inst_cream = (vneg_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VNEG_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vneg_inst *inst_cream = (vneg_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vneg_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VSQRT */
+/* cond 1110 1D11 0001 Vd-- 101X 11M0 Vm-- */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vsqrt_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vsqrt_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vsqrt)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vsqrt_inst));
+ vsqrt_inst *inst_cream = (vsqrt_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VSQRT_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vsqrt_inst *inst_cream = (vsqrt_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vsqrt_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VCMP VCMPE */
+/* cond 1110 1D11 0100 Vd-- 101X E1M0 Vm-- Encoding 1 */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vcmp_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vcmp_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vcmp)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vcmp_inst));
+ vcmp_inst *inst_cream = (vcmp_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VCMP_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vcmp_inst *inst_cream = (vcmp_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vcmp_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VCMP VCMPE */
+/* cond 1110 1D11 0100 Vd-- 101X E1M0 Vm-- Encoding 2 */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vcmp2_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vcmp2_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vcmp2)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vcmp2_inst));
+ vcmp2_inst *inst_cream = (vcmp2_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VCMP2_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vcmp2_inst *inst_cream = (vcmp2_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vcmp2_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VCVTBDS between double and single */
+/* cond 1110 1D11 0111 Vd-- 101X 11M0 Vm-- */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vcvtbds_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vcvtbds_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vcvtbds)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vcvtbds_inst));
+ vcvtbds_inst *inst_cream = (vcvtbds_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VCVTBDS_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vcvtbds_inst *inst_cream = (vcvtbds_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vcvtbds_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VCVTBFF between floating point and fixed point */
+/* cond 1110 1D11 1op2 Vd-- 101X X1M0 Vm-- */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vcvtbff_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vcvtbff_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vcvtbff)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ VFP_DEBUG_UNTESTED(VCVTBFF);
+
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vcvtbff_inst));
+ vcvtbff_inst *inst_cream = (vcvtbff_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VCVTBFF_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vcvtbff_inst *inst_cream = (vcvtbff_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vcvtbff_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VCVTBFI between floating point and integer */
+/* cond 1110 1D11 1op2 Vd-- 101X X1M0 Vm-- */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vcvtbfi_inst {
+ unsigned int instr;
+ unsigned int dp_operation;
+} vcvtbfi_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vcvtbfi)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vcvtbfi_inst));
+ vcvtbfi_inst *inst_cream = (vcvtbfi_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->dp_operation = BIT(inst, 8);
+ inst_cream->instr = inst;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VCVTBFI_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vcvtbfi_inst *inst_cream = (vcvtbfi_inst *)inst_base->component;
+
+ int ret;
+
+ if (inst_cream->dp_operation)
+ ret = vfp_double_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+ else
+ ret = vfp_single_cpdo(cpu, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]);
+
+ CHECK_VFP_CDP_RET;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vcvtbfi_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* MRC / MCR instructions */
+/* cond 1110 AAAL XXXX XXXX 101C XBB1 XXXX */
+/* cond 1110 op11 CRn- Rt-- copr op21 CRm- */
+
+/* ----------------------------------------------------------------------- */
+/* VMOVBRS between register and single precision */
+/* cond 1110 000o Vn-- Rt-- 1010 N001 0000 */
+/* cond 1110 op11 CRn- Rt-- copr op21 CRm- MRC */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vmovbrs_inst {
+ unsigned int to_arm;
+ unsigned int t;
+ unsigned int n;
+} vmovbrs_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrs)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vmovbrs_inst));
+ vmovbrs_inst *inst_cream = (vmovbrs_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->to_arm = BIT(inst, 20) == 1;
+ inst_cream->t = BITS(inst, 12, 15);
+ inst_cream->n = BIT(inst, 7) | BITS(inst, 16, 19)<<1;
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VMOVBRS_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vmovbrs_inst *inst_cream = (vmovbrs_inst *)inst_base->component;
+
+ VMOVBRS(cpu, inst_cream->to_arm, inst_cream->t, inst_cream->n, &(cpu->Reg[inst_cream->t]));
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vmovbrs_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VMSR */
+/* cond 1110 1110 reg- Rt-- 1010 0001 0000 */
+/* cond 1110 op10 CRn- Rt-- copr op21 CRm- MCR */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vmsr_inst {
+ unsigned int reg;
+ unsigned int Rd;
+} vmsr_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vmsr)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vmsr_inst));
+ vmsr_inst *inst_cream = (vmsr_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->reg = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VMSR_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ /* FIXME: special case for access to FPSID and FPEXC, VFP must be disabled ,
+ and in privilegied mode */
+ /* Exceptions must be checked, according to v7 ref manual */
+ CHECK_VFP_ENABLED;
+
+ vmsr_inst *inst_cream = (vmsr_inst *)inst_base->component;
+
+ VMSR(cpu, inst_cream->reg, inst_cream->Rd);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vmsr_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VMOVBRC register to scalar */
+/* cond 1110 0XX0 Vd-- Rt-- 1011 DXX1 0000 */
+/* cond 1110 op10 CRn- Rt-- copr op21 CRm- MCR */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vmovbrc_inst {
+ unsigned int esize;
+ unsigned int index;
+ unsigned int d;
+ unsigned int t;
+} vmovbrc_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrc)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vmovbrc_inst));
+ vmovbrc_inst *inst_cream = (vmovbrc_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->d = BITS(inst, 16, 19)|BIT(inst, 7)<<4;
+ inst_cream->t = BITS(inst, 12, 15);
+ /* VFP variant of instruction */
+ inst_cream->esize = 32;
+ inst_cream->index = BIT(inst, 21);
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VMOVBRC_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vmovbrc_inst *inst_cream = (vmovbrc_inst *)inst_base->component;
+
+ VFP_DEBUG_UNIMPLEMENTED(VMOVBRC);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vmovbrc_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VMRS */
+/* cond 1110 1111 CRn- Rt-- 1010 0001 0000 */
+/* cond 1110 op11 CRn- Rt-- copr op21 CRm- MRC */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vmrs_inst {
+ unsigned int reg;
+ unsigned int Rt;
+} vmrs_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vmrs)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vmrs_inst));
+ vmrs_inst *inst_cream = (vmrs_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->reg = BITS(inst, 16, 19);
+ inst_cream->Rt = BITS(inst, 12, 15);
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VMRS_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ /* FIXME: special case for access to FPSID and FPEXC, VFP must be disabled,
+ and in privilegied mode */
+ /* Exceptions must be checked, according to v7 ref manual */
+ CHECK_VFP_ENABLED;
+
+ vmrs_inst *inst_cream = (vmrs_inst *)inst_base->component;
+
+ if (inst_cream->reg == 1) /* FPSCR */
+ {
+ if (inst_cream->Rt != 15)
+ {
+ cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_OFFSET(VFP_FPSCR)];
+ }
+ else
+ {
+ cpu->NFlag = (cpu->VFP[VFP_OFFSET(VFP_FPSCR)] >> 31) & 1;
+ cpu->ZFlag = (cpu->VFP[VFP_OFFSET(VFP_FPSCR)] >> 30) & 1;
+ cpu->CFlag = (cpu->VFP[VFP_OFFSET(VFP_FPSCR)] >> 29) & 1;
+ cpu->VFlag = (cpu->VFP[VFP_OFFSET(VFP_FPSCR)] >> 28) & 1;
+ }
+ }
+ else
+ {
+ switch (inst_cream->reg)
+ {
+ case 0:
+ cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_OFFSET(VFP_FPSID)];
+ break;
+ case 6:
+ /* MVFR1, VFPv3 only ? */
+ LOG("\tr%d <= MVFR1 unimplemented\n", inst_cream->Rt);
+ break;
+ case 7:
+ /* MVFR0, VFPv3 only? */
+ LOG("\tr%d <= MVFR0 unimplemented\n", inst_cream->Rt);
+ break;
+ case 8:
+ cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_OFFSET(VFP_FPEXC)];
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vmrs_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VMOVBCR scalar to register */
+/* cond 1110 XXX1 Vd-- Rt-- 1011 NXX1 0000 */
+/* cond 1110 op11 CRn- Rt-- copr op21 CRm- MCR */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vmovbcr_inst {
+ unsigned int esize;
+ unsigned int index;
+ unsigned int d;
+ unsigned int t;
+} vmovbcr_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbcr)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vmovbcr_inst));
+ vmovbcr_inst *inst_cream = (vmovbcr_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->d = BITS(inst, 16, 19)|BIT(inst, 7)<<4;
+ inst_cream->t = BITS(inst, 12, 15);
+ /* VFP variant of instruction */
+ inst_cream->esize = 32;
+ inst_cream->index = BIT(inst, 21);
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VMOVBCR_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vmovbcr_inst *inst_cream = (vmovbcr_inst *)inst_base->component;
+
+ VFP_DEBUG_UNIMPLEMENTED(VMOVBCR);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vmovbcr_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* MRRC / MCRR instructions */
+/* cond 1100 0101 Rt2- Rt-- copr opc1 CRm- MRRC */
+/* cond 1100 0100 Rt2- Rt-- copr opc1 CRm- MCRR */
+
+/* ----------------------------------------------------------------------- */
+/* VMOVBRRSS between 2 registers to 2 singles */
+/* cond 1100 010X Rt2- Rt-- 1010 00X1 Vm-- */
+/* cond 1100 0101 Rt2- Rt-- copr opc1 CRm- MRRC */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vmovbrrss_inst {
+ unsigned int to_arm;
+ unsigned int t;
+ unsigned int t2;
+ unsigned int m;
+} vmovbrrss_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrrss)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vmovbrrss_inst));
+ vmovbrrss_inst *inst_cream = (vmovbrrss_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->to_arm = BIT(inst, 20) == 1;
+ inst_cream->t = BITS(inst, 12, 15);
+ inst_cream->t2 = BITS(inst, 16, 19);
+ inst_cream->m = BITS(inst, 0, 3)<<1|BIT(inst, 5);
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VMOVBRRSS_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vmovbrrss_inst* const inst_cream = (vmovbrrss_inst*)inst_base->component;
+
+ VMOVBRRSS(cpu, inst_cream->to_arm, inst_cream->t, inst_cream->t2, inst_cream->m,
+ &cpu->Reg[inst_cream->t], &cpu->Reg[inst_cream->t2]);
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vmovbrrss_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VMOVBRRD between 2 registers and 1 double */
+/* cond 1100 010X Rt2- Rt-- 1011 00X1 Vm-- */
+/* cond 1100 0101 Rt2- Rt-- copr opc1 CRm- MRRC */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vmovbrrd_inst {
+ unsigned int to_arm;
+ unsigned int t;
+ unsigned int t2;
+ unsigned int m;
+} vmovbrrd_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrrd)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vmovbrrd_inst));
+ vmovbrrd_inst *inst_cream = (vmovbrrd_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->to_arm = BIT(inst, 20) == 1;
+ inst_cream->t = BITS(inst, 12, 15);
+ inst_cream->t2 = BITS(inst, 16, 19);
+ inst_cream->m = BIT(inst, 5)<<4 | BITS(inst, 0, 3);
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VMOVBRRD_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vmovbrrd_inst *inst_cream = (vmovbrrd_inst *)inst_base->component;
+
+ VMOVBRRD(cpu, inst_cream->to_arm, inst_cream->t, inst_cream->t2, inst_cream->m,
+ &(cpu->Reg[inst_cream->t]), &(cpu->Reg[inst_cream->t2]));
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vmovbrrd_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* LDC/STC between 2 registers and 1 double */
+/* cond 110X XXX1 Rn-- CRd- copr imm- imm- LDC */
+/* cond 110X XXX0 Rn-- CRd- copr imm8 imm8 STC */
+
+/* ----------------------------------------------------------------------- */
+/* VSTR */
+/* cond 1101 UD00 Rn-- Vd-- 101X imm8 imm8 */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vstr_inst {
+ unsigned int single;
+ unsigned int n;
+ unsigned int d;
+ unsigned int imm32;
+ unsigned int add;
+} vstr_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vstr)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vstr_inst));
+ vstr_inst *inst_cream = (vstr_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->single = BIT(inst, 8) == 0;
+ inst_cream->add = BIT(inst, 23);
+ inst_cream->imm32 = BITS(inst, 0,7) << 2;
+ inst_cream->d = (inst_cream->single ? BITS(inst, 12, 15)<<1|BIT(inst, 22) : BITS(inst, 12, 15)|BIT(inst, 22)<<4);
+ inst_cream->n = BITS(inst, 16, 19);
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VSTR_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vstr_inst *inst_cream = (vstr_inst *)inst_base->component;
+
+ unsigned int base = (inst_cream->n == 15 ? (cpu->Reg[inst_cream->n] & 0xFFFFFFFC) + 8 : cpu->Reg[inst_cream->n]);
+ addr = (inst_cream->add ? base + inst_cream->imm32 : base - inst_cream->imm32);
+
+ if (inst_cream->single)
+ {
+ ARMul_StoreWordN(cpu, addr, cpu->ExtReg[inst_cream->d]);
+ }
+ else
+ {
+ const u32 word1 = cpu->ExtReg[inst_cream->d*2+0];
+ const u32 word2 = cpu->ExtReg[inst_cream->d*2+1];
+
+ if (InBigEndianMode(cpu)) {
+ ARMul_StoreWordN(cpu, addr + 0, word2);
+ ARMul_StoreWordN(cpu, addr + 4, word1);
+ } else {
+ ARMul_StoreWordN(cpu, addr + 0, word1);
+ ARMul_StoreWordN(cpu, addr + 4, word2);
+ }
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vstr_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VPUSH */
+/* cond 1101 0D10 1101 Vd-- 101X imm8 imm8 */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vpush_inst {
+ unsigned int single;
+ unsigned int d;
+ unsigned int imm32;
+ unsigned int regs;
+} vpush_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vpush)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vpush_inst));
+ vpush_inst *inst_cream = (vpush_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->single = BIT(inst, 8) == 0;
+ inst_cream->d = (inst_cream->single ? BITS(inst, 12, 15)<<1|BIT(inst, 22) : BITS(inst, 12, 15)|BIT(inst, 22)<<4);
+ inst_cream->imm32 = BITS(inst, 0, 7)<<2;
+ inst_cream->regs = (inst_cream->single ? BITS(inst, 0, 7) : BITS(inst, 1, 7));
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VPUSH_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vpush_inst *inst_cream = (vpush_inst *)inst_base->component;
+
+ addr = cpu->Reg[R13] - inst_cream->imm32;
+
+ for (unsigned int i = 0; i < inst_cream->regs; i++)
+ {
+ if (inst_cream->single)
+ {
+ ARMul_StoreWordN(cpu, addr, cpu->ExtReg[inst_cream->d+i]);
+ addr += 4;
+ }
+ else
+ {
+ const u32 word1 = cpu->ExtReg[(inst_cream->d+i)*2+0];
+ const u32 word2 = cpu->ExtReg[(inst_cream->d+i)*2+1];
+
+ if (InBigEndianMode(cpu)) {
+ ARMul_StoreWordN(cpu, addr + 0, word2);
+ ARMul_StoreWordN(cpu, addr + 4, word1);
+ } else {
+ ARMul_StoreWordN(cpu, addr + 0, word1);
+ ARMul_StoreWordN(cpu, addr + 4, word2);
+ }
+
+ addr += 8;
+ }
+ }
+
+ cpu->Reg[R13] -= inst_cream->imm32;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vpush_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VSTM */
+/* cond 110P UDW0 Rn-- Vd-- 101X imm8 imm8 */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vstm_inst {
+ unsigned int single;
+ unsigned int add;
+ unsigned int wback;
+ unsigned int d;
+ unsigned int n;
+ unsigned int imm32;
+ unsigned int regs;
+} vstm_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vstm)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vstm_inst));
+ vstm_inst *inst_cream = (vstm_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->single = BIT(inst, 8) == 0;
+ inst_cream->add = BIT(inst, 23);
+ inst_cream->wback = BIT(inst, 21);
+ inst_cream->d = (inst_cream->single ? BITS(inst, 12, 15)<<1|BIT(inst, 22) : BITS(inst, 12, 15)|BIT(inst, 22)<<4);
+ inst_cream->n = BITS(inst, 16, 19);
+ inst_cream->imm32 = BITS(inst, 0, 7)<<2;
+ inst_cream->regs = (inst_cream->single ? BITS(inst, 0, 7) : BITS(inst, 1, 7));
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VSTM_INST: /* encoding 1 */
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vstm_inst *inst_cream = (vstm_inst *)inst_base->component;
+
+ addr = (inst_cream->add ? cpu->Reg[inst_cream->n] : cpu->Reg[inst_cream->n] - inst_cream->imm32);
+
+ for (unsigned int i = 0; i < inst_cream->regs; i++)
+ {
+ if (inst_cream->single)
+ {
+ ARMul_StoreWordN(cpu, addr, cpu->ExtReg[inst_cream->d+i]);
+ addr += 4;
+ }
+ else
+ {
+ const u32 word1 = cpu->ExtReg[(inst_cream->d+i)*2+0];
+ const u32 word2 = cpu->ExtReg[(inst_cream->d+i)*2+1];
+
+ if (InBigEndianMode(cpu)) {
+ ARMul_StoreWordN(cpu, addr + 0, word2);
+ ARMul_StoreWordN(cpu, addr + 4, word1);
+ } else {
+ ARMul_StoreWordN(cpu, addr + 0, word1);
+ ARMul_StoreWordN(cpu, addr + 4, word2);
+ }
+
+ addr += 8;
+ }
+ }
+ if (inst_cream->wback){
+ cpu->Reg[inst_cream->n] = (inst_cream->add ? cpu->Reg[inst_cream->n] + inst_cream->imm32 :
+ cpu->Reg[inst_cream->n] - inst_cream->imm32);
+ }
+ }
+ cpu->Reg[15] += 4;
+ INC_PC(sizeof(vstm_inst));
+
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VPOP */
+/* cond 1100 1D11 1101 Vd-- 101X imm8 imm8 */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vpop_inst {
+ unsigned int single;
+ unsigned int d;
+ unsigned int imm32;
+ unsigned int regs;
+} vpop_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vpop)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vpop_inst));
+ vpop_inst *inst_cream = (vpop_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->single = BIT(inst, 8) == 0;
+ inst_cream->d = (inst_cream->single ? (BITS(inst, 12, 15)<<1)|BIT(inst, 22) : BITS(inst, 12, 15)|(BIT(inst, 22)<<4));
+ inst_cream->imm32 = BITS(inst, 0, 7)<<2;
+ inst_cream->regs = (inst_cream->single ? BITS(inst, 0, 7) : BITS(inst, 1, 7));
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VPOP_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vpop_inst *inst_cream = (vpop_inst *)inst_base->component;
+
+ addr = cpu->Reg[R13];
+
+ for (unsigned int i = 0; i < inst_cream->regs; i++)
+ {
+ if (inst_cream->single)
+ {
+ cpu->ExtReg[inst_cream->d+i] = ARMul_LoadWordN(cpu, addr);
+ addr += 4;
+ }
+ else
+ {
+ const u32 word1 = ARMul_LoadWordN(cpu, addr + 0);
+ const u32 word2 = ARMul_LoadWordN(cpu, addr + 4);
+
+ if (InBigEndianMode(cpu)) {
+ cpu->ExtReg[(inst_cream->d+i)*2+0] = word2;
+ cpu->ExtReg[(inst_cream->d+i)*2+1] = word1;
+ } else {
+ cpu->ExtReg[(inst_cream->d+i)*2+0] = word1;
+ cpu->ExtReg[(inst_cream->d+i)*2+1] = word2;
+ }
+
+ addr += 8;
+ }
+ }
+ cpu->Reg[R13] += inst_cream->imm32;
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vpop_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+
+/* ----------------------------------------------------------------------- */
+/* VLDR */
+/* cond 1101 UD01 Rn-- Vd-- 101X imm8 imm8 */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vldr_inst {
+ unsigned int single;
+ unsigned int n;
+ unsigned int d;
+ unsigned int imm32;
+ unsigned int add;
+} vldr_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vldr)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vldr_inst));
+ vldr_inst *inst_cream = (vldr_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->single = BIT(inst, 8) == 0;
+ inst_cream->add = BIT(inst, 23);
+ inst_cream->imm32 = BITS(inst, 0,7) << 2;
+ inst_cream->d = (inst_cream->single ? BITS(inst, 12, 15)<<1|BIT(inst, 22) : BITS(inst, 12, 15)|BIT(inst, 22)<<4);
+ inst_cream->n = BITS(inst, 16, 19);
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VLDR_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vldr_inst *inst_cream = (vldr_inst *)inst_base->component;
+
+ unsigned int base = (inst_cream->n == 15 ? (cpu->Reg[inst_cream->n] & 0xFFFFFFFC) + 8 : cpu->Reg[inst_cream->n]);
+ addr = (inst_cream->add ? base + inst_cream->imm32 : base - inst_cream->imm32);
+
+ if (inst_cream->single)
+ {
+ cpu->ExtReg[inst_cream->d] = ARMul_LoadWordN(cpu, addr);
+ }
+ else
+ {
+ const u32 word1 = ARMul_LoadWordN(cpu, addr + 0);
+ const u32 word2 = ARMul_LoadWordN(cpu, addr + 4);
+
+ if (InBigEndianMode(cpu)) {
+ cpu->ExtReg[inst_cream->d*2+0] = word2;
+ cpu->ExtReg[inst_cream->d*2+1] = word1;
+ } else {
+ cpu->ExtReg[inst_cream->d*2+0] = word1;
+ cpu->ExtReg[inst_cream->d*2+1] = word2;
+ }
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vldr_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* VLDM */
+/* cond 110P UDW1 Rn-- Vd-- 101X imm8 imm8 */
+#ifdef VFP_INTERPRETER_STRUCT
+typedef struct _vldm_inst {
+ unsigned int single;
+ unsigned int add;
+ unsigned int wback;
+ unsigned int d;
+ unsigned int n;
+ unsigned int imm32;
+ unsigned int regs;
+} vldm_inst;
+#endif
+#ifdef VFP_INTERPRETER_TRANS
+static ARM_INST_PTR INTERPRETER_TRANSLATE(vldm)(ARMul_State* cpu,unsigned int inst, int index)
+{
+ arm_inst *inst_base = (arm_inst *)AllocBuffer(cpu,sizeof(arm_inst) + sizeof(vldm_inst));
+ vldm_inst *inst_cream = (vldm_inst *)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->single = BIT(inst, 8) == 0;
+ inst_cream->add = BIT(inst, 23);
+ inst_cream->wback = BIT(inst, 21);
+ inst_cream->d = (inst_cream->single ? BITS(inst, 12, 15)<<1|BIT(inst, 22) : BITS(inst, 12, 15)|BIT(inst, 22)<<4);
+ inst_cream->n = BITS(inst, 16, 19);
+ inst_cream->imm32 = BITS(inst, 0, 7)<<2;
+ inst_cream->regs = (inst_cream->single ? BITS(inst, 0, 7) : BITS(inst, 1, 7));
+
+ return inst_base;
+}
+#endif
+#ifdef VFP_INTERPRETER_IMPL
+VLDM_INST:
+{
+ if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ CHECK_VFP_ENABLED;
+
+ vldm_inst *inst_cream = (vldm_inst *)inst_base->component;
+
+ addr = (inst_cream->add ? cpu->Reg[inst_cream->n] : cpu->Reg[inst_cream->n] - inst_cream->imm32);
+
+ for (unsigned int i = 0; i < inst_cream->regs; i++)
+ {
+ if (inst_cream->single)
+ {
+ cpu->ExtReg[inst_cream->d+i] = ARMul_LoadWordN(cpu, addr);
+ addr += 4;
+ }
+ else
+ {
+ const u32 word1 = ARMul_LoadWordN(cpu, addr + 0);
+ const u32 word2 = ARMul_LoadWordN(cpu, addr + 4);
+
+ if (InBigEndianMode(cpu)) {
+ cpu->ExtReg[(inst_cream->d+i)*2+0] = word2;
+ cpu->ExtReg[(inst_cream->d+i)*2+1] = word1;
+ } else {
+ cpu->ExtReg[(inst_cream->d+i)*2+0] = word1;
+ cpu->ExtReg[(inst_cream->d+i)*2+1] = word2;
+ }
+
+ addr += 8;
+ }
+ }
+ if (inst_cream->wback){
+ cpu->Reg[inst_cream->n] = (inst_cream->add ? cpu->Reg[inst_cream->n] + inst_cream->imm32 :
+ cpu->Reg[inst_cream->n] - inst_cream->imm32);
+ }
+ }
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(vldm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+}
+#endif
--- /dev/null
+/*
+ vfp/vfpsingle.c - ARM VFPv3 emulation unit - SoftFloat single instruction
+ Copyright (C) 2003 Skyeye Develop Group
+ for help please send mail to <skyeye-developer@lists.gro.clinux.org>
+
+ 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 of the License, 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
+*/
+
+/*
+ * This code is derived in part from :
+ * - Android kernel
+ * - John R. Housers softfloat library, which
+ * carries the following notice:
+ *
+ * ===========================================================================
+ * This C source file is part of the SoftFloat IEC/IEEE Floating-point
+ * Arithmetic Package, Release 2.
+ *
+ * Written by John R. Hauser. This work was made possible in part by the
+ * International Computer Science Institute, located at Suite 600, 1947 Center
+ * Street, Berkeley, California 94704. Funding was partially provided by the
+ * National Science Foundation under grant MIP-9311980. The original version
+ * of this code was written as part of a project to build a fixed-point vector
+ * processor in collaboration with the University of California at Berkeley,
+ * overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+ * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+ * arithmetic/softfloat.html'.
+ *
+ * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
+ * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+ * TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
+ * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+ * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+ *
+ * Derivative works are acceptable, even for commercial purposes, so long as
+ * (1) they include prominent notice that the work is derivative, and (2) they
+ * include prominent notice akin to these three paragraphs for those parts of
+ * this code that are retained.
+ * ===========================================================================
+ */
+
+#include "Kernel.h"
+
+#include "arm/skyeye_common/vfp/vfp_helper.h"
+#include "arm/skyeye_common/vfp/asm_vfp.h"
+#include "arm/skyeye_common/vfp/vfp.h"
+
+#define BITS(s, a, b) ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1))
+#define BIT(s, n) ((s >> (n)) & 1)
+
+static struct vfp_single vfp_single_default_qnan = {
+ 255,
+ 0,
+ VFP_SINGLE_SIGNIFICAND_QNAN,
+};
+
+static void vfp_single_dump(const char *str, struct vfp_single *s)
+{
+ pr_debug("VFP: %s: sign=%d exponent=%d significand=%08x\n",
+ str, s->sign != 0, s->exponent, s->significand);
+}
+
+static void vfp_single_normalise_denormal(struct vfp_single *vs)
+{
+ int bits = 31 - fls(vs->significand);
+
+ vfp_single_dump("normalise_denormal: in", vs);
+
+ if (bits) {
+ vs->exponent -= bits - 1;
+ vs->significand <<= bits;
+ }
+
+ vfp_single_dump("normalise_denormal: out", vs);
+}
+
+
+u32 vfp_single_normaliseround(ARMul_State* state, int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func)
+{
+ u32 significand, incr, rmode;
+ int exponent, shift, underflow;
+
+ vfp_single_dump("pack: in", vs);
+
+ /*
+ * Infinities and NaNs are a special case.
+ */
+ if (vs->exponent == 255 && (vs->significand == 0 || exceptions))
+ goto pack;
+
+ /*
+ * Special-case zero.
+ */
+ if (vs->significand == 0) {
+ vs->exponent = 0;
+ goto pack;
+ }
+
+ exponent = vs->exponent;
+ significand = vs->significand;
+
+ /*
+ * Normalise first. Note that we shift the significand up to
+ * bit 31, so we have VFP_SINGLE_LOW_BITS + 1 below the least
+ * significant bit.
+ */
+ shift = 32 - fls(significand);
+ if (shift < 32 && shift) {
+ exponent -= shift;
+ significand <<= shift;
+ }
+
+#if 1
+ vs->exponent = exponent;
+ vs->significand = significand;
+ vfp_single_dump("pack: normalised", vs);
+#endif
+
+ /*
+ * Tiny number?
+ */
+ underflow = exponent < 0;
+ if (underflow) {
+ significand = vfp_shiftright32jamming(significand, -exponent);
+ exponent = 0;
+#if 1
+ vs->exponent = exponent;
+ vs->significand = significand;
+ vfp_single_dump("pack: tiny number", vs);
+#endif
+ if (!(significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1)))
+ underflow = 0;
+ }
+
+ /*
+ * Select rounding increment.
+ */
+ incr = 0;
+ rmode = fpscr & FPSCR_RMODE_MASK;
+
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ incr = 1 << VFP_SINGLE_LOW_BITS;
+ if ((significand & (1 << (VFP_SINGLE_LOW_BITS + 1))) == 0)
+ incr -= 1;
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
+ incr = 0;
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vs->sign != 0))
+ incr = (1 << (VFP_SINGLE_LOW_BITS + 1)) - 1;
+
+ pr_debug("VFP: rounding increment = 0x%08x\n", incr);
+
+ /*
+ * Is our rounding going to overflow?
+ */
+ if ((significand + incr) < significand) {
+ exponent += 1;
+ significand = (significand >> 1) | (significand & 1);
+ incr >>= 1;
+#if 1
+ vs->exponent = exponent;
+ vs->significand = significand;
+ vfp_single_dump("pack: overflow", vs);
+#endif
+ }
+
+ /*
+ * If any of the low bits (which will be shifted out of the
+ * number) are non-zero, the result is inexact.
+ */
+ if (significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1))
+ exceptions |= FPSCR_IXC;
+
+ /*
+ * Do our rounding.
+ */
+ significand += incr;
+
+ /*
+ * Infinity?
+ */
+ if (exponent >= 254) {
+ exceptions |= FPSCR_OFC | FPSCR_IXC;
+ if (incr == 0) {
+ vs->exponent = 253;
+ vs->significand = 0x7fffffff;
+ } else {
+ vs->exponent = 255; /* infinity */
+ vs->significand = 0;
+ }
+ } else {
+ if (significand >> (VFP_SINGLE_LOW_BITS + 1) == 0)
+ exponent = 0;
+ if (exponent || significand > 0x80000000)
+ underflow = 0;
+ if (underflow)
+ exceptions |= FPSCR_UFC;
+ vs->exponent = exponent;
+ vs->significand = significand >> 1;
+ }
+
+pack:
+ vfp_single_dump("pack: final", vs);
+ {
+ s32 d = vfp_single_pack(vs);
+#if 1
+ pr_debug("VFP: %s: d(s%d)=%08x exceptions=%08x\n", func,
+ sd, d, exceptions);
+#endif
+ vfp_put_float(state, d, sd);
+ }
+
+ return exceptions;
+}
+
+/*
+ * Propagate the NaN, setting exceptions if it is signalling.
+ * 'n' is always a NaN. 'm' may be a number, NaN or infinity.
+ */
+static u32
+vfp_propagate_nan(struct vfp_single *vsd, struct vfp_single *vsn,
+ struct vfp_single *vsm, u32 fpscr)
+{
+ struct vfp_single *nan;
+ int tn, tm = 0;
+
+ tn = vfp_single_type(vsn);
+
+ if (vsm)
+ tm = vfp_single_type(vsm);
+
+ if (fpscr & FPSCR_DEFAULT_NAN)
+ /*
+ * Default NaN mode - always returns a quiet NaN
+ */
+ nan = &vfp_single_default_qnan;
+ else {
+ /*
+ * Contemporary mode - select the first signalling
+ * NAN, or if neither are signalling, the first
+ * quiet NAN.
+ */
+ if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN))
+ nan = vsn;
+ else
+ nan = vsm;
+ /*
+ * Make the NaN quiet.
+ */
+ nan->significand |= VFP_SINGLE_SIGNIFICAND_QNAN;
+ }
+
+ *vsd = *nan;
+
+ /*
+ * If one was a signalling NAN, raise invalid operation.
+ */
+ return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : VFP_NAN_FLAG;
+}
+
+
+/*
+ * Extended operations
+ */
+static u32 vfp_single_fabs(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ vfp_put_float(state, vfp_single_packed_abs(m), sd);
+ return 0;
+}
+
+static u32 vfp_single_fcpy(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ vfp_put_float(state, m, sd);
+ return 0;
+}
+
+static u32 vfp_single_fneg(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ vfp_put_float(state, vfp_single_packed_negate(m), sd);
+ return 0;
+}
+
+static const u16 sqrt_oddadjust[] = {
+ 0x0004, 0x0022, 0x005d, 0x00b1, 0x011d, 0x019f, 0x0236, 0x02e0,
+ 0x039c, 0x0468, 0x0545, 0x0631, 0x072b, 0x0832, 0x0946, 0x0a67
+};
+
+static const u16 sqrt_evenadjust[] = {
+ 0x0a2d, 0x08af, 0x075a, 0x0629, 0x051a, 0x0429, 0x0356, 0x029e,
+ 0x0200, 0x0179, 0x0109, 0x00af, 0x0068, 0x0034, 0x0012, 0x0002
+};
+
+u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand)
+{
+ int index;
+ u32 z, a;
+
+ if ((significand & 0xc0000000) != 0x40000000) {
+ pr_debug("VFP: estimate_sqrt: invalid significand\n");
+ }
+
+ a = significand << 1;
+ index = (a >> 27) & 15;
+ if (exponent & 1) {
+ z = 0x4000 + (a >> 17) - sqrt_oddadjust[index];
+ z = ((a / z) << 14) + (z << 15);
+ a >>= 1;
+ } else {
+ z = 0x8000 + (a >> 17) - sqrt_evenadjust[index];
+ z = a / z + z;
+ z = (z >= 0x20000) ? 0xffff8000 : (z << 15);
+ if (z <= a)
+ return (s32)a >> 1;
+ }
+ {
+ u64 v = (u64)a << 31;
+ do_div(v, z);
+ return (u32)(v + (z >> 1));
+ }
+}
+
+static u32 vfp_single_fsqrt(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ struct vfp_single vsm, vsd, *vsp;
+ int ret, tm;
+
+ vfp_single_unpack(&vsm, m);
+ tm = vfp_single_type(&vsm);
+ if (tm & (VFP_NAN|VFP_INFINITY)) {
+ vsp = &vsd;
+
+ if (tm & VFP_NAN)
+ ret = vfp_propagate_nan(vsp, &vsm, NULL, fpscr);
+ else if (vsm.sign == 0) {
+sqrt_copy:
+ vsp = &vsm;
+ ret = 0;
+ } else {
+sqrt_invalid:
+ vsp = &vfp_single_default_qnan;
+ ret = FPSCR_IOC;
+ }
+ vfp_put_float(state, vfp_single_pack(vsp), sd);
+ return ret;
+ }
+
+ /*
+ * sqrt(+/- 0) == +/- 0
+ */
+ if (tm & VFP_ZERO)
+ goto sqrt_copy;
+
+ /*
+ * Normalise a denormalised number
+ */
+ if (tm & VFP_DENORMAL)
+ vfp_single_normalise_denormal(&vsm);
+
+ /*
+ * sqrt(<0) = invalid
+ */
+ if (vsm.sign)
+ goto sqrt_invalid;
+
+ vfp_single_dump("sqrt", &vsm);
+
+ /*
+ * Estimate the square root.
+ */
+ vsd.sign = 0;
+ vsd.exponent = ((vsm.exponent - 127) >> 1) + 127;
+ vsd.significand = vfp_estimate_sqrt_significand(vsm.exponent, vsm.significand) + 2;
+
+ vfp_single_dump("sqrt estimate", &vsd);
+
+ /*
+ * And now adjust.
+ */
+ if ((vsd.significand & VFP_SINGLE_LOW_BITS_MASK) <= 5) {
+ if (vsd.significand < 2) {
+ vsd.significand = 0xffffffff;
+ } else {
+ u64 term;
+ s64 rem;
+ vsm.significand <<= !(vsm.exponent & 1);
+ term = (u64)vsd.significand * vsd.significand;
+ rem = ((u64)vsm.significand << 32) - term;
+
+ pr_debug("VFP: term=%016llx rem=%016llx\n", term, rem);
+
+ while (rem < 0) {
+ vsd.significand -= 1;
+ rem += ((u64)vsd.significand << 1) | 1;
+ }
+ vsd.significand |= rem != 0;
+ }
+ }
+ vsd.significand = vfp_shiftright32jamming(vsd.significand, 1);
+
+ return vfp_single_normaliseround(state, sd, &vsd, fpscr, 0, "fsqrt");
+}
+
+/*
+ * Equal := ZC
+ * Less than := N
+ * Greater than := C
+ * Unordered := CV
+ */
+static u32 vfp_compare(ARMul_State* state, int sd, int signal_on_qnan, s32 m, u32 fpscr)
+{
+ s32 d;
+ u32 ret = 0;
+
+ d = vfp_get_float(state, sd);
+ if (vfp_single_packed_exponent(m) == 255 && vfp_single_packed_mantissa(m)) {
+ ret |= FPSCR_CFLAG | FPSCR_VFLAG;
+ if (signal_on_qnan || !(vfp_single_packed_mantissa(m) & (1 << (VFP_SINGLE_MANTISSA_BITS - 1))))
+ /*
+ * Signalling NaN, or signalling on quiet NaN
+ */
+ ret |= FPSCR_IOC;
+ }
+
+ if (vfp_single_packed_exponent(d) == 255 && vfp_single_packed_mantissa(d)) {
+ ret |= FPSCR_CFLAG | FPSCR_VFLAG;
+ if (signal_on_qnan || !(vfp_single_packed_mantissa(d) & (1 << (VFP_SINGLE_MANTISSA_BITS - 1))))
+ /*
+ * Signalling NaN, or signalling on quiet NaN
+ */
+ ret |= FPSCR_IOC;
+ }
+
+ if (ret == 0) {
+ if (d == m || vfp_single_packed_abs(d | m) == 0) {
+ /*
+ * equal
+ */
+ ret |= FPSCR_ZFLAG | FPSCR_CFLAG;
+ } else if (vfp_single_packed_sign(d ^ m)) {
+ /*
+ * different signs
+ */
+ if (vfp_single_packed_sign(d))
+ /*
+ * d is negative, so d < m
+ */
+ ret |= FPSCR_NFLAG;
+ else
+ /*
+ * d is positive, so d > m
+ */
+ ret |= FPSCR_CFLAG;
+ } else if ((vfp_single_packed_sign(d) != 0) ^ (d < m)) {
+ /*
+ * d < m
+ */
+ ret |= FPSCR_NFLAG;
+ } else if ((vfp_single_packed_sign(d) != 0) ^ (d > m)) {
+ /*
+ * d > m
+ */
+ ret |= FPSCR_CFLAG;
+ }
+ }
+ return ret;
+}
+
+static u32 vfp_single_fcmp(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ return vfp_compare(state, sd, 0, m, fpscr);
+}
+
+static u32 vfp_single_fcmpe(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ return vfp_compare(state, sd, 1, m, fpscr);
+}
+
+static u32 vfp_single_fcmpz(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ return vfp_compare(state, sd, 0, 0, fpscr);
+}
+
+static u32 vfp_single_fcmpez(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ return vfp_compare(state, sd, 1, 0, fpscr);
+}
+
+static u32 vfp_single_fcvtd(ARMul_State* state, int dd, int unused, s32 m, u32 fpscr)
+{
+ struct vfp_single vsm;
+ struct vfp_double vdd;
+ int tm;
+ u32 exceptions = 0;
+
+ vfp_single_unpack(&vsm, m);
+
+ tm = vfp_single_type(&vsm);
+
+ /*
+ * If we have a signalling NaN, signal invalid operation.
+ */
+ if (tm == VFP_SNAN)
+ exceptions = FPSCR_IOC;
+
+ if (tm & VFP_DENORMAL)
+ vfp_single_normalise_denormal(&vsm);
+
+ vdd.sign = vsm.sign;
+ vdd.significand = (u64)vsm.significand << 32;
+
+ /*
+ * If we have an infinity or NaN, the exponent must be 2047.
+ */
+ if (tm & (VFP_INFINITY|VFP_NAN)) {
+ vdd.exponent = 2047;
+ if (tm == VFP_QNAN)
+ vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
+ goto pack_nan;
+ } else if (tm & VFP_ZERO)
+ vdd.exponent = 0;
+ else
+ vdd.exponent = vsm.exponent + (1023 - 127);
+
+ return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fcvtd");
+
+pack_nan:
+ vfp_put_double(state, vfp_double_pack(&vdd), dd);
+ return exceptions;
+}
+
+static u32 vfp_single_fuito(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ struct vfp_single vs;
+
+ vs.sign = 0;
+ vs.exponent = 127 + 31 - 1;
+ vs.significand = (u32)m;
+
+ return vfp_single_normaliseround(state, sd, &vs, fpscr, 0, "fuito");
+}
+
+static u32 vfp_single_fsito(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ struct vfp_single vs;
+
+ vs.sign = (m & 0x80000000) >> 16;
+ vs.exponent = 127 + 31 - 1;
+ vs.significand = vs.sign ? -m : m;
+
+ return vfp_single_normaliseround(state, sd, &vs, fpscr, 0, "fsito");
+}
+
+static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ struct vfp_single vsm;
+ u32 d, exceptions = 0;
+ int rmode = fpscr & FPSCR_RMODE_MASK;
+ int tm;
+
+ vfp_single_unpack(&vsm, m);
+ vfp_single_dump("VSM", &vsm);
+
+ /*
+ * Do we have a denormalised number?
+ */
+ tm = vfp_single_type(&vsm);
+ if (tm & VFP_DENORMAL)
+ exceptions |= FPSCR_IDC;
+
+ if (tm & VFP_NAN)
+ vsm.sign = 1;
+
+ if (vsm.exponent >= 127 + 32) {
+ d = vsm.sign ? 0 : 0xffffffff;
+ exceptions = FPSCR_IOC;
+ } else if (vsm.exponent >= 127) {
+ int shift = 127 + 31 - vsm.exponent;
+ u32 rem, incr = 0;
+
+ /*
+ * 2^0 <= m < 2^32-2^8
+ */
+ d = (vsm.significand << 1) >> shift;
+ rem = vsm.significand << (33 - shift);
+
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ incr = 0x80000000;
+ if ((d & 1) == 0)
+ incr -= 1;
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
+ incr = 0;
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vsm.sign != 0)) {
+ incr = ~0;
+ }
+
+ if ((rem + incr) < rem) {
+ if (d < 0xffffffff)
+ d += 1;
+ else
+ exceptions |= FPSCR_IOC;
+ }
+
+ if (d && vsm.sign) {
+ d = 0;
+ exceptions |= FPSCR_IOC;
+ } else if (rem)
+ exceptions |= FPSCR_IXC;
+ } else {
+ d = 0;
+ if (vsm.exponent | vsm.significand) {
+ exceptions |= FPSCR_IXC;
+ if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
+ d = 1;
+ else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) {
+ d = 0;
+ exceptions |= FPSCR_IOC;
+ }
+ }
+ }
+
+ pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+
+ vfp_put_float(state, d, sd);
+
+ return exceptions;
+}
+
+static u32 vfp_single_ftouiz(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ return vfp_single_ftoui(state, sd, unused, m, FPSCR_ROUND_TOZERO);
+}
+
+static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ struct vfp_single vsm;
+ u32 d, exceptions = 0;
+ int rmode = fpscr & FPSCR_RMODE_MASK;
+ int tm;
+
+ vfp_single_unpack(&vsm, m);
+ vfp_single_dump("VSM", &vsm);
+
+ /*
+ * Do we have a denormalised number?
+ */
+ tm = vfp_single_type(&vsm);
+ if (vfp_single_type(&vsm) & VFP_DENORMAL)
+ exceptions |= FPSCR_IDC;
+
+ if (tm & VFP_NAN) {
+ d = 0;
+ exceptions |= FPSCR_IOC;
+ } else if (vsm.exponent >= 127 + 32) {
+ /*
+ * m >= 2^31-2^7: invalid
+ */
+ d = 0x7fffffff;
+ if (vsm.sign)
+ d = ~d;
+ exceptions |= FPSCR_IOC;
+ } else if (vsm.exponent >= 127) {
+ int shift = 127 + 31 - vsm.exponent;
+ u32 rem, incr = 0;
+
+ /* 2^0 <= m <= 2^31-2^7 */
+ d = (vsm.significand << 1) >> shift;
+ rem = vsm.significand << (33 - shift);
+
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ incr = 0x80000000;
+ if ((d & 1) == 0)
+ incr -= 1;
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
+ incr = 0;
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vsm.sign != 0)) {
+ incr = ~0;
+ }
+
+ if ((rem + incr) < rem && d < 0xffffffff)
+ d += 1;
+ if (d > (0x7fffffffu + (vsm.sign != 0))) {
+ d = (0x7fffffffu + (vsm.sign != 0));
+ exceptions |= FPSCR_IOC;
+ } else if (rem)
+ exceptions |= FPSCR_IXC;
+
+ if (vsm.sign)
+ d = 0-d;
+ } else {
+ d = 0;
+ if (vsm.exponent | vsm.significand) {
+ exceptions |= FPSCR_IXC;
+ if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
+ d = 1;
+ else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign)
+ d = -1;
+ }
+ }
+
+ pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+
+ vfp_put_float(state, (s32)d, sd);
+
+ return exceptions;
+}
+
+static u32 vfp_single_ftosiz(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
+{
+ return vfp_single_ftosi(state, sd, unused, m, FPSCR_ROUND_TOZERO);
+}
+
+static struct op fops_ext[] = {
+ { vfp_single_fcpy, 0 }, //0x00000000 - FEXT_FCPY
+ { vfp_single_fabs, 0 }, //0x00000001 - FEXT_FABS
+ { vfp_single_fneg, 0 }, //0x00000002 - FEXT_FNEG
+ { vfp_single_fsqrt, 0 }, //0x00000003 - FEXT_FSQRT
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { vfp_single_fcmp, OP_SCALAR }, //0x00000008 - FEXT_FCMP
+ { vfp_single_fcmpe, OP_SCALAR }, //0x00000009 - FEXT_FCMPE
+ { vfp_single_fcmpz, OP_SCALAR }, //0x0000000A - FEXT_FCMPZ
+ { vfp_single_fcmpez, OP_SCALAR }, //0x0000000B - FEXT_FCMPEZ
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { vfp_single_fcvtd, OP_SCALAR|OP_DD }, //0x0000000F - FEXT_FCVT
+ { vfp_single_fuito, OP_SCALAR }, //0x00000010 - FEXT_FUITO
+ { vfp_single_fsito, OP_SCALAR }, //0x00000011 - FEXT_FSITO
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { vfp_single_ftoui, OP_SCALAR }, //0x00000018 - FEXT_FTOUI
+ { vfp_single_ftouiz, OP_SCALAR }, //0x00000019 - FEXT_FTOUIZ
+ { vfp_single_ftosi, OP_SCALAR }, //0x0000001A - FEXT_FTOSI
+ { vfp_single_ftosiz, OP_SCALAR }, //0x0000001B - FEXT_FTOSIZ
+};
+
+
+
+
+
+static u32
+vfp_single_fadd_nonnumber(struct vfp_single *vsd, struct vfp_single *vsn,
+ struct vfp_single *vsm, u32 fpscr)
+{
+ struct vfp_single *vsp;
+ u32 exceptions = 0;
+ int tn, tm;
+
+ tn = vfp_single_type(vsn);
+ tm = vfp_single_type(vsm);
+
+ if (tn & tm & VFP_INFINITY) {
+ /*
+ * Two infinities. Are they different signs?
+ */
+ if (vsn->sign ^ vsm->sign) {
+ /*
+ * different signs -> invalid
+ */
+ exceptions = FPSCR_IOC;
+ vsp = &vfp_single_default_qnan;
+ } else {
+ /*
+ * same signs -> valid
+ */
+ vsp = vsn;
+ }
+ } else if (tn & VFP_INFINITY && tm & VFP_NUMBER) {
+ /*
+ * One infinity and one number -> infinity
+ */
+ vsp = vsn;
+ } else {
+ /*
+ * 'n' is a NaN of some type
+ */
+ return vfp_propagate_nan(vsd, vsn, vsm, fpscr);
+ }
+ *vsd = *vsp;
+ return exceptions;
+}
+
+static u32
+vfp_single_add(struct vfp_single *vsd, struct vfp_single *vsn,
+ struct vfp_single *vsm, u32 fpscr)
+{
+ u32 exp_diff, m_sig;
+
+ if (vsn->significand & 0x80000000 ||
+ vsm->significand & 0x80000000) {
+ pr_info("VFP: bad FP values in %s\n", __func__);
+ vfp_single_dump("VSN", vsn);
+ vfp_single_dump("VSM", vsm);
+ }
+
+ /*
+ * Ensure that 'n' is the largest magnitude number. Note that
+ * if 'n' and 'm' have equal exponents, we do not swap them.
+ * This ensures that NaN propagation works correctly.
+ */
+ if (vsn->exponent < vsm->exponent) {
+ struct vfp_single *t = vsn;
+ vsn = vsm;
+ vsm = t;
+ }
+
+ /*
+ * Is 'n' an infinity or a NaN? Note that 'm' may be a number,
+ * infinity or a NaN here.
+ */
+ if (vsn->exponent == 255)
+ return vfp_single_fadd_nonnumber(vsd, vsn, vsm, fpscr);
+
+ /*
+ * We have two proper numbers, where 'vsn' is the larger magnitude.
+ *
+ * Copy 'n' to 'd' before doing the arithmetic.
+ */
+ *vsd = *vsn;
+
+ /*
+ * Align both numbers.
+ */
+ exp_diff = vsn->exponent - vsm->exponent;
+ m_sig = vfp_shiftright32jamming(vsm->significand, exp_diff);
+
+ /*
+ * If the signs are different, we are really subtracting.
+ */
+ if (vsn->sign ^ vsm->sign) {
+ m_sig = vsn->significand - m_sig;
+ if ((s32)m_sig < 0) {
+ vsd->sign = vfp_sign_negate(vsd->sign);
+ m_sig = 0-m_sig;
+ } else if (m_sig == 0) {
+ vsd->sign = (fpscr & FPSCR_RMODE_MASK) ==
+ FPSCR_ROUND_MINUSINF ? 0x8000 : 0;
+ }
+ } else {
+ m_sig = vsn->significand + m_sig;
+ }
+ vsd->significand = m_sig;
+
+ return 0;
+}
+
+static u32
+vfp_single_multiply(struct vfp_single *vsd, struct vfp_single *vsn, struct vfp_single *vsm, u32 fpscr)
+{
+ vfp_single_dump("VSN", vsn);
+ vfp_single_dump("VSM", vsm);
+
+ /*
+ * Ensure that 'n' is the largest magnitude number. Note that
+ * if 'n' and 'm' have equal exponents, we do not swap them.
+ * This ensures that NaN propagation works correctly.
+ */
+ if (vsn->exponent < vsm->exponent) {
+ struct vfp_single *t = vsn;
+ vsn = vsm;
+ vsm = t;
+ pr_debug("VFP: swapping M <-> N\n");
+ }
+
+ vsd->sign = vsn->sign ^ vsm->sign;
+
+ /*
+ * If 'n' is an infinity or NaN, handle it. 'm' may be anything.
+ */
+ if (vsn->exponent == 255) {
+ if (vsn->significand || (vsm->exponent == 255 && vsm->significand))
+ return vfp_propagate_nan(vsd, vsn, vsm, fpscr);
+ if ((vsm->exponent | vsm->significand) == 0) {
+ *vsd = vfp_single_default_qnan;
+ return FPSCR_IOC;
+ }
+ vsd->exponent = vsn->exponent;
+ vsd->significand = 0;
+ return 0;
+ }
+
+ /*
+ * If 'm' is zero, the result is always zero. In this case,
+ * 'n' may be zero or a number, but it doesn't matter which.
+ */
+ if ((vsm->exponent | vsm->significand) == 0) {
+ vsd->exponent = 0;
+ vsd->significand = 0;
+ return 0;
+ }
+
+ /*
+ * We add 2 to the destination exponent for the same reason as
+ * the addition case - though this time we have +1 from each
+ * input operand.
+ */
+ vsd->exponent = vsn->exponent + vsm->exponent - 127 + 2;
+ vsd->significand = vfp_hi64to32jamming((u64)vsn->significand * vsm->significand);
+
+ vfp_single_dump("VSD", vsd);
+ return 0;
+}
+
+#define NEG_MULTIPLY (1 << 0)
+#define NEG_SUBTRACT (1 << 1)
+
+static u32
+vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr, u32 negate, const char *func)
+{
+ vfp_single vsd, vsp, vsn, vsm;
+ u32 exceptions;
+ s32 v;
+
+ v = vfp_get_float(state, sn);
+ pr_debug("VFP: s%u = %08x\n", sn, v);
+ vfp_single_unpack(&vsn, v);
+ if (vsn.exponent == 0 && vsn.significand)
+ vfp_single_normalise_denormal(&vsn);
+
+ vfp_single_unpack(&vsm, m);
+ if (vsm.exponent == 0 && vsm.significand)
+ vfp_single_normalise_denormal(&vsm);
+
+ exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr);
+
+ if (negate & NEG_MULTIPLY)
+ vsp.sign = vfp_sign_negate(vsp.sign);
+
+ v = vfp_get_float(state, sd);
+ pr_debug("VFP: s%u = %08x\n", sd, v);
+ vfp_single_unpack(&vsn, v);
+ if (vsn.exponent == 0 && vsn.significand != 0)
+ vfp_single_normalise_denormal(&vsn);
+
+ if (negate & NEG_SUBTRACT)
+ vsn.sign = vfp_sign_negate(vsn.sign);
+
+ exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr);
+
+ return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, func);
+}
+
+/*
+ * Standard operations
+ */
+
+/*
+ * sd = sd + (sn * sm)
+ */
+static u32 vfp_single_fmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
+{
+ pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd);
+ return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac");
+}
+
+/*
+ * sd = sd - (sn * sm)
+ */
+static u32 vfp_single_fnmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
+{
+ pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sd, sn);
+ return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_MULTIPLY, "fnmac");
+}
+
+/*
+ * sd = -sd + (sn * sm)
+ */
+static u32 vfp_single_fmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
+{
+ pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd);
+ return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_SUBTRACT, "fmsc");
+}
+
+/*
+ * sd = -sd - (sn * sm)
+ */
+static u32 vfp_single_fnmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
+{
+ pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd);
+ return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
+}
+
+/*
+ * sd = sn * sm
+ */
+static u32 vfp_single_fmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
+{
+ struct vfp_single vsd, vsn, vsm;
+ u32 exceptions;
+ s32 n = vfp_get_float(state, sn);
+
+ pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, n);
+
+ vfp_single_unpack(&vsn, n);
+ if (vsn.exponent == 0 && vsn.significand)
+ vfp_single_normalise_denormal(&vsn);
+
+ vfp_single_unpack(&vsm, m);
+ if (vsm.exponent == 0 && vsm.significand)
+ vfp_single_normalise_denormal(&vsm);
+
+ exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
+ return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fmul");
+}
+
+/*
+ * sd = -(sn * sm)
+ */
+static u32 vfp_single_fnmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
+{
+ struct vfp_single vsd, vsn, vsm;
+ u32 exceptions;
+ s32 n = vfp_get_float(state, sn);
+
+ pr_debug("VFP: s%u = %08x\n", sn, n);
+
+ vfp_single_unpack(&vsn, n);
+ if (vsn.exponent == 0 && vsn.significand)
+ vfp_single_normalise_denormal(&vsn);
+
+ vfp_single_unpack(&vsm, m);
+ if (vsm.exponent == 0 && vsm.significand)
+ vfp_single_normalise_denormal(&vsm);
+
+ exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
+ vsd.sign = vfp_sign_negate(vsd.sign);
+ return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fnmul");
+}
+
+/*
+ * sd = sn + sm
+ */
+static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
+{
+ struct vfp_single vsd, vsn, vsm;
+ u32 exceptions;
+ s32 n = vfp_get_float(state, sn);
+
+ pr_debug("VFP: s%u = %08x\n", sn, n);
+
+ /*
+ * Unpack and normalise denormals.
+ */
+ vfp_single_unpack(&vsn, n);
+ if (vsn.exponent == 0 && vsn.significand)
+ vfp_single_normalise_denormal(&vsn);
+
+ vfp_single_unpack(&vsm, m);
+ if (vsm.exponent == 0 && vsm.significand)
+ vfp_single_normalise_denormal(&vsm);
+
+ exceptions = vfp_single_add(&vsd, &vsn, &vsm, fpscr);
+
+ return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fadd");
+}
+
+/*
+ * sd = sn - sm
+ */
+static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
+{
+ pr_debug("In %sVFP: s%u = %08x\n", __FUNCTION__, sn, sd);
+ /*
+ * Subtraction is addition with one sign inverted.
+ */
+ if (m != 0x7FC00000) // Only negate if m isn't NaN.
+ m = vfp_single_packed_negate(m);
+
+ return vfp_single_fadd(state, sd, sn, m, fpscr);
+}
+
+/*
+ * sd = sn / sm
+ */
+static u32 vfp_single_fdiv(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
+{
+ struct vfp_single vsd, vsn, vsm;
+ u32 exceptions = 0;
+ s32 n = vfp_get_float(state, sn);
+ int tm, tn;
+
+ pr_debug("VFP: s%u = %08x\n", sn, n);
+
+ vfp_single_unpack(&vsn, n);
+ vfp_single_unpack(&vsm, m);
+
+ vsd.sign = vsn.sign ^ vsm.sign;
+
+ tn = vfp_single_type(&vsn);
+ tm = vfp_single_type(&vsm);
+
+ /*
+ * Is n a NAN?
+ */
+ if (tn & VFP_NAN)
+ goto vsn_nan;
+
+ /*
+ * Is m a NAN?
+ */
+ if (tm & VFP_NAN)
+ goto vsm_nan;
+
+ /*
+ * If n and m are infinity, the result is invalid
+ * If n and m are zero, the result is invalid
+ */
+ if (tm & tn & (VFP_INFINITY|VFP_ZERO))
+ goto invalid;
+
+ /*
+ * If n is infinity, the result is infinity
+ */
+ if (tn & VFP_INFINITY)
+ goto infinity;
+
+ /*
+ * If m is zero, raise div0 exception
+ */
+ if (tm & VFP_ZERO)
+ goto divzero;
+
+ /*
+ * If m is infinity, or n is zero, the result is zero
+ */
+ if (tm & VFP_INFINITY || tn & VFP_ZERO)
+ goto zero;
+
+ if (tn & VFP_DENORMAL)
+ vfp_single_normalise_denormal(&vsn);
+ if (tm & VFP_DENORMAL)
+ vfp_single_normalise_denormal(&vsm);
+
+ /*
+ * Ok, we have two numbers, we can perform division.
+ */
+ vsd.exponent = vsn.exponent - vsm.exponent + 127 - 1;
+ vsm.significand <<= 1;
+ if (vsm.significand <= (2 * vsn.significand)) {
+ vsn.significand >>= 1;
+ vsd.exponent++;
+ }
+ {
+ u64 significand = (u64)vsn.significand << 32;
+ do_div(significand, vsm.significand);
+ vsd.significand = (u32)significand;
+ }
+ if ((vsd.significand & 0x3f) == 0)
+ vsd.significand |= ((u64)vsm.significand * vsd.significand != (u64)vsn.significand << 32);
+
+ return vfp_single_normaliseround(state, sd, &vsd, fpscr, 0, "fdiv");
+
+vsn_nan:
+ exceptions = vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr);
+pack:
+ vfp_put_float(state, vfp_single_pack(&vsd), sd);
+ return exceptions;
+
+vsm_nan:
+ exceptions = vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr);
+ goto pack;
+
+zero:
+ vsd.exponent = 0;
+ vsd.significand = 0;
+ goto pack;
+
+divzero:
+ exceptions = FPSCR_DZC;
+infinity:
+ vsd.exponent = 255;
+ vsd.significand = 0;
+ goto pack;
+
+invalid:
+ vfp_put_float(state, vfp_single_pack(&vfp_single_default_qnan), sd);
+ return FPSCR_IOC;
+}
+
+static struct op fops[] = {
+ { vfp_single_fmac, 0 },
+ { vfp_single_fmsc, 0 },
+ { vfp_single_fmul, 0 },
+ { vfp_single_fadd, 0 },
+ { vfp_single_fnmac, 0 },
+ { vfp_single_fnmsc, 0 },
+ { vfp_single_fnmul, 0 },
+ { vfp_single_fsub, 0 },
+ { vfp_single_fdiv, 0 },
+};
+
+#define FREG_BANK(x) ((x) & 0x18)
+#define FREG_IDX(x) ((x) & 7)
+
+u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
+{
+ u32 op = inst & FOP_MASK;
+ u32 exceptions = 0;
+ unsigned int dest;
+ unsigned int sn = vfp_get_sn(inst);
+ unsigned int sm = vfp_get_sm(inst);
+ unsigned int vecitr, veclen, vecstride;
+ struct op *fop;
+ pr_debug("In %s\n", __FUNCTION__);
+
+ vecstride = 1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK);
+
+ fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)];
+
+ /*
+ * fcvtsd takes a dN register number as destination, not sN.
+ * Technically, if bit 0 of dd is set, this is an invalid
+ * instruction. However, we ignore this for efficiency.
+ * It also only operates on scalars.
+ */
+ if (fop->flags & OP_DD)
+ dest = vfp_get_dd(inst);
+ else
+ dest = vfp_get_sd(inst);
+
+ /*
+ * If destination bank is zero, vector length is always '1'.
+ * ARM DDI0100F C5.1.3, C5.3.2.
+ */
+ if ((fop->flags & OP_SCALAR) || FREG_BANK(dest) == 0)
+ veclen = 0;
+ else
+ veclen = fpscr & FPSCR_LENGTH_MASK;
+
+ pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride,
+ (veclen >> FPSCR_LENGTH_BIT) + 1);
+
+ if (!fop->fn) {
+ printf("VFP: could not find single op %d, inst=0x%x@0x%x\n", FEXT_TO_IDX(inst), inst, state->Reg[15]);
+ exit(-1);
+ goto invalid;
+ }
+
+ for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) {
+ s32 m = vfp_get_float(state, sm);
+ u32 except;
+ char type;
+
+ type = (fop->flags & OP_DD) ? 'd' : 's';
+ if (op == FOP_EXT)
+ pr_debug("VFP: itr%d (%c%u) = op[%u] (s%u=%08x)\n",
+ vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
+ sm, m);
+ else
+ pr_debug("VFP: itr%d (%c%u) = (s%u) op[%u] (s%u=%08x)\n",
+ vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
+ FOP_TO_IDX(op), sm, m);
+
+ except = fop->fn(state, dest, sn, m, fpscr);
+ pr_debug("VFP: itr%d: exceptions=%08x\n",
+ vecitr >> FPSCR_LENGTH_BIT, except);
+
+ exceptions |= except;
+
+ /*
+ * CHECK: It appears to be undefined whether we stop when
+ * we encounter an exception. We continue.
+ */
+ dest = FREG_BANK(dest) + ((FREG_IDX(dest) + vecstride) & 7);
+ sn = FREG_BANK(sn) + ((FREG_IDX(sn) + vecstride) & 7);
+ if (FREG_BANK(sm) != 0)
+ sm = FREG_BANK(sm) + ((FREG_IDX(sm) + vecstride) & 7);
+ }
+ return exceptions;
+
+invalid:
+ return (u32)-1;
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <type_traits>
+
+#include "citraimport\common/assert.h"
+#include "citraimport\common/bit_field.h"
+#include "citraimport\common/common_funcs.h"
+#include "citraimport\common/common_types.h"
+
+namespace GPU {
+
+// Returns index corresponding to the Regs member labeled by field_name
+// TODO: Due to Visual studio bug 209229, offsetof does not return constant expressions
+// when used with array elements (e.g. GPU_REG_INDEX(memory_fill_config[0])).
+// For details cf. https://connect.microsoft.com/VisualStudio/feedback/details/209229/offsetof-does-not-produce-a-constant-expression-for-array-members
+// Hopefully, this will be fixed sometime in the future.
+// For lack of better alternatives, we currently hardcode the offsets when constant
+// expressions are needed via GPU_REG_INDEX_WORKAROUND (on sane compilers, static_asserts
+// will then make sure the offsets indeed match the automatically calculated ones).
+#define GPU_REG_INDEX(field_name) (offsetof(GPU::Regs, field_name) / sizeof(u32))
+#if defined(_MSC_VER)
+#define GPU_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) (backup_workaround_index)
+#else
+// NOTE: Yeah, hacking in a static_assert here just to workaround the lacking MSVC compiler
+// really is this annoying. This macro just forwards its first argument to GPU_REG_INDEX
+// and then performs a (no-op) cast to size_t iff the second argument matches the expected
+// field offset. Otherwise, the compiler will fail to compile this code.
+#define GPU_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \
+ ((typename std::enable_if<backup_workaround_index == GPU_REG_INDEX(field_name), size_t>::type)GPU_REG_INDEX(field_name))
+#endif
+
+// MMIO region 0x1EFxxxxx
+struct Regs {
+
+// helper macro to make sure the defined structures are of the expected size.
+#if defined(_MSC_VER)
+// TODO: MSVC does not support using sizeof() on non-static data members even though this
+// is technically allowed since C++11. This macro should be enabled once MSVC adds
+// support for that.
+#define ASSERT_MEMBER_SIZE(name, size_in_bytes)
+#else
+#define ASSERT_MEMBER_SIZE(name, size_in_bytes) \
+ static_assert(sizeof(name) == size_in_bytes, \
+ "Structure size and register block length don't match")
+#endif
+
+ // Components are laid out in reverse byte order, most significant bits first.
+ enum class PixelFormat : u32 {
+ RGBA8 = 0,
+ RGB8 = 1,
+ RGB565 = 2,
+ RGB5A1 = 3,
+ RGBA4 = 4,
+ };
+
+ /**
+ * Returns the number of bytes per pixel.
+ */
+ static int BytesPerPixel(PixelFormat format) {
+ switch (format) {
+ case PixelFormat::RGBA8:
+ return 4;
+ case PixelFormat::RGB8:
+ return 3;
+ case PixelFormat::RGB565:
+ case PixelFormat::RGB5A1:
+ case PixelFormat::RGBA4:
+ return 2;
+ default:
+ UNIMPLEMENTED();
+ }
+ }
+
+ INSERT_PADDING_WORDS(0x4);
+
+ struct {
+ u32 address_start;
+ u32 address_end;
+
+ union {
+ u32 value_32bit;
+
+ BitField<0, 16, u32> value_16bit;
+
+ // TODO: Verify component order
+ BitField< 0, 8, u32> value_24bit_r;
+ BitField< 8, 8, u32> value_24bit_g;
+ BitField<16, 8, u32> value_24bit_b;
+ };
+
+ union {
+ u32 control;
+
+ // Setting this field to 1 triggers the memory fill.
+ // This field also acts as a status flag, and gets reset to 0 upon completion.
+ BitField<0, 1, u32> trigger;
+
+ // Set to 1 upon completion.
+ BitField<1, 1, u32> finished;
+
+ // If both of these bits are unset, then it will fill the memory with a 16 bit value
+ // 1: fill with 24-bit wide values
+ BitField<8, 1, u32> fill_24bit;
+ // 1: fill with 32-bit wide values
+ BitField<9, 1, u32> fill_32bit;
+ };
+
+ inline u32 GetStartAddress() const {
+ return DecodeAddressRegister(address_start);
+ }
+
+ inline u32 GetEndAddress() const {
+ return DecodeAddressRegister(address_end);
+ }
+ } memory_fill_config[2];
+ ASSERT_MEMBER_SIZE(memory_fill_config[0], 0x10);
+
+ INSERT_PADDING_WORDS(0x10b);
+
+ struct FramebufferConfig {
+ union {
+ u32 size;
+
+ BitField< 0, 16, u32> width;
+ BitField<16, 16, u32> height;
+ };
+
+ INSERT_PADDING_WORDS(0x2);
+
+ u32 address_left1;
+ u32 address_left2;
+
+ union {
+ u32 format;
+
+ BitField< 0, 3, PixelFormat> color_format;
+ };
+
+ INSERT_PADDING_WORDS(0x1);
+
+ union {
+ u32 active_fb;
+
+ // 0: Use parameters ending with "1"
+ // 1: Use parameters ending with "2"
+ BitField<0, 1, u32> second_fb_active;
+ };
+
+ INSERT_PADDING_WORDS(0x5);
+
+ // Distance between two pixel rows, in bytes
+ u32 stride;
+
+ u32 address_right1;
+ u32 address_right2;
+
+ INSERT_PADDING_WORDS(0x30);
+ } framebuffer_config[2];
+ ASSERT_MEMBER_SIZE(framebuffer_config[0], 0x100);
+
+ INSERT_PADDING_WORDS(0x169);
+
+ struct {
+ u32 input_address;
+ u32 output_address;
+
+ inline u32 GetPhysicalInputAddress() const {
+ return DecodeAddressRegister(input_address);
+ }
+
+ inline u32 GetPhysicalOutputAddress() const {
+ return DecodeAddressRegister(output_address);
+ }
+
+ union {
+ u32 output_size;
+
+ BitField< 0, 16, u32> output_width;
+ BitField<16, 16, u32> output_height;
+ };
+
+ union {
+ u32 input_size;
+
+ BitField< 0, 16, u32> input_width;
+ BitField<16, 16, u32> input_height;
+ };
+
+ enum ScalingMode : u32 {
+ NoScale = 0, // Doesn't scale the image
+ ScaleX = 1, // Downscales the image in half in the X axis and applies a box filter
+ ScaleXY = 2, // Downscales the image in half in both the X and Y axes and applies a box filter
+ };
+
+ union {
+ u32 flags;
+
+ BitField< 0, 1, u32> flip_vertically; // flips input data vertically
+ BitField< 1, 1, u32> input_linear; // Converts from linear to tiled format
+ BitField< 2, 1, u32> crop_input_lines;
+ BitField< 3, 1, u32> is_texture_copy; // Copies the data without performing any processing and respecting texture copy fields
+ BitField< 5, 1, u32> dont_swizzle;
+ BitField< 8, 3, PixelFormat> input_format;
+ BitField<12, 3, PixelFormat> output_format;
+ /// Uses some kind of 32x32 block swizzling mode, instead of the usual 8x8 one.
+ BitField<16, 1, u32> block_32; // TODO(yuriks): unimplemented
+ BitField<24, 2, ScalingMode> scaling; // Determines the scaling mode of the transfer
+ };
+
+ INSERT_PADDING_WORDS(0x1);
+
+ // it seems that writing to this field triggers the display transfer
+ u32 trigger;
+
+ INSERT_PADDING_WORDS(0x1);
+
+ struct {
+ u32 size;
+
+ union {
+ u32 input_size;
+
+ BitField< 0, 16, u32> input_width;
+ BitField<16, 16, u32> input_gap;
+ };
+
+ union {
+ u32 output_size;
+
+ BitField< 0, 16, u32> output_width;
+ BitField<16, 16, u32> output_gap;
+ };
+ } texture_copy;
+ } display_transfer_config;
+ ASSERT_MEMBER_SIZE(display_transfer_config, 0x2c);
+
+ INSERT_PADDING_WORDS(0x32D);
+
+ struct {
+ // command list size (in bytes)
+ u32 size;
+
+ INSERT_PADDING_WORDS(0x1);
+
+ // command list address
+ u32 address;
+
+ INSERT_PADDING_WORDS(0x1);
+
+ // it seems that writing to this field triggers command list processing
+ u32 trigger;
+
+ inline u32 GetPhysicalAddress() const {
+ return DecodeAddressRegister(address);
+ }
+ } command_processor_config;
+ ASSERT_MEMBER_SIZE(command_processor_config, 0x14);
+
+ INSERT_PADDING_WORDS(0x9c3);
+
+ static inline size_t NumIds() {
+ return sizeof(Regs) / sizeof(u32);
+ }
+
+ u32& operator [] (int index) const {
+ u32* content = (u32*)this;
+ return content[index];
+ }
+
+ u32& operator [] (int index) {
+ u32* content = (u32*)this;
+ return content[index];
+ }
+
+#undef ASSERT_MEMBER_SIZE
+
+private:
+ /*
+ * Most physical addresses which GPU registers refer to are 8-byte aligned.
+ * This function should be used to get the address from a raw register value.
+ */
+ static inline u32 DecodeAddressRegister(u32 register_value) {
+ return register_value * 8;
+ }
+};
+static_assert(std::is_standard_layout<Regs>::value, "Structure does not use standard layout");
+
+// TODO: MSVC does not support using offsetof() on non-static data members even though this
+// is technically allowed since C++11. This macro should be enabled once MSVC adds
+// support for that.
+#ifndef _MSC_VER
+#define ASSERT_REG_POSITION(field_name, position) \
+ static_assert(offsetof(Regs, field_name) == position * 4, \
+ "Field "#field_name" has invalid position")
+
+ASSERT_REG_POSITION(memory_fill_config[0], 0x00004);
+ASSERT_REG_POSITION(memory_fill_config[1], 0x00008);
+ASSERT_REG_POSITION(framebuffer_config[0], 0x00117);
+ASSERT_REG_POSITION(framebuffer_config[1], 0x00157);
+ASSERT_REG_POSITION(display_transfer_config, 0x00300);
+ASSERT_REG_POSITION(command_processor_config, 0x00638);
+
+#undef ASSERT_REG_POSITION
+#endif // !defined(_MSC_VER)
+
+// The total number of registers is chosen arbitrarily, but let's make sure it's not some odd value anyway.
+static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of register set");
+
+extern Regs g_regs;
+extern bool g_skip_frame;
+
+template <typename T>
+void Read(T &var, const u32 addr);
+
+template <typename T>
+void Write(u32 addr, const T data);
+
+/// Initialize hardware
+void Init();
+
+/// Shutdown hardware
+void Shutdown();
+
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "citraimport\common/common_types.h"
+
+namespace HW {
+
+/// Beginnings of IO register regions, in the user VA space.
+enum : u32 {
+ VADDR_HASH = 0x1EC01000,
+ VADDR_CSND = 0x1EC03000,
+ VADDR_DSP = 0x1EC40000,
+ VADDR_PDN = 0x1EC41000,
+ VADDR_CODEC = 0x1EC41000,
+ VADDR_SPI = 0x1EC42000,
+ VADDR_SPI_2 = 0x1EC43000, // Only used under TWL_FIRM?
+ VADDR_I2C = 0x1EC44000,
+ VADDR_CODEC_2 = 0x1EC45000,
+ VADDR_HID = 0x1EC46000,
+ VADDR_GPIO = 0x1EC47000,
+ VADDR_I2C_2 = 0x1EC48000,
+ VADDR_SPI_3 = 0x1EC60000,
+ VADDR_I2C_3 = 0x1EC61000,
+ VADDR_MIC = 0x1EC62000,
+ VADDR_PXI = 0x1EC63000,
+ VADDR_LCD = 0x1ED02000,
+ VADDR_DSP_2 = 0x1ED03000,
+ VADDR_HASH_2 = 0x1EE01000,
+ VADDR_GPU = 0x1EF00000,
+};
+
+template <typename T>
+void Read(T &var, const u32 addr);
+
+template <typename T>
+void Write(u32 addr, const T data);
+
+/// Update hardware
+void Update();
+
+/// Initialize hardware
+void Init();
+
+/// Shutdown hardware
+void Shutdown();
+
+} // namespace
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <type_traits>
+
+#include "citraimport\common/bit_field.h"
+#include "citraimport\common/common_funcs.h"
+#include "citraimport\common/common_types.h"
+
+#define LCD_REG_INDEX(field_name) (offsetof(LCD::Regs, field_name) / sizeof(u32))
+
+namespace LCD {
+
+struct Regs {
+
+ union ColorFill {
+ u32 raw;
+
+ BitField<0, 8, u32> color_r;
+ BitField<8, 8, u32> color_g;
+ BitField<16, 8, u32> color_b;
+ BitField<24, 1, u32> is_enabled;
+ };
+
+ INSERT_PADDING_WORDS(0x81);
+ ColorFill color_fill_top;
+ INSERT_PADDING_WORDS(0xE);
+ u32 backlight_top;
+
+ INSERT_PADDING_WORDS(0x1F0);
+
+ ColorFill color_fill_bottom;
+ INSERT_PADDING_WORDS(0xE);
+ u32 backlight_bottom;
+ INSERT_PADDING_WORDS(0x16F);
+
+ static inline size_t NumIds() {
+ return sizeof(Regs) / sizeof(u32);
+ }
+
+ u32& operator [] (int index) const {
+ u32* content = (u32*)this;
+ return content[index];
+ }
+
+ u32& operator [] (int index) {
+ u32* content = (u32*)this;
+ return content[index];
+ }
+
+#undef ASSERT_MEMBER_SIZE
+
+};
+static_assert(std::is_standard_layout<Regs>::value, "Structure does not use standard layout");
+
+// TODO: MSVC does not support using offsetof() on non-static data members even though this
+// is technically allowed since C++11. This macro should be enabled once MSVC adds
+// support for that.
+#ifndef _MSC_VER
+#define ASSERT_REG_POSITION(field_name, position) \
+ static_assert(offsetof(Regs, field_name) == position * 4, \
+ "Field "#field_name" has invalid position")
+
+ASSERT_REG_POSITION(color_fill_top, 0x81);
+ASSERT_REG_POSITION(backlight_top, 0x90);
+ASSERT_REG_POSITION(color_fill_bottom, 0x281);
+ASSERT_REG_POSITION(backlight_bottom, 0x290);
+
+#undef ASSERT_REG_POSITION
+#endif // !defined(_MSC_VER)
+
+extern Regs g_regs;
+
+template <typename T>
+void Read(T &var, const u32 addr);
+
+template <typename T>
+void Write(u32 addr, const T data);
+
+/// Initialize hardware
+void Init();
+
+/// Shutdown hardware
+void Shutdown();
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+extern bool novideo;
+
+#include <cstring>
+#include <numeric>
+#include <type_traits>
+
+#include "citraimport\common/color.h"
+#include "citraimport\common/common_types.h"
+#include "citraimport\common/logging/log.h"
+#include "citraimport\common/microprofile.h"
+#include "citraimport\common/vector_math.h"
+
+#include "citraimport\settings.h"
+
+
+#include "citraimport\GPU\HW/hw.h"
+#include "citraimport\GPU\HW/gpu.h"
+
+#include "citraimport\GPU\video_core/command_processor.h"
+#include "citraimport\GPU\video_core/hwrasterizer_base.h"
+#include "citraimport\GPU\video_core/renderer_base.h"
+#include "citraimport\GPU\video_core/utils.h"
+#include "citraimport\GPU\video_core/video_core.h"
+
+#include "citraimport\GPU\video_core/debug_utils/debug_utils.h"
+
+u8* Mem_GetPhysicalPointer(u32 addr);
+
+extern "C" void citraFireInterrupt(int id);
+
+extern "C" bool citraSettingSkipGSP;
+
+namespace GPU {
+
+Regs g_regs;
+
+/// True if the current frame was skipped
+bool g_skip_frame;
+/// 268MHz CPU clocks / 60Hz frames per second
+const u64 frame_ticks = 268123480ull / 60;
+/// Event id for CoreTiming
+static int vblank_event;
+/// Total number of frames drawn
+static u64 frame_count;
+/// True if the last frame was skipped
+static bool last_skip_frame;
+
+template <typename T>
+inline void Read(T &var, const u32 raw_addr) {
+ u32 addr = raw_addr - HW::VADDR_GPU;
+ u32 index = addr / 4;
+
+ // Reads other than u32 are untested, so I'd rather have them abort than silently fail
+ if (index >= Regs::NumIds() || !std::is_same<T, u32>::value) {
+ LOG_ERROR(HW_GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
+ return;
+ }
+
+ var = g_regs[addr / 4];
+}
+
+static Math::Vec4<u8> DecodePixel(Regs::PixelFormat input_format, const u8* src_pixel) {
+ switch (input_format) {
+ case Regs::PixelFormat::RGBA8:
+ return Color::DecodeRGBA8(src_pixel);
+
+ case Regs::PixelFormat::RGB8:
+ return Color::DecodeRGB8(src_pixel);
+
+ case Regs::PixelFormat::RGB565:
+ return Color::DecodeRGB565(src_pixel);
+
+ case Regs::PixelFormat::RGB5A1:
+ return Color::DecodeRGB5A1(src_pixel);
+
+ case Regs::PixelFormat::RGBA4:
+ return Color::DecodeRGBA4(src_pixel);
+
+ default:
+ LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", input_format);
+ return {0, 0, 0, 0};
+ }
+}
+
+template <typename T>
+inline void Write(u32 addr, const T data) {
+ addr -= HW::VADDR_GPU;
+ u32 index = addr / 4;
+
+ // Writes other than u32 are untested, so I'd rather have them abort than silently fail
+ if (index >= Regs::NumIds() || !std::is_same<T, u32>::value) {
+ LOG_ERROR(HW_GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
+ return;
+ }
+
+ g_regs[index] = static_cast<u32>(data);
+
+ switch (index) {
+
+ // Memory fills are triggered once the fill value is written.
+ case GPU_REG_INDEX_WORKAROUND(memory_fill_config[0].trigger, 0x00004 + 0x3):
+ case GPU_REG_INDEX_WORKAROUND(memory_fill_config[1].trigger, 0x00008 + 0x3):
+ {
+ const bool is_second_filler = (index != GPU_REG_INDEX(memory_fill_config[0].trigger));
+ auto& config = g_regs.memory_fill_config[is_second_filler];
+
+ if (config.trigger) {
+ if (config.address_start) { // Some games pass invalid values here
+ u8* start = Mem_GetPhysicalPointer(config.GetStartAddress());
+ u8* end = Mem_GetPhysicalPointer(config.GetEndAddress());
+
+ if (config.fill_24bit) {
+ // fill with 24-bit values
+ for (u8* ptr = start; ptr < end; ptr += 3) {
+ ptr[0] = config.value_24bit_r;
+ ptr[1] = config.value_24bit_g;
+ ptr[2] = config.value_24bit_b;
+ }
+ } else if (config.fill_32bit) {
+ // fill with 32-bit values
+ for (u32* ptr = (u32*)start; ptr < (u32*)end; ++ptr)
+ *ptr = config.value_32bit;
+ } else {
+ // fill with 16-bit values
+ for (u16* ptr = (u16*)start; ptr < (u16*)end; ++ptr)
+ *ptr = config.value_16bit;
+ }
+
+ LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
+
+ if (!is_second_filler) {
+ citraFireInterrupt(0x29/*GSP_GPU::InterruptId::PSC0*/);
+ } else {
+ citraFireInterrupt(0x28/*GSP_GPU::InterruptId::PSC1*/);
+ }
+
+ if (!citraSettingSkipGSP)
+ VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress());
+ }
+
+ // Reset "trigger" flag and set the "finish" flag
+ // NOTE: This was confirmed to happen on hardware even if "address_start" is zero.
+ config.trigger = 0;
+ config.finished = 1;
+ }
+ break;
+ }
+
+ case GPU_REG_INDEX(display_transfer_config.trigger):
+ {
+
+ const auto& config = g_regs.display_transfer_config;
+ if (config.trigger & 1) {
+
+ if (Pica::g_debug_context)
+ Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::IncomingDisplayTransfer, nullptr);
+
+ u8* src_pointer = Mem_GetPhysicalPointer(config.GetPhysicalInputAddress());
+ u8* dst_pointer = Mem_GetPhysicalPointer(config.GetPhysicalOutputAddress());
+
+ if (config.is_texture_copy) {
+ u32 input_width = config.texture_copy.input_width * 16;
+ u32 input_gap = config.texture_copy.input_gap * 16;
+ u32 output_width = config.texture_copy.output_width * 16;
+ u32 output_gap = config.texture_copy.output_gap * 16;
+
+ size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap);
+ VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(config.GetPhysicalInputAddress(), contiguous_input_size);
+
+ u32 remaining_size = config.texture_copy.size;
+ u32 remaining_input = input_width;
+ u32 remaining_output = output_width;
+ while (remaining_size > 0) {
+ u32 copy_size = std::min({ remaining_input, remaining_output, remaining_size });
+
+ std::memcpy(dst_pointer, src_pointer, copy_size);
+ src_pointer += copy_size;
+ dst_pointer += copy_size;
+
+ remaining_input -= copy_size;
+ remaining_output -= copy_size;
+ remaining_size -= copy_size;
+
+ if (remaining_input == 0) {
+ remaining_input = input_width;
+ src_pointer += input_gap;
+ }
+ if (remaining_output == 0) {
+ remaining_output = output_width;
+ dst_pointer += output_gap;
+ }
+ }
+
+ LOG_TRACE(HW_GPU, "TextureCopy: 0x%X bytes from 0x%08X(%u+%u)-> 0x%08X(%u+%u), flags 0x%08X",
+ config.texture_copy.size,
+ config.GetPhysicalInputAddress(), input_width, input_gap,
+ config.GetPhysicalOutputAddress(), output_width, output_gap,
+ config.flags);
+
+ size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap);
+ VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), contiguous_output_size);
+
+ citraFireInterrupt(0x2C/*GSP_GPU::InterruptId::PPF*/);
+ break;
+ }
+
+ if (config.scaling > config.ScaleXY) {
+ LOG_CRITICAL(HW_GPU, "Unimplemented display transfer scaling mode %u", config.scaling.Value());
+ UNIMPLEMENTED();
+ break;
+ }
+
+ if (config.input_linear && config.scaling != config.NoScale) {
+ LOG_CRITICAL(HW_GPU, "Scaling is only implemented on tiled input");
+ UNIMPLEMENTED();
+ break;
+ }
+
+ bool horizontal_scale = config.scaling != config.NoScale;
+ bool vertical_scale = config.scaling == config.ScaleXY;
+
+ u32 output_width = config.output_width >> horizontal_scale;
+ u32 output_height = config.output_height >> vertical_scale;
+
+ u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format);
+ u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format);
+
+ VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(config.GetPhysicalInputAddress(), input_size);
+
+ for (u32 y = 0; y < output_height; ++y) {
+ for (u32 x = 0; x < output_width; ++x) {
+ Math::Vec4<u8> src_color;
+
+ // Calculate the [x,y] position of the input image
+ // based on the current output position and the scale
+ u32 input_x = x << horizontal_scale;
+ u32 input_y = y << vertical_scale;
+
+ if (config.flip_vertically) {
+ // Flip the y value of the output data,
+ // we do this after calculating the [x,y] position of the input image
+ // to account for the scaling options.
+ y = output_height - y - 1;
+ }
+
+ u32 dst_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.output_format);
+ u32 src_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.input_format);
+ u32 src_offset;
+ u32 dst_offset;
+
+ if (config.input_linear) {
+ if (!config.dont_swizzle) {
+ // Interpret the input as linear and the output as tiled
+ u32 coarse_y = y & ~7;
+ u32 stride = output_width * dst_bytes_per_pixel;
+
+ src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel;
+ dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride;
+ } else {
+ // Both input and output are linear
+ src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel;
+ dst_offset = (x + y * output_width) * dst_bytes_per_pixel;
+ }
+ } else {
+ if (!config.dont_swizzle) {
+ // Interpret the input as tiled and the output as linear
+ u32 coarse_y = input_y & ~7;
+ u32 stride = config.input_width * src_bytes_per_pixel;
+
+ src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride;
+ dst_offset = (x + y * output_width) * dst_bytes_per_pixel;
+ } else {
+ // Both input and output are tiled
+ u32 out_coarse_y = y & ~7;
+ u32 out_stride = output_width * dst_bytes_per_pixel;
+
+ u32 in_coarse_y = input_y & ~7;
+ u32 in_stride = config.input_width * src_bytes_per_pixel;
+
+ src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + in_coarse_y * in_stride;
+ dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + out_coarse_y * out_stride;
+ }
+ }
+
+ const u8* src_pixel = src_pointer + src_offset;
+ src_color = DecodePixel(config.input_format, src_pixel);
+ if (config.scaling == config.ScaleX) {
+ Math::Vec4<u8> pixel = DecodePixel(config.input_format, src_pixel + src_bytes_per_pixel);
+ src_color = ((src_color + pixel) / 2).Cast<u8>();
+ } else if (config.scaling == config.ScaleXY) {
+ Math::Vec4<u8> pixel1 = DecodePixel(config.input_format, src_pixel + 1 * src_bytes_per_pixel);
+ Math::Vec4<u8> pixel2 = DecodePixel(config.input_format, src_pixel + 2 * src_bytes_per_pixel);
+ Math::Vec4<u8> pixel3 = DecodePixel(config.input_format, src_pixel + 3 * src_bytes_per_pixel);
+ src_color = (((src_color + pixel1) + (pixel2 + pixel3)) / 4).Cast<u8>();
+ }
+
+ u8* dst_pixel = dst_pointer + dst_offset;
+ switch (config.output_format) {
+ case Regs::PixelFormat::RGBA8:
+ Color::EncodeRGBA8(src_color, dst_pixel);
+ break;
+
+ case Regs::PixelFormat::RGB8:
+ Color::EncodeRGB8(src_color, dst_pixel);
+ break;
+
+ case Regs::PixelFormat::RGB565:
+ Color::EncodeRGB565(src_color, dst_pixel);
+ break;
+
+ case Regs::PixelFormat::RGB5A1:
+ Color::EncodeRGB5A1(src_color, dst_pixel);
+ break;
+
+ case Regs::PixelFormat::RGBA4:
+ Color::EncodeRGBA4(src_color, dst_pixel);
+ break;
+
+ default:
+ LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value());
+ break;
+ }
+ }
+ }
+
+ LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x, flags 0x%08X",
+ config.output_height * output_width * GPU::Regs::BytesPerPixel(config.output_format),
+ config.GetPhysicalInputAddress(), config.input_width.Value(), config.input_height.Value(),
+ config.GetPhysicalOutputAddress(), output_width, output_height,
+ config.output_format.Value(), config.flags);
+
+ g_regs.display_transfer_config.trigger = 0;
+ citraFireInterrupt(0x2C/*GSP_GPU::InterruptId::PPF*/);
+
+ VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), output_size);
+ }
+ break;
+ }
+
+ // Seems like writing to this register triggers processing
+ case GPU_REG_INDEX(command_processor_config.trigger):
+ {
+ const auto& config = g_regs.command_processor_config;
+ if (config.trigger & 1)
+ {
+
+ u32* buffer = (u32*)Mem_GetPhysicalPointer(config.GetPhysicalAddress());
+
+ Pica::CommandProcessor::ProcessCommandList(buffer, config.size);
+
+ g_regs.command_processor_config.trigger = 0;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+// Explicitly instantiate template functions because we aren't defining this in the header:
+
+template void Read<u64>(u64 &var, const u32 addr);
+template void Read<u32>(u32 &var, const u32 addr);
+template void Read<u16>(u16 &var, const u32 addr);
+template void Read<u8>(u8 &var, const u32 addr);
+
+template void Write<u64>(u32 addr, const u64 data);
+template void Write<u32>(u32 addr, const u32 data);
+template void Write<u16>(u32 addr, const u16 data);
+template void Write<u8>(u32 addr, const u8 data);
+
+/// Update hardware
+extern "C" void VBlankCallback() {
+ frame_count++;
+ last_skip_frame = g_skip_frame;
+ g_skip_frame = (frame_count & Settings::values.frame_skip) != 0;
+
+ // Swap buffers based on the frameskip mode, which is a little bit tricky. When
+ // a frame is being skipped, nothing is being rendered to the internal framebuffer(s).
+ // So, we should only swap frames if the last frame was rendered. The rules are:
+ // - If frameskip == 0 (disabled), always swap buffers
+ // - If frameskip == 1, swap buffers every other frame (starting from the first frame)
+ // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame)
+ if (!novideo)
+ {
+ if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) && last_skip_frame != g_skip_frame) ||
+ Settings::values.frame_skip == 0) {
+ VideoCore::g_renderer->SwapBuffers();
+ }
+ }
+
+}
+
+/// Initialize hardware
+void Init() {
+ memset(&g_regs, 0, sizeof(g_regs));
+
+ auto& framebuffer_top = g_regs.framebuffer_config[0];
+ auto& framebuffer_sub = g_regs.framebuffer_config[1];
+
+ // Setup default framebuffer addresses (located in VRAM)
+ // .. or at least these are the ones used by system applets.
+ // There's probably a smarter way to come up with addresses
+ // like this which does not require hardcoding.
+ framebuffer_top.address_left1 = 0x181E6000;
+ framebuffer_top.address_left2 = 0x1822C800;
+ framebuffer_top.address_right1 = 0x18273000;
+ framebuffer_top.address_right2 = 0x182B9800;
+ framebuffer_sub.address_left1 = 0x1848F000;
+ framebuffer_sub.address_left2 = 0x184C7800;
+
+ framebuffer_top.width = 240;
+ framebuffer_top.height = 400;
+ framebuffer_top.stride = 3 * 240;
+ framebuffer_top.color_format = Regs::PixelFormat::RGB8;
+ framebuffer_top.active_fb = 0;
+
+ framebuffer_sub.width = 240;
+ framebuffer_sub.height = 320;
+ framebuffer_sub.stride = 3 * 240;
+ framebuffer_sub.color_format = Regs::PixelFormat::RGB8;
+ framebuffer_sub.active_fb = 0;
+
+ last_skip_frame = false;
+ g_skip_frame = false;
+ frame_count = 0;
+
+ LOG_DEBUG(HW_GPU, "initialized OK");
+}
+
+/// Shutdown hardware
+void Shutdown() {
+ LOG_DEBUG(HW_GPU, "shutdown OK");
+}
+
+} // namespace
--- /dev/null
+set(SRCS
+ renderer_opengl/gl_rasterizer.cpp
+ renderer_opengl/gl_rasterizer_cache.cpp
+ renderer_opengl/gl_shader_util.cpp
+ renderer_opengl/gl_state.cpp
+ renderer_opengl/renderer_opengl.cpp
+ debug_utils/debug_utils.cpp
+ clipper.cpp
+ command_processor.cpp
+ pica.cpp
+ primitive_assembly.cpp
+ rasterizer.cpp
+ shader/shader.cpp
+ shader/shader_interpreter.cpp
+ utils.cpp
+ video_core.cpp
+ )
+
+set(HEADERS
+ debug_utils/debug_utils.h
+ renderer_opengl/gl_rasterizer.h
+ renderer_opengl/gl_rasterizer_cache.h
+ renderer_opengl/gl_resource_manager.h
+ renderer_opengl/gl_shader_util.h
+ renderer_opengl/gl_shaders.h
+ renderer_opengl/gl_state.h
+ renderer_opengl/pica_to_gl.h
+ renderer_opengl/renderer_opengl.h
+ clipper.h
+ command_processor.h
+ gpu_debugger.h
+ hwrasterizer_base.h
+ pica.h
+ primitive_assembly.h
+ rasterizer.h
+ renderer_base.h
+ shader/shader.h
+ shader/shader_interpreter.h
+ utils.h
+ video_core.h
+ )
+
+if(ARCHITECTURE_x86_64)
+ set(SRCS ${SRCS}
+ shader/shader_jit_x64.cpp)
+
+ set(HEADERS ${HEADERS}
+ shader/shader_jit_x64.h)
+endif()
+
+create_directory_groups(${SRCS} ${HEADERS})
+
+add_library(video_core STATIC ${SRCS} ${HEADERS})
+target_link_libraries(video_core glad)
+
+if (PNG_FOUND)
+ target_link_libraries(video_core ${PNG_LIBRARIES})
+ include_directories(${PNG_INCLUDE_DIRS})
+ add_definitions(${PNG_DEFINITIONS})
+endif()
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+//#include <boost/container/static_vector.hpp>
+
+#include "citraimport/GPU/video_core/clipper.h"
+#include "citraimport/GPU/video_core/pica.h"
+#include "citraimport/GPU/video_core/rasterizer.h"
+#include "citraimport/GPU/video_core/shader/shader_interpreter.h"
+
+namespace Pica {
+
+namespace Clipper {
+
+struct ClippingEdge {
+public:
+ ClippingEdge(Math::Vec4<float24> coeffs,
+ Math::Vec4<float24> bias = Math::Vec4<float24>(float24::FromFloat32(0),
+ float24::FromFloat32(0),
+ float24::FromFloat32(0),
+ float24::FromFloat32(0)))
+ : coeffs(coeffs),
+ bias(bias)
+ {
+ }
+
+ bool IsInside(const OutputVertex& vertex) const {
+ return Math::Dot(vertex.pos + bias, coeffs) <= float24::FromFloat32(0);
+ }
+
+ bool IsOutSide(const OutputVertex& vertex) const {
+ return !IsInside(vertex);
+ }
+
+ OutputVertex GetIntersection(const OutputVertex& v0, const OutputVertex& v1) const {
+ float24 dp = Math::Dot(v0.pos + bias, coeffs);
+ float24 dp_prev = Math::Dot(v1.pos + bias, coeffs);
+ float24 factor = dp_prev / (dp_prev - dp);
+
+ return OutputVertex::Lerp(factor, v0, v1);
+ }
+
+private:
+ float24 pos;
+ Math::Vec4<float24> coeffs;
+ Math::Vec4<float24> bias;
+};
+
+static void InitScreenCoordinates(OutputVertex& vtx)
+{
+ struct {
+ float24 halfsize_x;
+ float24 offset_x;
+ float24 halfsize_y;
+ float24 offset_y;
+ float24 zscale;
+ float24 offset_z;
+ } viewport;
+
+ const auto& regs = g_state.regs;
+ viewport.halfsize_x = float24::FromRawFloat24(regs.viewport_size_x);
+ viewport.halfsize_y = float24::FromRawFloat24(regs.viewport_size_y);
+ viewport.offset_x = float24::FromFloat32(static_cast<float>(regs.viewport_corner.x));
+ viewport.offset_y = float24::FromFloat32(static_cast<float>(regs.viewport_corner.y));
+ viewport.zscale = float24::FromRawFloat24(regs.viewport_depth_range);
+ viewport.offset_z = float24::FromRawFloat24(regs.viewport_depth_far_plane);
+
+ float24 inv_w = float24::FromFloat32(1.f) / vtx.pos.w;
+ vtx.color *= inv_w;
+ vtx.tc0 *= inv_w;
+ vtx.tc1 *= inv_w;
+ vtx.tc2 *= inv_w;
+ vtx.pos.w = inv_w;
+
+ vtx.screenpos[0] = (vtx.pos.x * inv_w + float24::FromFloat32(1.0)) * viewport.halfsize_x + viewport.offset_x;
+ vtx.screenpos[1] = (vtx.pos.y * inv_w + float24::FromFloat32(1.0)) * viewport.halfsize_y + viewport.offset_y;
+ vtx.screenpos[2] = viewport.offset_z + vtx.pos.z * inv_w * viewport.zscale;
+}
+
+void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) {
+
+ // Clipping a planar n-gon against a plane will remove at least 1 vertex and introduces 2 at
+ // the new edge (or less in degenerate cases). As such, we can say that each clipping plane
+ // introduces at most 1 new vertex to the polygon. Since we start with a triangle and have a
+ // fixed 6 clipping planes, the maximum number of vertices of the clipped polygon is 3 + 6 = 9.
+ //static const size_t MAX_VERTICES = 9; //auto optimced for 8 so it works
+ std::vector<OutputVertex> buffer_a = { v0, v1, v2 };
+ std::vector<OutputVertex> buffer_b;
+ auto* output_list = &buffer_a;
+ auto* input_list = &buffer_b;
+
+ // NOTE: We clip against a w=epsilon plane to guarantee that the output has a positive w value.
+ // TODO: Not sure if this is a valid approach. Also should probably instead use the smallest
+ // epsilon possible within float24 accuracy.
+ static const float24 EPSILON = float24::FromFloat32(0.00001f);
+ static const float24 f0 = float24::FromFloat32(0.0);
+ static const float24 f1 = float24::FromFloat32(1.0);
+ static const std::array<ClippingEdge, 7> clipping_edges = {{
+ { Math::MakeVec( f1, f0, f0, -f1) }, // x = +w
+ { Math::MakeVec(-f1, f0, f0, -f1) }, // x = -w
+ { Math::MakeVec( f0, f1, f0, -f1) }, // y = +w
+ { Math::MakeVec( f0, -f1, f0, -f1) }, // y = -w
+ { Math::MakeVec( f0, f0, f1, f0) }, // z = 0
+ { Math::MakeVec( f0, f0, -f1, -f1) }, // z = -w
+ { Math::MakeVec( f0, f0, f0, -f1), Math::Vec4<float24>(f0, f0, f0, EPSILON) }, // w = EPSILON
+ }};
+
+ // TODO: If one vertex lies outside one of the depth clipping planes, some platforms (e.g. Wii)
+ // drop the whole primitive instead of clipping the primitive properly. We should test if
+ // this happens on the 3DS, too.
+
+ // Simple implementation of the Sutherland-Hodgman clipping algorithm.
+ // TODO: Make this less inefficient (currently lots of useless buffering overhead happens here)
+ for (auto edge : clipping_edges) {
+
+ std::swap(input_list, output_list);
+ output_list->clear();
+
+ const OutputVertex* reference_vertex = &input_list->back();
+
+ for (const auto& vertex : *input_list) {
+ // NOTE: This algorithm changes vertex order in some cases!
+ if (edge.IsInside(vertex)) {
+ if (edge.IsOutSide(*reference_vertex)) {
+ output_list->push_back(edge.GetIntersection(vertex, *reference_vertex));
+ }
+
+ output_list->push_back(vertex);
+ } else if (edge.IsInside(*reference_vertex)) {
+ output_list->push_back(edge.GetIntersection(vertex, *reference_vertex));
+ }
+ reference_vertex = &vertex;
+ }
+
+ // Need to have at least a full triangle to continue...
+ if (output_list->size() < 3)
+ return;
+ }
+
+ InitScreenCoordinates((*output_list)[0]);
+ InitScreenCoordinates((*output_list)[1]);
+
+ for (size_t i = 0; i < output_list->size() - 2; i ++) {
+ OutputVertex& vtx0 = (*output_list)[0];
+ OutputVertex& vtx1 = (*output_list)[i+1];
+ OutputVertex& vtx2 = (*output_list)[i+2];
+
+ InitScreenCoordinates(vtx2);
+
+ LOG_TRACE(Render_Software,
+ "Triangle %lu/%lu at position (%.3f, %.3f, %.3f, %.3f), "
+ "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and "
+ "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)",
+ i + 1, output_list->size() - 2,
+ vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(),
+ vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(),
+ vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(),
+ vtx0.screenpos.x.ToFloat32(), vtx0.screenpos.y.ToFloat32(), vtx0.screenpos.z.ToFloat32(),
+ vtx1.screenpos.x.ToFloat32(), vtx1.screenpos.y.ToFloat32(), vtx1.screenpos.z.ToFloat32(),
+ vtx2.screenpos.x.ToFloat32(), vtx2.screenpos.y.ToFloat32(), vtx2.screenpos.z.ToFloat32());
+
+ Rasterizer::ProcessTriangle(vtx0, vtx1, vtx2);
+ }
+}
+
+
+} // namespace
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Pica {
+
+namespace Shader {
+ struct OutputVertex;
+}
+
+namespace Clipper {
+
+using Shader::OutputVertex;
+
+void ProcessTriangle(OutputVertex& v0, OutputVertex& v1, OutputVertex& v2);
+
+} // namespace
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#define NOMINMAX
+
+#include <cmath>
+
+#include <cassert>
+
+#include <citraimport/settings.h>
+
+#include "citraimport\GPU\video_core/clipper.h"
+#include "citraimport\GPU\video_core/command_processor.h"
+#include "citraimport\GPU\video_core/pica.h"
+#include "citraimport\GPU\video_core/primitive_assembly.h"
+#include "citraimport\GPU\video_core/renderer_base.h"
+#include "citraimport\GPU\video_core/video_core.h"
+#include "citraimport\GPU\video_core/debug_utils/debug_utils.h"
+#include "citraimport\GPU\video_core/shader/shader_interpreter.h"
+
+#include "citraimport\GPU\HW\gpu.h"
+
+u8* Mem_GetPhysicalPointer(u32 addr);
+extern "C" void citraFireInterrupt(int id);
+
+
+namespace Pica {
+
+namespace CommandProcessor {
+
+static int float_regs_counter = 0;
+
+static u32 uniform_write_buffer[4];
+
+static int default_attr_counter = 0;
+
+static u32 default_attr_write_buffer[3];
+
+extern "C" void citraFireInterrupt(int id);
+
+// Expand a 4-bit mask to 4-byte mask, e.g. 0b0101 -> 0x00FF00FF
+static const u32 expand_bits_to_bytes[] = {
+ 0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff,
+ 0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff,
+ 0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff,
+ 0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff
+};
+
+extern "C" bool citraSettingSkipGSP;
+
+static void WritePicaReg(u32 id, u32 value, u32 mask) {
+ auto& regs = g_state.regs;
+
+ if (id >= regs.NumIds())
+ return;
+
+ // If we're skipping this frame, only allow trigger IRQ
+ if (GPU::g_skip_frame && id != PICA_REG_INDEX(trigger_irq))
+ return;
+
+ // TODO: Figure out how register masking acts on e.g. vs.uniform_setup.set_value
+ u32 old_value = regs[id];
+
+ const u32 write_mask = expand_bits_to_bytes[mask];
+
+ regs[id] = (old_value & ~write_mask) | (value & write_mask);
+
+ DebugUtils::OnPicaRegWrite({ (u16)id, (u16)mask, regs[id] });
+
+ if (g_debug_context)
+ g_debug_context->OnEvent(DebugContext::Event::PicaCommandLoaded, reinterpret_cast<void*>(&id));
+
+ switch(id) {
+ // Trigger IRQ
+ case PICA_REG_INDEX(trigger_irq):
+ citraFireInterrupt(0x2D /*P3D*/);
+ break;
+
+ // Load default vertex input attributes
+ case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[0], 0x233):
+ case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[1], 0x234):
+ case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[2], 0x235):
+ {
+ // TODO: Does actual hardware indeed keep an intermediate buffer or does
+ // it directly write the values?
+ default_attr_write_buffer[default_attr_counter++] = value;
+
+ // Default attributes are written in a packed format such that four float24 values are encoded in
+ // three 32-bit numbers. We write to internal memory once a full such vector is
+ // written.
+ if (default_attr_counter >= 3) {
+ default_attr_counter = 0;
+
+ auto& setup = regs.vs_default_attributes_setup;
+
+ if (setup.index >= 16) {
+ LOG_ERROR(HW_GPU, "Invalid VS default attribute index %d", (int)setup.index);
+ break;
+ }
+
+ Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index];
+
+ // NOTE: The destination component order indeed is "backwards"
+ attribute.w = float24::FromRawFloat24(default_attr_write_buffer[0] >> 8);
+ attribute.z = float24::FromRawFloat24(((default_attr_write_buffer[0] & 0xFF) << 16) | ((default_attr_write_buffer[1] >> 16) & 0xFFFF));
+ attribute.y = float24::FromRawFloat24(((default_attr_write_buffer[1] & 0xFFFF) << 8) | ((default_attr_write_buffer[2] >> 24) & 0xFF));
+ attribute.x = float24::FromRawFloat24(default_attr_write_buffer[2] & 0xFFFFFF);
+
+ LOG_TRACE(HW_GPU, "Set default VS attribute %x to (%f %f %f %f)", (int)setup.index,
+ attribute.x.ToFloat32(), attribute.y.ToFloat32(), attribute.z.ToFloat32(),
+ attribute.w.ToFloat32());
+
+ // TODO: Verify that this actually modifies the register!
+ setup.index = setup.index + 1;
+ }
+ break;
+ }
+
+ case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[0], 0x23c):
+ case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[1], 0x23d):
+ {
+ unsigned index = static_cast<unsigned>(id - PICA_REG_INDEX(command_buffer.trigger[0]));
+ u32* head_ptr = (u32*)Mem_GetPhysicalPointer(regs.command_buffer.GetPhysicalAddress(index));
+ g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = head_ptr;
+ g_state.cmd_list.length = regs.command_buffer.GetSize(index) / sizeof(u32);
+ break;
+ }
+
+ // It seems like these trigger vertex rendering
+ case PICA_REG_INDEX(trigger_draw):
+ case PICA_REG_INDEX(trigger_draw_indexed):
+ {
+
+#if PICA_LOG_TEV
+ DebugUtils::DumpTevStageConfig(regs.GetTevStages());
+#endif
+ if (!citraSettingSkipGSP)
+ {
+ if (g_debug_context)
+ g_debug_context->OnEvent(DebugContext::Event::IncomingPrimitiveBatch, nullptr);
+
+ const auto& attribute_config = regs.vertex_attributes;
+ const u32 base_address = attribute_config.GetPhysicalBaseAddress();
+
+ // Information about internal vertex attributes
+ u32 vertex_attribute_sources[16];
+ for (int i = 0; i < 16; ++i)
+ vertex_attribute_sources[i] = 0xdeadbeef;
+ u32 vertex_attribute_strides[16] = {};
+ Regs::VertexAttributeFormat vertex_attribute_formats[16] = {};
+
+ u32 vertex_attribute_elements[16] = {};
+ u32 vertex_attribute_element_size[16] = {};
+
+ // Setup attribute data from loaders
+ for (int loader = 0; loader < 12; ++loader) {
+ const auto& loader_config = attribute_config.attribute_loaders[loader];
+
+ u32 load_address = base_address + loader_config.data_offset;
+
+ // TODO: What happens if a loader overwrites a previous one's data?
+ for (unsigned component = 0; component < loader_config.component_count; ++component) {
+ u32 attribute_index = loader_config.GetComponent(component);
+ vertex_attribute_sources[attribute_index] = load_address;
+ vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count);
+ vertex_attribute_formats[attribute_index] = attribute_config.GetFormat(attribute_index);
+ vertex_attribute_elements[attribute_index] = attribute_config.GetNumElements(attribute_index);
+ vertex_attribute_element_size[attribute_index] = attribute_config.GetElementSizeInBytes(attribute_index);
+ load_address += attribute_config.GetStride(attribute_index);
+ }
+ }
+
+ // Load vertices
+ bool is_indexed = (id == PICA_REG_INDEX(trigger_draw_indexed));
+
+ const auto& index_info = regs.index_array;
+ const u8* index_address_8 = Mem_GetPhysicalPointer(base_address + index_info.offset);
+ const u16* index_address_16 = (u16*)index_address_8;
+ bool index_u16 = index_info.format != 0;
+
+#if PICA_DUMP_GEOMETRY
+ DebugUtils::GeometryDumper geometry_dumper;
+ PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value());
+#endif
+ PrimitiveAssembler<Shader::OutputVertex> primitive_assembler(regs.triangle_topology.Value());
+
+ if (g_debug_context) {
+ for (int i = 0; i < 3; ++i) {
+ const auto texture = regs.GetTextures()[i];
+ if (!texture.enabled)
+ continue;
+
+ u8* texture_data = Mem_GetPhysicalPointer(texture.config.GetPhysicalAddress());
+ }
+ }
+
+ class {
+ /// Combine overlapping and close ranges
+ void SimplifyRanges() {
+ for (auto it = ranges.begin(); it != ranges.end(); ++it) {
+ // NOTE: We add 32 to the range end address to make sure "close" ranges are combined, too
+ auto it2 = std::next(it);
+ while (it2 != ranges.end() && it->first + it->second + 32 >= it2->first) {
+ it->second = std::max(it->second, it2->first + it2->second - it->first);
+ it2 = ranges.erase(it2);
+ }
+ }
+ }
+
+ public:
+ /// Record a particular memory access in the list
+ void AddAccess(u32 paddr, u32 size) {
+ // Create new range or extend existing one
+ ranges[paddr] = std::max(ranges[paddr], size);
+
+ // Simplify ranges...
+ SimplifyRanges();
+ }
+
+ /// Map of accessed ranges (mapping start address to range size)
+ std::map<u32, u32> ranges;
+ } memory_accesses;
+
+ // Simple circular-replacement vertex cache
+ // The size has been tuned for optimal balance between hit-rate and the cost of lookup
+ const size_t VERTEX_CACHE_SIZE = 32;
+ std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids;
+ std::array<Shader::OutputVertex, VERTEX_CACHE_SIZE> vertex_cache;
+
+ unsigned int vertex_cache_pos = 0;
+ vertex_cache_ids.fill(-1);
+
+ Shader::UnitState<false> shader_unit;
+ Shader::Setup(shader_unit);
+
+ for (unsigned int index = 0; index < regs.num_vertices; ++index)
+ {
+ // Indexed rendering doesn't use the start offset
+ unsigned int vertex = is_indexed ? (index_u16 ? index_address_16[index] : index_address_8[index]) : (index + regs.vertex_offset);
+
+ // -1 is a common special value used for primitive restart. Since it's unknown if
+ // the PICA supports it, and it would mess up the caching, guard against it here.
+ //ASSERT(vertex != -1);
+
+ bool vertex_cache_hit = false;
+ Shader::OutputVertex output;
+
+ if (is_indexed) {
+
+ for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) {
+ if (vertex == vertex_cache_ids[i]) {
+ output = vertex_cache[i];
+ vertex_cache_hit = true;
+ break;
+ }
+ }
+ }
+
+ if (!vertex_cache_hit) {
+ // Initialize data for the current vertex
+ Shader::InputVertex input;
+
+ for (int i = 0; i < attribute_config.GetNumTotalAttributes(); ++i) {
+ if (vertex_attribute_elements[i] != 0) {
+ // Default attribute values set if array elements have < 4 components. This
+ // is *not* carried over from the default attribute settings even if they're
+ // enabled for this attribute.
+ static const float24 zero = float24::FromFloat32(0.0f);
+ static const float24 one = float24::FromFloat32(1.0f);
+ input.attr[i] = Math::Vec4<float24>(zero, zero, zero, one);
+
+ // Load per-vertex data from the loader arrays
+ for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) {
+ u32 source_addr = vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i];
+ const u8* srcdata = Mem_GetPhysicalPointer(source_addr);
+
+ /*if (g_debug_context && Pica::g_debug_context->recorder) {
+ memory_accesses.AddAccess(source_addr,
+ (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::FLOAT) ? 4
+ : (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? 2 : 1);
+ }*/
+
+ const float srcval = (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::BYTE) ? *(s8*)srcdata :
+ (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::UBYTE) ? *(u8*)srcdata :
+ (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? *(s16*)srcdata :
+ *(float*)srcdata;
+
+ input.attr[i][comp] = float24::FromFloat32(srcval);
+ LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08x + 0x%04x: %f",
+ comp, i, vertex, index,
+ attribute_config.GetPhysicalBaseAddress(),
+ vertex_attribute_sources[i] - base_address,
+ vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i],
+ input.attr[i][comp].ToFloat32());
+ }
+ }
+ else if (attribute_config.IsDefaultAttribute(i)) {
+ // Load the default attribute if we're configured to do so
+ input.attr[i] = g_state.vs.default_attributes[i];
+ LOG_TRACE(HW_GPU, "Loaded default attribute %x for vertex %x (index %x): (%f, %f, %f, %f)",
+ i, vertex, index,
+ input.attr[i][0].ToFloat32(), input.attr[i][1].ToFloat32(),
+ input.attr[i][2].ToFloat32(), input.attr[i][3].ToFloat32());
+ }
+ else {
+ // TODO(yuriks): In this case, no data gets loaded and the vertex
+ // remains with the last value it had. This isn't currently maintained
+ // as global state, however, and so won't work in Citra yet.
+ }
+ }
+
+ if (g_debug_context)
+ g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, (void*)&input);
+
+#if PICA_DUMP_GEOMETRY
+ // NOTE: When dumping geometry, we simply assume that the first input attribute
+ // corresponds to the position for now.
+ DebugUtils::GeometryDumper::Vertex dumped_vertex = {
+ input.attr[0][0].ToFloat32(), input.attr[0][1].ToFloat32(), input.attr[0][2].ToFloat32()
+ };
+ using namespace std::placeholders;
+ dumping_primitive_assembler.SubmitVertex(dumped_vertex,
+ std::bind(&DebugUtils::GeometryDumper::AddTriangle,
+ &geometry_dumper, _1, _2, _3));
+#endif
+ // Send to vertex shader
+ output = Shader::Run(shader_unit, input, attribute_config.GetNumTotalAttributes());
+
+ if (is_indexed) {
+ vertex_cache[vertex_cache_pos] = output;
+ vertex_cache_ids[vertex_cache_pos] = vertex;
+ vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE;
+ }
+ }
+
+ if (Settings::values.use_hw_renderer) {
+ // Send to hardware renderer
+ static auto AddHWTriangle = [](const Pica::Shader::OutputVertex& v0,
+ const Pica::Shader::OutputVertex& v1,
+ const Pica::Shader::OutputVertex& v2) {
+ VideoCore::g_renderer->hw_rasterizer->AddTriangle(v0, v1, v2);
+ };
+
+ primitive_assembler.SubmitVertex(output, AddHWTriangle);
+ }
+ else {
+ // Send to triangle clipper
+ primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle);
+ }
+ }
+
+ for (auto& range : memory_accesses.ranges) {
+ /*g_debug_context->recorder->MemoryAccessed(Memory::GetPhysicalPointer(range.first),
+ range.second, range.first);*/
+ }
+
+ if (Settings::values.use_hw_renderer) {
+ VideoCore::g_renderer->hw_rasterizer->DrawTriangles();
+ }
+
+#if PICA_DUMP_GEOMETRY
+ geometry_dumper.Dump();
+#endif
+
+ if (g_debug_context) {
+ g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
+ }
+ }
+ break;
+ }
+
+ case PICA_REG_INDEX(vs.bool_uniforms):
+ for (unsigned i = 0; i < 16; ++i)
+ g_state.vs.uniforms.b[i] = (regs.vs.bool_uniforms.Value() & (1 << i)) != 0;
+
+ break;
+
+ case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[0], 0x2b1):
+ case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[1], 0x2b2):
+ case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[2], 0x2b3):
+ case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[3], 0x2b4):
+ {
+ int index = (id - PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[0], 0x2b1));
+ auto values = regs.vs.int_uniforms[index];
+ g_state.vs.uniforms.i[index] = Math::Vec4<u8>(values.x, values.y, values.z, values.w);
+ LOG_TRACE(HW_GPU, "Set integer uniform %d to %02x %02x %02x %02x",
+ index, values.x.Value(), values.y.Value(), values.z.Value(), values.w.Value());
+ break;
+ }
+
+ case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[0], 0x2c1):
+ case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[1], 0x2c2):
+ case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[2], 0x2c3):
+ case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[3], 0x2c4):
+ case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[4], 0x2c5):
+ case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[5], 0x2c6):
+ case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[6], 0x2c7):
+ case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[7], 0x2c8):
+ {
+ auto& uniform_setup = regs.vs.uniform_setup;
+
+ // TODO: Does actual hardware indeed keep an intermediate buffer or does
+ // it directly write the values?
+ uniform_write_buffer[float_regs_counter++] = value;
+
+ // Uniforms are written in a packed format such that four float24 values are encoded in
+ // three 32-bit numbers. We write to internal memory once a full such vector is
+ // written.
+ if ((float_regs_counter >= 4 && uniform_setup.IsFloat32()) ||
+ (float_regs_counter >= 3 && !uniform_setup.IsFloat32())) {
+ float_regs_counter = 0;
+
+ auto& uniform = g_state.vs.uniforms.f[uniform_setup.index];
+
+ if (uniform_setup.index > 95) {
+ LOG_ERROR(HW_GPU, "Invalid VS uniform index %d", (int)uniform_setup.index);
+ break;
+ }
+
+ // NOTE: The destination component order indeed is "backwards"
+ if (uniform_setup.IsFloat32()) {
+ for (auto i : {0,1,2,3})
+ uniform[3 - i] = float24::FromFloat32(*(float*)(&uniform_write_buffer[i]));
+ } else {
+ // TODO: Untested
+ uniform.w = float24::FromRawFloat24(uniform_write_buffer[0] >> 8);
+ uniform.z = float24::FromRawFloat24(((uniform_write_buffer[0] & 0xFF)<<16) | ((uniform_write_buffer[1] >> 16) & 0xFFFF));
+ uniform.y = float24::FromRawFloat24(((uniform_write_buffer[1] & 0xFFFF)<<8) | ((uniform_write_buffer[2] >> 24) & 0xFF));
+ uniform.x = float24::FromRawFloat24(uniform_write_buffer[2] & 0xFFFFFF);
+ }
+
+ LOG_TRACE(HW_GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index,
+ uniform.x.ToFloat32(), uniform.y.ToFloat32(), uniform.z.ToFloat32(),
+ uniform.w.ToFloat32());
+
+ // TODO: Verify that this actually modifies the register!
+ uniform_setup.index = uniform_setup.index + 1;
+ }
+ break;
+ }
+
+ // Load shader program code
+ case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[0], 0x2cc):
+ case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[1], 0x2cd):
+ case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[2], 0x2ce):
+ case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[3], 0x2cf):
+ case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[4], 0x2d0):
+ case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[5], 0x2d1):
+ case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[6], 0x2d2):
+ case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[7], 0x2d3):
+ {
+ g_state.vs.program_code[regs.vs.program.offset] = value;
+ regs.vs.program.offset++;
+ break;
+ }
+
+ // Load swizzle pattern data
+ case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[0], 0x2d6):
+ case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[1], 0x2d7):
+ case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[2], 0x2d8):
+ case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[3], 0x2d9):
+ case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[4], 0x2da):
+ case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[5], 0x2db):
+ case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[6], 0x2dc):
+ case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[7], 0x2dd):
+ {
+ g_state.vs.swizzle_data[regs.vs.swizzle_patterns.offset] = value;
+ regs.vs.swizzle_patterns.offset++;
+ break;
+ }
+
+ default:
+ break;
+ }
+ if (!citraSettingSkipGSP)
+ {
+ VideoCore::g_renderer->hw_rasterizer->NotifyPicaRegisterChanged(id);
+ }
+
+ if (g_debug_context)
+ g_debug_context->OnEvent(DebugContext::Event::PicaCommandProcessed, reinterpret_cast<void*>(&id));
+}
+
+void ProcessCommandList(const u32* list, u32 size) {
+ g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = list;
+ g_state.cmd_list.length = size;
+
+ while (g_state.cmd_list.current_ptr < g_state.cmd_list.head_ptr + (g_state.cmd_list.length << 1)) {
+
+ // Align read pointer to 8 bytes
+ if ((g_state.cmd_list.head_ptr - g_state.cmd_list.current_ptr) % 2 != 0)
+ ++g_state.cmd_list.current_ptr;
+
+ u32 value = *g_state.cmd_list.current_ptr++;
+ const CommandHeader header = { *g_state.cmd_list.current_ptr++ };
+ u32 cmd = header.cmd_id;
+
+
+ printf("reg %03x\n", cmd);
+
+ WritePicaReg(cmd, value, header.parameter_mask);
+
+ for (unsigned i = 0; i < header.extra_data_length; ++i) {
+ u32 cmd = header.cmd_id + (header.group_commands ? i + 1 : 0);
+ WritePicaReg(cmd, *g_state.cmd_list.current_ptr++, header.parameter_mask);
+ }
+ }
+}
+
+} // namespace
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <type_traits>
+
+#include "citraimport\common/bit_field.h"
+#include "citraimport\common/common_types.h"
+
+namespace Pica {
+
+namespace CommandProcessor {
+
+union CommandHeader {
+ u32 hex;
+
+ BitField< 0, 16, u32> cmd_id;
+
+ // parameter_mask:
+ // Mask applied to the input value to make it possible to update
+ // parts of a register without overwriting its other fields.
+ // first bit: 0x000000FF
+ // second bit: 0x0000FF00
+ // third bit: 0x00FF0000
+ // fourth bit: 0xFF000000
+ BitField<16, 4, u32> parameter_mask;
+
+ BitField<20, 11, u32> extra_data_length;
+
+ BitField<31, 1, u32> group_commands;
+};
+static_assert(std::is_standard_layout<CommandHeader>::value == true,
+ "CommandHeader does not use standard layout");
+static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!");
+
+void ProcessCommandList(const u32* list, u32 size);
+
+} // namespace
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <condition_variable>
+#include <cstring>
+#include <fstream>
+#include <list>
+#include <map>
+#include <mutex>
+#include <string>
+
+#ifdef HAVE_PNG
+#include <png.h>
+#endif
+
+#include <citraimport/nihstro/float24.h>
+#include <citraimport/nihstro/shader_binary.h>
+
+#include "citraimport/common/assert.h"
+#include "citraimport/common/color.h"
+#include "citraimport/common/common_types.h"
+#include "citraimport/common/file_util.h"
+#include "citraimport/common/math_util.h"
+#include "citraimport/common/vector_math.h"
+
+#include "citraimport/settings.h"
+
+#include "citraimport/GPU\video_core/pica.h"
+#include "citraimport/GPU\video_core/renderer_base.h"
+#include "citraimport/GPU\video_core/utils.h"
+#include "citraimport/GPU\video_core/video_core.h"
+#include "citraimport/GPU\video_core/debug_utils/debug_utils.h"
+
+using nihstro::DVLBHeader;
+using nihstro::DVLEHeader;
+using nihstro::DVLPHeader;
+
+namespace Pica {
+
+void DebugContext::OnEvent(Event event, void* data) {
+ if (!breakpoints[event].enabled)
+ return;
+
+ {
+ std::unique_lock<std::mutex> lock(breakpoint_mutex);
+
+ if (Settings::values.use_hw_renderer) {
+ // Commit the hardware renderer's framebuffer so it will show on debug widgets
+ VideoCore::g_renderer->hw_rasterizer->CommitFramebuffer();
+ }
+
+ // TODO: Should stop the CPU thread here once we multithread emulation.
+
+ active_breakpoint = event;
+ at_breakpoint = true;
+
+ // Tell all observers that we hit a breakpoint
+ for (auto& breakpoint_observer : breakpoint_observers) {
+ breakpoint_observer->OnPicaBreakPointHit(event, data);
+ }
+
+ // Wait until another thread tells us to Resume()
+ resume_from_breakpoint.wait(lock, [&]{ return !at_breakpoint; });
+ }
+}
+
+void DebugContext::Resume() {
+ {
+ std::lock_guard<std::mutex> lock(breakpoint_mutex);
+
+ // Tell all observers that we are about to resume
+ for (auto& breakpoint_observer : breakpoint_observers) {
+ breakpoint_observer->OnPicaResume();
+ }
+
+ // Resume the waiting thread (i.e. OnEvent())
+ at_breakpoint = false;
+ }
+
+ resume_from_breakpoint.notify_one();
+}
+
+std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
+
+namespace DebugUtils {
+
+void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) {
+ vertices.push_back(v0);
+ vertices.push_back(v1);
+ vertices.push_back(v2);
+
+ int num_vertices = (int)vertices.size();
+ faces.push_back({{ num_vertices-3, num_vertices-2, num_vertices-1 }});
+}
+
+void GeometryDumper::Dump() {
+ static int index = 0;
+ std::string filename = std::string("geometry_dump") + std::to_string(++index) + ".obj";
+
+ std::ofstream file(filename);
+
+ for (const auto& vertex : vertices) {
+ file << "v " << vertex.pos[0]
+ << " " << vertex.pos[1]
+ << " " << vertex.pos[2] << std::endl;
+ }
+
+ for (const Face& face : faces) {
+ file << "f " << 1+face.index[0]
+ << " " << 1+face.index[1]
+ << " " << 1+face.index[2] << std::endl;
+ }
+}
+
+
+void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const State::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes)
+{
+ struct StuffToWrite {
+ u8* pointer;
+ u32 size;
+ };
+ std::vector<StuffToWrite> writing_queue;
+ u32 write_offset = 0;
+
+ auto QueueForWriting = [&writing_queue,&write_offset](u8* pointer, u32 size) {
+ writing_queue.push_back({pointer, size});
+ u32 old_write_offset = write_offset;
+ write_offset += size;
+ return old_write_offset;
+ };
+
+ // First off, try to translate Pica state (one enum for output attribute type and component)
+ // into shbin format (separate type and component mask).
+ union OutputRegisterInfo {
+ enum Type : u64 {
+ POSITION = 0,
+ QUATERNION = 1,
+ COLOR = 2,
+ TEXCOORD0 = 3,
+ TEXCOORD1 = 5,
+ TEXCOORD2 = 6,
+
+ VIEW = 8,
+ };
+
+ BitField< 0, 64, u64> hex;
+
+ BitField< 0, 16, Type> type;
+ BitField<16, 16, u64> id;
+ BitField<32, 4, u64> component_mask;
+ };
+
+ // This is put into a try-catch block to make sure we notice unknown configurations.
+ std::vector<OutputRegisterInfo> output_info_table;
+ for (unsigned i = 0; i < 7; ++i) {
+ using OutputAttributes = Pica::Regs::VSOutputAttributes;
+
+ // TODO: It's still unclear how the attribute components map to the register!
+ // Once we know that, this code probably will not make much sense anymore.
+ std::map<OutputAttributes::Semantic, std::pair<OutputRegisterInfo::Type, u32> > map = {
+ { OutputAttributes::POSITION_X, { OutputRegisterInfo::POSITION, 1} },
+ { OutputAttributes::POSITION_Y, { OutputRegisterInfo::POSITION, 2} },
+ { OutputAttributes::POSITION_Z, { OutputRegisterInfo::POSITION, 4} },
+ { OutputAttributes::POSITION_W, { OutputRegisterInfo::POSITION, 8} },
+ { OutputAttributes::QUATERNION_X, { OutputRegisterInfo::QUATERNION, 1} },
+ { OutputAttributes::QUATERNION_Y, { OutputRegisterInfo::QUATERNION, 2} },
+ { OutputAttributes::QUATERNION_Z, { OutputRegisterInfo::QUATERNION, 4} },
+ { OutputAttributes::QUATERNION_W, { OutputRegisterInfo::QUATERNION, 8} },
+ { OutputAttributes::COLOR_R, { OutputRegisterInfo::COLOR, 1} },
+ { OutputAttributes::COLOR_G, { OutputRegisterInfo::COLOR, 2} },
+ { OutputAttributes::COLOR_B, { OutputRegisterInfo::COLOR, 4} },
+ { OutputAttributes::COLOR_A, { OutputRegisterInfo::COLOR, 8} },
+ { OutputAttributes::TEXCOORD0_U, { OutputRegisterInfo::TEXCOORD0, 1} },
+ { OutputAttributes::TEXCOORD0_V, { OutputRegisterInfo::TEXCOORD0, 2} },
+ { OutputAttributes::TEXCOORD1_U, { OutputRegisterInfo::TEXCOORD1, 1} },
+ { OutputAttributes::TEXCOORD1_V, { OutputRegisterInfo::TEXCOORD1, 2} },
+ { OutputAttributes::TEXCOORD2_U, { OutputRegisterInfo::TEXCOORD2, 1} },
+ { OutputAttributes::TEXCOORD2_V, { OutputRegisterInfo::TEXCOORD2, 2} },
+ { OutputAttributes::VIEW_X, { OutputRegisterInfo::VIEW, 1} },
+ { OutputAttributes::VIEW_Y, { OutputRegisterInfo::VIEW, 2} },
+ { OutputAttributes::VIEW_Z, { OutputRegisterInfo::VIEW, 4} }
+ };
+
+ for (const auto& semantic : std::vector<OutputAttributes::Semantic>{
+ output_attributes[i].map_x,
+ output_attributes[i].map_y,
+ output_attributes[i].map_z,
+ output_attributes[i].map_w }) {
+ if (semantic == OutputAttributes::INVALID)
+ continue;
+
+ try {
+ OutputRegisterInfo::Type type = map.at(semantic).first;
+ u32 component_mask = map.at(semantic).second;
+
+ auto it = std::find_if(output_info_table.begin(), output_info_table.end(),
+ [&i, &type](const OutputRegisterInfo& info) {
+ return info.id == i && info.type == type;
+ }
+ );
+
+ if (it == output_info_table.end()) {
+ output_info_table.emplace_back();
+ output_info_table.back().type = type;
+ output_info_table.back().component_mask = component_mask;
+ output_info_table.back().id = i;
+ } else {
+ it->component_mask = it->component_mask | component_mask;
+ }
+ } catch (const std::out_of_range& ) {
+ DEBUG_ASSERT_MSG(false, "Unknown output attribute mapping");
+ LOG_ERROR(HW_GPU, "Unknown output attribute mapping: %03x, %03x, %03x, %03x",
+ (int)output_attributes[i].map_x.Value(),
+ (int)output_attributes[i].map_y.Value(),
+ (int)output_attributes[i].map_z.Value(),
+ (int)output_attributes[i].map_w.Value());
+ }
+ }
+ }
+
+
+ struct {
+ DVLBHeader header;
+ u32 dvle_offset;
+ } dvlb{ {DVLBHeader::MAGIC_WORD, 1 } }; // 1 DVLE
+
+ DVLPHeader dvlp{ DVLPHeader::MAGIC_WORD };
+ DVLEHeader dvle{ DVLEHeader::MAGIC_WORD };
+
+ QueueForWriting((u8*)&dvlb, sizeof(dvlb));
+ u32 dvlp_offset = QueueForWriting((u8*)&dvlp, sizeof(dvlp));
+ dvlb.dvle_offset = QueueForWriting((u8*)&dvle, sizeof(dvle));
+
+ // TODO: Reduce the amount of binary code written to relevant portions
+ dvlp.binary_offset = write_offset - dvlp_offset;
+ dvlp.binary_size_words = setup.program_code.size();
+ QueueForWriting((u8*)setup.program_code.data(), setup.program_code.size() * sizeof(u32));
+
+ dvlp.swizzle_info_offset = write_offset - dvlp_offset;
+ dvlp.swizzle_info_num_entries = setup.swizzle_data.size();
+ u32 dummy = 0;
+ for (unsigned int i = 0; i < setup.swizzle_data.size(); ++i) {
+ QueueForWriting((u8*)&setup.swizzle_data[i], sizeof(setup.swizzle_data[i]));
+ QueueForWriting((u8*)&dummy, sizeof(dummy));
+ }
+
+ dvle.main_offset_words = config.main_offset;
+ dvle.output_register_table_offset = write_offset - dvlb.dvle_offset;
+ dvle.output_register_table_size = static_cast<u32>(output_info_table.size());
+ QueueForWriting((u8*)output_info_table.data(), static_cast<u32>(output_info_table.size() * sizeof(OutputRegisterInfo)));
+
+ // TODO: Create a label table for "main"
+
+ std::vector<nihstro::ConstantInfo> constant_table;
+ for (unsigned i = 0; i < setup.uniforms.b.size(); ++i) {
+ nihstro::ConstantInfo constant;
+ memset(&constant, 0, sizeof(constant));
+ constant.type = nihstro::ConstantInfo::Bool;
+ constant.regid = i;
+ constant.b = setup.uniforms.b[i];
+ constant_table.emplace_back(constant);
+ }
+ for (unsigned i = 0; i < setup.uniforms.i.size(); ++i) {
+ nihstro::ConstantInfo constant;
+ memset(&constant, 0, sizeof(constant));
+ constant.type = nihstro::ConstantInfo::Int;
+ constant.regid = i;
+ constant.i.x = setup.uniforms.i[i].x;
+ constant.i.y = setup.uniforms.i[i].y;
+ constant.i.z = setup.uniforms.i[i].z;
+ constant.i.w = setup.uniforms.i[i].w;
+ constant_table.emplace_back(constant);
+ }
+ for (unsigned i = 0; i < sizeof(setup.uniforms.f) / sizeof(setup.uniforms.f[0]); ++i) {
+ nihstro::ConstantInfo constant;
+ memset(&constant, 0, sizeof(constant));
+ constant.type = nihstro::ConstantInfo::Float;
+ constant.regid = i;
+ constant.f.x = nihstro::to_float24(setup.uniforms.f[i].x.ToFloat32());
+ constant.f.y = nihstro::to_float24(setup.uniforms.f[i].y.ToFloat32());
+ constant.f.z = nihstro::to_float24(setup.uniforms.f[i].z.ToFloat32());
+ constant.f.w = nihstro::to_float24(setup.uniforms.f[i].w.ToFloat32());
+
+ // Store constant if it's different from zero..
+ if (setup.uniforms.f[i].x.ToFloat32() != 0.0 ||
+ setup.uniforms.f[i].y.ToFloat32() != 0.0 ||
+ setup.uniforms.f[i].z.ToFloat32() != 0.0 ||
+ setup.uniforms.f[i].w.ToFloat32() != 0.0)
+ constant_table.emplace_back(constant);
+ }
+ dvle.constant_table_offset = write_offset - dvlb.dvle_offset;
+ dvle.constant_table_size = constant_table.size();
+ for (const auto& constant : constant_table) {
+ QueueForWriting((uint8_t*)&constant, sizeof(constant));
+ }
+
+ // Write data to file
+ std::ofstream file(filename, std::ios_base::out | std::ios_base::binary);
+
+ for (auto& chunk : writing_queue) {
+ file.write((char*)chunk.pointer, chunk.size);
+ }
+}
+
+static std::unique_ptr<PicaTrace> pica_trace;
+static std::mutex pica_trace_mutex;
+static int is_pica_tracing = false;
+
+void StartPicaTracing()
+{
+ if (is_pica_tracing) {
+ LOG_WARNING(HW_GPU, "StartPicaTracing called even though tracing already running!");
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(pica_trace_mutex);
+ pica_trace = std::unique_ptr<PicaTrace>(new PicaTrace);
+
+ is_pica_tracing = true;
+}
+
+bool IsPicaTracing()
+{
+ return is_pica_tracing != 0;
+}
+
+void OnPicaRegWrite(PicaTrace::Write write)
+{
+ // Double check for is_pica_tracing to avoid pointless locking overhead
+ if (!is_pica_tracing)
+ return;
+
+ std::lock_guard<std::mutex> lock(pica_trace_mutex);
+
+ if (!is_pica_tracing)
+ return;
+
+ pica_trace->writes.push_back(write);
+}
+
+std::unique_ptr<PicaTrace> FinishPicaTracing()
+{
+ if (!is_pica_tracing) {
+ LOG_WARNING(HW_GPU, "FinishPicaTracing called even though tracing isn't running!");
+ return {};
+ }
+
+ // signalize that no further tracing should be performed
+ is_pica_tracing = false;
+
+ // Wait until running tracing is finished
+ std::lock_guard<std::mutex> lock(pica_trace_mutex);
+ std::unique_ptr<PicaTrace> ret(std::move(pica_trace));
+
+ return std::move(ret);
+}
+
+const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info, bool disable_alpha) {
+ const unsigned int coarse_x = x & ~7;
+ const unsigned int coarse_y = y & ~7;
+
+ if (info.format != Regs::TextureFormat::ETC1 &&
+ info.format != Regs::TextureFormat::ETC1A4) {
+ // TODO(neobrain): Fix code design to unify vertical block offsets!
+ source += coarse_y * info.stride;
+ }
+
+ // TODO: Assert that width/height are multiples of block dimensions
+
+ switch (info.format) {
+ case Regs::TextureFormat::RGBA8:
+ {
+ auto res = Color::DecodeRGBA8(source + VideoCore::GetMortonOffset(x, y, 4));
+ return { res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a()) };
+ }
+
+ case Regs::TextureFormat::RGB8:
+ {
+ auto res = Color::DecodeRGB8(source + VideoCore::GetMortonOffset(x, y, 3));
+ return { res.r(), res.g(), res.b(), 255 };
+ }
+
+ case Regs::TextureFormat::RGB5A1:
+ {
+ auto res = Color::DecodeRGB5A1(source + VideoCore::GetMortonOffset(x, y, 2));
+ return { res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a()) };
+ }
+
+ case Regs::TextureFormat::RGB565:
+ {
+ auto res = Color::DecodeRGB565(source + VideoCore::GetMortonOffset(x, y, 2));
+ return { res.r(), res.g(), res.b(), 255 };
+ }
+
+ case Regs::TextureFormat::RGBA4:
+ {
+ auto res = Color::DecodeRGBA4(source + VideoCore::GetMortonOffset(x, y, 2));
+ return { res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a()) };
+ }
+
+ case Regs::TextureFormat::IA8:
+ {
+ const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 2);
+
+ if (disable_alpha) {
+ // Show intensity as red, alpha as green
+ return { source_ptr[1], source_ptr[0], 0, 255 };
+ } else {
+ return { source_ptr[1], source_ptr[1], source_ptr[1], source_ptr[0] };
+ }
+ }
+
+ case Regs::TextureFormat::RG8:
+ {
+ auto res = Color::DecodeRG8(source + VideoCore::GetMortonOffset(x, y, 2));
+ return { res.r(), res.g(), 0, 255 };
+ }
+
+ case Regs::TextureFormat::I8:
+ {
+ const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1);
+ return { *source_ptr, *source_ptr, *source_ptr, 255 };
+ }
+
+ case Regs::TextureFormat::A8:
+ {
+ const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1);
+
+ if (disable_alpha) {
+ return { *source_ptr, *source_ptr, *source_ptr, 255 };
+ } else {
+ return { 0, 0, 0, *source_ptr };
+ }
+ }
+
+ case Regs::TextureFormat::IA4:
+ {
+ const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1);
+
+ u8 i = Color::Convert4To8(((*source_ptr) & 0xF0) >> 4);
+ u8 a = Color::Convert4To8((*source_ptr) & 0xF);
+
+ if (disable_alpha) {
+ // Show intensity as red, alpha as green
+ return { i, a, 0, 255 };
+ } else {
+ return { i, i, i, a };
+ }
+ }
+
+ case Regs::TextureFormat::I4:
+ {
+ u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1);
+ const u8* source_ptr = source + morton_offset / 2;
+
+ u8 i = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF);
+ i = Color::Convert4To8(i);
+
+ return { i, i, i, 255 };
+ }
+
+ case Regs::TextureFormat::A4:
+ {
+ u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1);
+ const u8* source_ptr = source + morton_offset / 2;
+
+ u8 a = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF);
+ a = Color::Convert4To8(a);
+
+ if (disable_alpha) {
+ return { a, a, a, 255 };
+ } else {
+ return { 0, 0, 0, a };
+ }
+ }
+
+ case Regs::TextureFormat::ETC1:
+ case Regs::TextureFormat::ETC1A4:
+ {
+ bool has_alpha = (info.format == Regs::TextureFormat::ETC1A4);
+
+ // ETC1 further subdivides each 8x8 tile into four 4x4 subtiles
+ const int subtile_width = 4;
+ const int subtile_height = 4;
+
+ int subtile_index = ((x / subtile_width) & 1) + 2 * ((y / subtile_height) & 1);
+ unsigned subtile_bytes = has_alpha ? 2 : 1; // TODO: Name...
+
+ const u64* source_ptr = (const u64*)(source
+ + coarse_x * subtile_bytes * 4
+ + coarse_y * subtile_bytes * 4 * (info.width / 8)
+ + subtile_index * subtile_bytes * 8);
+ u64 alpha = 0xFFFFFFFFFFFFFFFF;
+ if (has_alpha) {
+ alpha = *source_ptr;
+ source_ptr++;
+ }
+
+ union ETC1Tile {
+ // Each of these two is a collection of 16 bits (one per lookup value)
+ BitField< 0, 16, u64> table_subindexes;
+ BitField<16, 16, u64> negation_flags;
+
+ unsigned GetTableSubIndex(unsigned index) const {
+ return (table_subindexes >> index) & 1;
+ }
+
+ bool GetNegationFlag(unsigned index) const {
+ return ((negation_flags >> index) & 1) == 1;
+ }
+
+ BitField<32, 1, u64> flip;
+ BitField<33, 1, u64> differential_mode;
+
+ BitField<34, 3, u64> table_index_2;
+ BitField<37, 3, u64> table_index_1;
+
+ union {
+ // delta value + base value
+ BitField<40, 3, s64> db;
+ BitField<43, 5, u64> b;
+
+ BitField<48, 3, s64> dg;
+ BitField<51, 5, u64> g;
+
+ BitField<56, 3, s64> dr;
+ BitField<59, 5, u64> r;
+ } differential;
+
+ union {
+ BitField<40, 4, u64> b2;
+ BitField<44, 4, u64> b1;
+
+ BitField<48, 4, u64> g2;
+ BitField<52, 4, u64> g1;
+
+ BitField<56, 4, u64> r2;
+ BitField<60, 4, u64> r1;
+ } separate;
+
+ const Math::Vec3<u8> GetRGB(int x, int y) const {
+ int texel = 4 * x + y;
+
+ if (flip)
+ std::swap(x, y);
+
+ // Lookup base value
+ Math::Vec3<int> ret;
+ if (differential_mode) {
+ ret.r() = static_cast<int>(differential.r);
+ ret.g() = static_cast<int>(differential.g);
+ ret.b() = static_cast<int>(differential.b);
+ if (x >= 2) {
+ ret.r() += static_cast<int>(differential.dr);
+ ret.g() += static_cast<int>(differential.dg);
+ ret.b() += static_cast<int>(differential.db);
+ }
+ ret.r() = Color::Convert5To8(ret.r());
+ ret.g() = Color::Convert5To8(ret.g());
+ ret.b() = Color::Convert5To8(ret.b());
+ } else {
+ if (x < 2) {
+ ret.r() = Color::Convert4To8(static_cast<u8>(separate.r1));
+ ret.g() = Color::Convert4To8(static_cast<u8>(separate.g1));
+ ret.b() = Color::Convert4To8(static_cast<u8>(separate.b1));
+ } else {
+ ret.r() = Color::Convert4To8(static_cast<u8>(separate.r2));
+ ret.g() = Color::Convert4To8(static_cast<u8>(separate.g2));
+ ret.b() = Color::Convert4To8(static_cast<u8>(separate.b2));
+ }
+ }
+
+ // Add modifier
+ unsigned table_index = static_cast<int>((x < 2) ? table_index_1.Value() : table_index_2.Value());
+
+ static const std::array<std::array<u8, 2>, 8> etc1_modifier_table = {{
+ {{ 2, 8 }}, {{ 5, 17 }}, {{ 9, 29 }}, {{ 13, 42 }},
+ {{ 18, 60 }}, {{ 24, 80 }}, {{ 33, 106 }}, {{ 47, 183 }}
+ }};
+
+ int modifier = etc1_modifier_table.at(table_index).at(GetTableSubIndex(texel));
+ if (GetNegationFlag(texel))
+ modifier *= -1;
+
+ ret.r() = MathUtil::Clamp(ret.r() + modifier, 0, 255);
+ ret.g() = MathUtil::Clamp(ret.g() + modifier, 0, 255);
+ ret.b() = MathUtil::Clamp(ret.b() + modifier, 0, 255);
+
+ return ret.Cast<u8>();
+ }
+ } const *etc1_tile = reinterpret_cast<const ETC1Tile*>(source_ptr);
+
+ alpha >>= 4 * ((x & 3) * 4 + (y & 3));
+ return Math::MakeVec(etc1_tile->GetRGB(x & 3, y & 3),
+ disable_alpha ? (u8)255 : Color::Convert4To8(alpha & 0xF));
+ }
+
+ default:
+ //LOG_ERROR(HW_GPU, "Unknown texture format: %x", (u32)info.format);
+ //DEBUG_ASSERT(false);
+ return {};
+ }
+}
+
+TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config,
+ const Regs::TextureFormat& format)
+{
+ TextureInfo info;
+ info.physical_address = config.GetPhysicalAddress();
+ info.width = config.width;
+ info.height = config.height;
+ info.format = format;
+ info.stride = Pica::Regs::NibblesPerPixel(info.format) * info.width / 2;
+ return info;
+}
+
+void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
+#ifndef HAVE_PNG
+ return;
+#else
+ if (!data)
+ return;
+
+ // Write data to file
+ static int dump_index = 0;
+ std::string filename = std::string("texture_dump") + std::to_string(++dump_index) + std::string(".png");
+ u32 row_stride = texture_config.width * 3;
+
+ u8* buf;
+
+ char title[] = "Citra texture dump";
+ char title_key[] = "Title";
+ png_structp png_ptr = nullptr;
+ png_infop info_ptr = nullptr;
+
+ // Open file for writing (binary mode)
+ FileUtil::IOFile fp(filename, "wb");
+
+ // Initialize write structure
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ if (png_ptr == nullptr) {
+ LOG_ERROR(Debug_GPU, "Could not allocate write struct\n");
+ goto finalise;
+
+ }
+
+ // Initialize info structure
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == nullptr) {
+ LOG_ERROR(Debug_GPU, "Could not allocate info struct\n");
+ goto finalise;
+ }
+
+ // Setup Exception handling
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ LOG_ERROR(Debug_GPU, "Error during png creation\n");
+ goto finalise;
+ }
+
+ png_init_io(png_ptr, fp.GetHandle());
+
+ // Write header (8 bit color depth)
+ png_set_IHDR(png_ptr, info_ptr, texture_config.width, texture_config.height,
+ 8, PNG_COLOR_TYPE_RGB /*_ALPHA*/, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ png_text title_text;
+ title_text.compression = PNG_TEXT_COMPRESSION_NONE;
+ title_text.key = title_key;
+ title_text.text = title;
+ png_set_text(png_ptr, info_ptr, &title_text, 1);
+
+ png_write_info(png_ptr, info_ptr);
+
+ buf = new u8[row_stride * texture_config.height];
+ for (unsigned y = 0; y < texture_config.height; ++y) {
+ for (unsigned x = 0; x < texture_config.width; ++x) {
+ TextureInfo info;
+ info.width = texture_config.width;
+ info.height = texture_config.height;
+ info.stride = row_stride;
+ info.format = g_state.regs.texture0_format;
+ Math::Vec4<u8> texture_color = LookupTexture(data, x, y, info);
+ buf[3 * x + y * row_stride ] = texture_color.r();
+ buf[3 * x + y * row_stride + 1] = texture_color.g();
+ buf[3 * x + y * row_stride + 2] = texture_color.b();
+ }
+ }
+
+ // Write image data
+ for (unsigned y = 0; y < texture_config.height; ++y)
+ {
+ u8* row_ptr = (u8*)buf + y * row_stride;
+ png_write_row(png_ptr, row_ptr);
+ }
+
+ delete[] buf;
+
+ // End write
+ png_write_end(png_ptr, nullptr);
+
+finalise:
+ if (info_ptr != nullptr) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
+ if (png_ptr != nullptr) png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
+#endif
+}
+
+void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages)
+{
+ using Source = Pica::Regs::TevStageConfig::Source;
+ using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier;
+ using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier;
+ using Operation = Pica::Regs::TevStageConfig::Operation;
+
+ std::string stage_info = "Tev setup:\n";
+ for (size_t index = 0; index < stages.size(); ++index) {
+ const auto& tev_stage = stages[index];
+
+ static const std::map<Source, std::string> source_map = {
+ { Source::PrimaryColor, "PrimaryColor" },
+ { Source::Texture0, "Texture0" },
+ { Source::Texture1, "Texture1" },
+ { Source::Texture2, "Texture2" },
+ { Source::Constant, "Constant" },
+ { Source::Previous, "Previous" },
+ };
+
+ static const std::map<ColorModifier, std::string> color_modifier_map = {
+ { ColorModifier::SourceColor, { "%source.rgb" } },
+ { ColorModifier::SourceAlpha, { "%source.aaa" } },
+ };
+ static const std::map<AlphaModifier, std::string> alpha_modifier_map = {
+ { AlphaModifier::SourceAlpha, "%source.a" },
+ { AlphaModifier::OneMinusSourceAlpha, "(255 - %source.a)" },
+ };
+
+ static const std::map<Operation, std::string> combiner_map = {
+ { Operation::Replace, "%source1" },
+ { Operation::Modulate, "(%source1 * %source2) / 255" },
+ { Operation::Add, "(%source1 + %source2)" },
+ { Operation::Lerp, "lerp(%source1, %source2, %source3)" },
+ };
+
+ static auto ReplacePattern =
+ [](const std::string& input, const std::string& pattern, const std::string& replacement) -> std::string {
+ size_t start = input.find(pattern);
+ if (start == std::string::npos)
+ return input;
+
+ std::string ret = input;
+ ret.replace(start, pattern.length(), replacement);
+ return ret;
+ };
+ static auto GetColorSourceStr =
+ [](const Source& src, const ColorModifier& modifier) {
+ auto src_it = source_map.find(src);
+ std::string src_str = "Unknown";
+ if (src_it != source_map.end())
+ src_str = src_it->second;
+
+ auto modifier_it = color_modifier_map.find(modifier);
+ std::string modifier_str = "%source.????";
+ if (modifier_it != color_modifier_map.end())
+ modifier_str = modifier_it->second;
+
+ return ReplacePattern(modifier_str, "%source", src_str);
+ };
+ static auto GetColorCombinerStr =
+ [](const Regs::TevStageConfig& tev_stage) {
+ auto op_it = combiner_map.find(tev_stage.color_op);
+ std::string op_str = "Unknown op (%source1, %source2, %source3)";
+ if (op_it != combiner_map.end())
+ op_str = op_it->second;
+
+ op_str = ReplacePattern(op_str, "%source1", GetColorSourceStr(tev_stage.color_source1, tev_stage.color_modifier1));
+ op_str = ReplacePattern(op_str, "%source2", GetColorSourceStr(tev_stage.color_source2, tev_stage.color_modifier2));
+ return ReplacePattern(op_str, "%source3", GetColorSourceStr(tev_stage.color_source3, tev_stage.color_modifier3));
+ };
+ static auto GetAlphaSourceStr =
+ [](const Source& src, const AlphaModifier& modifier) {
+ auto src_it = source_map.find(src);
+ std::string src_str = "Unknown";
+ if (src_it != source_map.end())
+ src_str = src_it->second;
+
+ auto modifier_it = alpha_modifier_map.find(modifier);
+ std::string modifier_str = "%source.????";
+ if (modifier_it != alpha_modifier_map.end())
+ modifier_str = modifier_it->second;
+
+ return ReplacePattern(modifier_str, "%source", src_str);
+ };
+ static auto GetAlphaCombinerStr =
+ [](const Regs::TevStageConfig& tev_stage) {
+ auto op_it = combiner_map.find(tev_stage.alpha_op);
+ std::string op_str = "Unknown op (%source1, %source2, %source3)";
+ if (op_it != combiner_map.end())
+ op_str = op_it->second;
+
+ op_str = ReplacePattern(op_str, "%source1", GetAlphaSourceStr(tev_stage.alpha_source1, tev_stage.alpha_modifier1));
+ op_str = ReplacePattern(op_str, "%source2", GetAlphaSourceStr(tev_stage.alpha_source2, tev_stage.alpha_modifier2));
+ return ReplacePattern(op_str, "%source3", GetAlphaSourceStr(tev_stage.alpha_source3, tev_stage.alpha_modifier3));
+ };
+
+ stage_info += "Stage " + std::to_string(index) + ": " + GetColorCombinerStr(tev_stage) + " " + GetAlphaCombinerStr(tev_stage) + "\n";
+ }
+
+ LOG_TRACE(HW_GPU, "%s", stage_info.c_str());
+}
+
+} // namespace
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <condition_variable>
+#include <list>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include "citraimport\common/vector_math.h"
+
+#include "citraimport\GPU\video_core/pica.h"
+
+namespace Pica {
+
+class DebugContext {
+public:
+ enum class Event {
+ FirstEvent = 0,
+
+ PicaCommandLoaded = FirstEvent,
+ PicaCommandProcessed,
+ IncomingPrimitiveBatch,
+ FinishedPrimitiveBatch,
+ VertexLoaded,
+ IncomingDisplayTransfer,
+ GSPCommandProcessed,
+ BufferSwapped,
+
+ NumEvents
+ };
+
+ /**
+ * Inherit from this class to be notified of events registered to some debug context.
+ * Most importantly this is used for our debugger GUI.
+ *
+ * To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods.
+ * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state access
+ * @todo Evaluate an alternative interface, in which there is only one managing observer and multiple child observers running (by design) on the same thread.
+ */
+ class BreakPointObserver {
+ public:
+ /// Constructs the object such that it observes events of the given DebugContext.
+ BreakPointObserver(std::shared_ptr<DebugContext> debug_context) : context_weak(debug_context) {
+ std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
+ debug_context->breakpoint_observers.push_back(this);
+ }
+
+ virtual ~BreakPointObserver() {
+ auto context = context_weak.lock();
+ if (context) {
+ std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
+ context->breakpoint_observers.remove(this);
+
+ // If we are the last observer to be destroyed, tell the debugger context that
+ // it is free to continue. In particular, this is required for a proper Citra
+ // shutdown, when the emulation thread is waiting at a breakpoint.
+ if (context->breakpoint_observers.empty())
+ context->Resume();
+ }
+ }
+
+ /**
+ * Action to perform when a breakpoint was reached.
+ * @param event Type of event which triggered the breakpoint
+ * @param data Optional data pointer (if unused, this is a nullptr)
+ * @note This function will perform nothing unless it is overridden in the child class.
+ */
+ virtual void OnPicaBreakPointHit(Event, void*) {
+ }
+
+ /**
+ * Action to perform when emulation is resumed from a breakpoint.
+ * @note This function will perform nothing unless it is overridden in the child class.
+ */
+ virtual void OnPicaResume() {
+ }
+
+ protected:
+ /**
+ * Weak context pointer. This need not be valid, so when requesting a shared_ptr via
+ * context_weak.lock(), always compare the result against nullptr.
+ */
+ std::weak_ptr<DebugContext> context_weak;
+ };
+
+ /**
+ * Simple structure defining a breakpoint state
+ */
+ struct BreakPoint {
+ bool enabled = false;
+ };
+
+ /**
+ * Static constructor used to create a shared_ptr of a DebugContext.
+ */
+ static std::shared_ptr<DebugContext> Construct() {
+ return std::shared_ptr<DebugContext>(new DebugContext);
+ }
+
+ /**
+ * Used by the emulation core when a given event has happened. If a breakpoint has been set
+ * for this event, OnEvent calls the event handlers of the registered breakpoint observers.
+ * The current thread then is halted until Resume() is called from another thread (or until
+ * emulation is stopped).
+ * @param event Event which has happened
+ * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until Resume() is called.
+ */
+ void OnEvent(Event event, void* data);
+
+ /**
+ * Resume from the current breakpoint.
+ * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock. Calling from any other thread is safe.
+ */
+ void Resume();
+
+ /**
+ * Delete all set breakpoints and resume emulation.
+ */
+ void ClearBreakpoints() {
+ breakpoints.clear();
+ Resume();
+ }
+
+ // TODO: Evaluate if access to these members should be hidden behind a public interface.
+ std::map<Event, BreakPoint> breakpoints;
+ Event active_breakpoint;
+ bool at_breakpoint = false;
+
+ //std::shared_ptr<CiTrace::Recorder> recorder = nullptr;
+
+private:
+ /**
+ * Private default constructor to make sure people always construct this through Construct()
+ * instead.
+ */
+ DebugContext() = default;
+
+ /// Mutex protecting current breakpoint state and the observer list.
+ std::mutex breakpoint_mutex;
+
+ /// Used by OnEvent to wait for resumption.
+ std::condition_variable resume_from_breakpoint;
+
+ /// List of registered observers
+ std::list<BreakPointObserver*> breakpoint_observers;
+};
+
+extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
+
+namespace DebugUtils {
+
+#define PICA_DUMP_GEOMETRY 0
+#define PICA_DUMP_TEXTURES 0
+#define PICA_LOG_TEV 0
+
+// Simple utility class for dumping geometry data to an OBJ file
+class GeometryDumper {
+public:
+ struct Vertex {
+ std::array<float,3> pos;
+ };
+
+ void AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2);
+
+ void Dump();
+
+private:
+ struct Face {
+ int index[3];
+ };
+
+ std::vector<Vertex> vertices;
+ std::vector<Face> faces;
+};
+
+void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
+ const State::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes);
+
+
+// Utility class to log Pica commands.
+struct PicaTrace {
+ struct Write {
+ u16 cmd_id;
+ u16 mask;
+ u32 value;
+ };
+ std::vector<Write> writes;
+};
+
+void StartPicaTracing();
+bool IsPicaTracing();
+void OnPicaRegWrite(PicaTrace::Write write);
+std::unique_ptr<PicaTrace> FinishPicaTracing();
+
+struct TextureInfo {
+ PAddr physical_address;
+ int width;
+ int height;
+ int stride;
+ Pica::Regs::TextureFormat format;
+
+ static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config,
+ const Pica::Regs::TextureFormat& format);
+};
+
+/**
+ * Lookup texel located at the given coordinates and return an RGBA vector of its color.
+ * @param source Source pointer to read data from
+ * @param s,t Texture coordinates to read from
+ * @param info TextureInfo object describing the texture setup
+ * @param disable_alpha This is used for debug widgets which use this method to display textures without providing a good way to visualize alpha by themselves. If true, this will return 255 for the alpha component, and either drop the information entirely or store it in an "unused" color channel.
+ * @todo Eventually we should get rid of the disable_alpha parameter.
+ */
+const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const TextureInfo& info,
+ bool disable_alpha = false);
+
+void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data);
+
+void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages);
+
+} // namespace
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+
+#include "core/hle/service/gsp_gpu.h"
+
+class GraphicsDebugger
+{
+public:
+ // Base class for all objects which need to be notified about GPU events
+ class DebuggerObserver
+ {
+ public:
+ DebuggerObserver() : observed(nullptr) { }
+
+ virtual ~DebuggerObserver()
+ {
+ if (observed)
+ observed->UnregisterObserver(this);
+ }
+
+ /**
+ * Called when a GX command has been processed and is ready for being
+ * read via GraphicsDebugger::ReadGXCommandHistory.
+ * @param total_command_count Total number of commands in the GX history
+ * @note All methods in this class are called from the GSP thread
+ */
+ virtual void GXCommandProcessed(int total_command_count)
+ {
+ const GSP_GPU::Command& cmd = observed->ReadGXCommandHistory(total_command_count-1);
+ LOG_TRACE(Debug_GPU, "Received command: id=%x", (int)cmd.id.Value());
+ }
+
+ protected:
+ const GraphicsDebugger* GetDebugger() const
+ {
+ return observed;
+ }
+
+ private:
+ GraphicsDebugger* observed;
+
+ friend class GraphicsDebugger;
+ };
+
+ void GXCommandProcessed(u8* command_data)
+ {
+ if (observers.empty())
+ return;
+
+ gx_command_history.emplace_back();
+ GSP_GPU::Command& cmd = gx_command_history.back();
+
+ memcpy(&cmd, command_data, sizeof(GSP_GPU::Command));
+
+ ForEachObserver([this](DebuggerObserver* observer) {
+ observer->GXCommandProcessed(static_cast<int>(this->gx_command_history.size()));
+ } );
+ }
+
+ const GSP_GPU::Command& ReadGXCommandHistory(int index) const
+ {
+ // TODO: Is this thread-safe?
+ return gx_command_history[index];
+ }
+
+ void RegisterObserver(DebuggerObserver* observer)
+ {
+ // TODO: Check for duplicates
+ observers.push_back(observer);
+ observer->observed = this;
+ }
+
+ void UnregisterObserver(DebuggerObserver* observer)
+ {
+ observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
+ observer->observed = nullptr;
+ }
+
+private:
+ void ForEachObserver(std::function<void (DebuggerObserver*)> func)
+ {
+ std::for_each(observers.begin(),observers.end(), func);
+ }
+
+ std::vector<DebuggerObserver*> observers;
+
+ std::vector<GSP_GPU::Command> gx_command_history;
+};
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "citraimport\common/common_types.h"
+
+namespace Pica {
+namespace Shader {
+struct OutputVertex;
+}
+}
+
+class HWRasterizer {
+public:
+ virtual ~HWRasterizer() {
+ }
+
+ /// Initialize API-specific GPU objects
+ virtual void InitObjects() = 0;
+
+ /// Reset the rasterizer, such as flushing all caches and updating all state
+ virtual void Reset() = 0;
+
+ /// Queues the primitive formed by the given vertices for rendering
+ virtual void AddTriangle(const Pica::Shader::OutputVertex& v0,
+ const Pica::Shader::OutputVertex& v1,
+ const Pica::Shader::OutputVertex& v2) = 0;
+
+ /// Draw the current batch of triangles
+ virtual void DrawTriangles() = 0;
+
+ /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer
+ virtual void CommitFramebuffer() = 0;
+
+ /// Notify rasterizer that the specified PICA register has been changed
+ virtual void NotifyPicaRegisterChanged(u32 id) = 0;
+
+ /// Notify rasterizer that the specified 3DS memory region will be read from after this notification
+ virtual void NotifyPreRead(PAddr addr, u32 size) = 0;
+
+ /// Notify rasterizer that a 3DS memory region has been changed
+ virtual void NotifyFlush(PAddr addr, u32 size) = 0;
+};
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include <unordered_map>
+
+#include "citraimport\GPU\video_core/pica.h"
+#include "citraimport\GPU\video_core/shader/shader.h"
+
+namespace Pica {
+
+State g_state;
+
+std::string Regs::GetCommandName(int index) {
+ static std::unordered_map<u32, std::string> map;
+
+ if (map.empty()) {
+ #define ADD_FIELD(name) \
+ map.insert({static_cast<u32>(PICA_REG_INDEX(name)), #name}); \
+ /* TODO: change to Regs::name when VS2015 and other compilers support it */ \
+ for (u32 i = PICA_REG_INDEX(name) + 1; i < PICA_REG_INDEX(name) + sizeof(Regs().name) / 4; ++i) \
+ map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \
+
+ ADD_FIELD(trigger_irq);
+ ADD_FIELD(cull_mode);
+ ADD_FIELD(viewport_size_x);
+ ADD_FIELD(viewport_size_y);
+ ADD_FIELD(viewport_depth_range);
+ ADD_FIELD(viewport_depth_far_plane);
+ ADD_FIELD(viewport_corner);
+ ADD_FIELD(texture0_enable);
+ ADD_FIELD(texture0);
+ ADD_FIELD(texture0_format);
+ ADD_FIELD(texture1);
+ ADD_FIELD(texture1_format);
+ ADD_FIELD(texture2);
+ ADD_FIELD(texture2_format);
+ ADD_FIELD(tev_stage0);
+ ADD_FIELD(tev_stage1);
+ ADD_FIELD(tev_stage2);
+ ADD_FIELD(tev_stage3);
+ ADD_FIELD(tev_combiner_buffer_input);
+ ADD_FIELD(tev_stage4);
+ ADD_FIELD(tev_stage5);
+ ADD_FIELD(tev_combiner_buffer_color);
+ ADD_FIELD(output_merger);
+ ADD_FIELD(framebuffer);
+ ADD_FIELD(vertex_attributes);
+ ADD_FIELD(index_array);
+ ADD_FIELD(num_vertices);
+ ADD_FIELD(vertex_offset);
+ ADD_FIELD(trigger_draw);
+ ADD_FIELD(trigger_draw_indexed);
+ ADD_FIELD(vs_default_attributes_setup);
+ ADD_FIELD(command_buffer);
+ ADD_FIELD(triangle_topology);
+ ADD_FIELD(restart_primitive);
+ ADD_FIELD(gs.bool_uniforms);
+ ADD_FIELD(gs.int_uniforms);
+ ADD_FIELD(gs.main_offset);
+ ADD_FIELD(gs.input_register_map);
+ ADD_FIELD(gs.uniform_setup);
+ ADD_FIELD(gs.program);
+ ADD_FIELD(gs.swizzle_patterns);
+ ADD_FIELD(vs.bool_uniforms);
+ ADD_FIELD(vs.int_uniforms);
+ ADD_FIELD(vs.main_offset);
+ ADD_FIELD(vs.input_register_map);
+ ADD_FIELD(vs.uniform_setup);
+ ADD_FIELD(vs.program);
+ ADD_FIELD(vs.swizzle_patterns);
+
+#undef ADD_FIELD
+ }
+
+ // Return empty string if no match is found
+ auto it = map.find(index);
+ if (it != map.end()) {
+ return it->second;
+ } else {
+ return std::string();
+ }
+}
+
+void Init() {
+}
+
+void Shutdown() {
+ Shader::Shutdown();
+
+ memset(&g_state, 0, sizeof(State));
+}
+
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <cmath>
+#include <cstddef>
+#include <string>
+
+#include "citraimport\common/assert.h"
+#include "citraimport\common/bit_field.h"
+#include "citraimport\common/common_funcs.h"
+#include "citraimport\common/common_types.h"
+#include "citraimport\common/vector_math.h"
+#include "citraimport\common/logging/log.h"
+
+namespace Pica {
+
+// Returns index corresponding to the Regs member labeled by field_name
+// TODO: Due to Visual studio bug 209229, offsetof does not return constant expressions
+// when used with array elements (e.g. PICA_REG_INDEX(vs_uniform_setup.set_value[1])).
+// For details cf. https://connect.microsoft.com/VisualStudio/feedback/details/209229/offsetof-does-not-produce-a-constant-expression-for-array-members
+// Hopefully, this will be fixed sometime in the future.
+// For lack of better alternatives, we currently hardcode the offsets when constant
+// expressions are needed via PICA_REG_INDEX_WORKAROUND (on sane compilers, static_asserts
+// will then make sure the offsets indeed match the automatically calculated ones).
+#define PICA_REG_INDEX(field_name) (offsetof(Pica::Regs, field_name) / sizeof(u32))
+#if defined(_MSC_VER)
+#define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) (backup_workaround_index)
+#else
+// NOTE: Yeah, hacking in a static_assert here just to workaround the lacking MSVC compiler
+// really is this annoying. This macro just forwards its first argument to PICA_REG_INDEX
+// and then performs a (no-op) cast to size_t iff the second argument matches the expected
+// field offset. Otherwise, the compiler will fail to compile this code.
+#define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \
+ ((typename std::enable_if<backup_workaround_index == PICA_REG_INDEX(field_name), size_t>::type)PICA_REG_INDEX(field_name))
+#endif // _MSC_VER
+
+struct Regs {
+
+ INSERT_PADDING_WORDS(0x10);
+
+ u32 trigger_irq;
+
+ INSERT_PADDING_WORDS(0x2f);
+
+ enum class CullMode : u32 {
+ // Select which polygons are considered to be "frontfacing".
+ KeepAll = 0,
+ KeepClockWise = 1,
+ KeepCounterClockWise = 2,
+ // TODO: What does the third value imply?
+ };
+
+ union {
+ BitField<0, 2, CullMode> cull_mode;
+ };
+
+ BitField<0, 24, u32> viewport_size_x;
+
+ INSERT_PADDING_WORDS(0x1);
+
+ BitField<0, 24, u32> viewport_size_y;
+
+ INSERT_PADDING_WORDS(0x9);
+
+ BitField<0, 24, u32> viewport_depth_range; // float24
+ BitField<0, 24, u32> viewport_depth_far_plane; // float24
+
+ INSERT_PADDING_WORDS(0x1);
+
+ union VSOutputAttributes {
+ // Maps components of output vertex attributes to semantics
+ enum Semantic : u32
+ {
+ POSITION_X = 0,
+ POSITION_Y = 1,
+ POSITION_Z = 2,
+ POSITION_W = 3,
+
+ QUATERNION_X = 4,
+ QUATERNION_Y = 5,
+ QUATERNION_Z = 6,
+ QUATERNION_W = 7,
+
+ COLOR_R = 8,
+ COLOR_G = 9,
+ COLOR_B = 10,
+ COLOR_A = 11,
+
+ TEXCOORD0_U = 12,
+ TEXCOORD0_V = 13,
+ TEXCOORD1_U = 14,
+ TEXCOORD1_V = 15,
+
+ // TODO: Not verified
+ VIEW_X = 18,
+ VIEW_Y = 19,
+ VIEW_Z = 20,
+
+ TEXCOORD2_U = 22,
+ TEXCOORD2_V = 23,
+
+ INVALID = 31,
+ };
+
+ BitField< 0, 5, Semantic> map_x;
+ BitField< 8, 5, Semantic> map_y;
+ BitField<16, 5, Semantic> map_z;
+ BitField<24, 5, Semantic> map_w;
+ } vs_output_attributes[7];
+
+ INSERT_PADDING_WORDS(0x11);
+
+ union {
+ BitField< 0, 16, u32> x;
+ BitField<16, 16, u32> y;
+ } viewport_corner;
+
+ INSERT_PADDING_WORDS(0x17);
+
+ struct TextureConfig {
+ enum WrapMode : u32 {
+ ClampToEdge = 0,
+ ClampToBorder = 1,
+ Repeat = 2,
+ MirroredRepeat = 3,
+ };
+
+ enum TextureFilter : u32 {
+ Nearest = 0,
+ Linear = 1
+ };
+
+ union {
+ u32 raw;
+ BitField< 0, 8, u32> r;
+ BitField< 8, 8, u32> g;
+ BitField<16, 8, u32> b;
+ BitField<24, 8, u32> a;
+ } border_color;
+
+ union {
+ BitField< 0, 16, u32> height;
+ BitField<16, 16, u32> width;
+ };
+
+ union {
+ BitField< 1, 1, TextureFilter> mag_filter;
+ BitField< 2, 1, TextureFilter> min_filter;
+ BitField< 8, 2, WrapMode> wrap_t;
+ BitField<12, 2, WrapMode> wrap_s;
+ };
+
+ INSERT_PADDING_WORDS(0x1);
+
+ u32 address;
+
+ u32 GetPhysicalAddress() const {
+ return DecodeAddressRegister(address);
+ }
+
+ // texture1 and texture2 store the texture format directly after the address
+ // whereas texture0 inserts some additional flags inbetween.
+ // Hence, we store the format separately so that all other parameters can be described
+ // in a single structure.
+ };
+
+ enum class TextureFormat : u32 {
+ RGBA8 = 0,
+ RGB8 = 1,
+ RGB5A1 = 2,
+ RGB565 = 3,
+ RGBA4 = 4,
+ IA8 = 5,
+ RG8 = 6, ///< @note Also called HILO8 in 3DBrew.
+ I8 = 7,
+ A8 = 8,
+ IA4 = 9,
+ I4 = 10,
+ A4 = 11,
+ ETC1 = 12, // compressed
+ ETC1A4 = 13, // compressed
+ };
+
+ enum class LogicOp : u32 {
+ Clear = 0,
+ And = 1,
+ AndReverse = 2,
+ Copy = 3,
+ Set = 4,
+ CopyInverted = 5,
+ NoOp = 6,
+ Invert = 7,
+ Nand = 8,
+ Or = 9,
+ Nor = 10,
+ Xor = 11,
+ Equiv = 12,
+ AndInverted = 13,
+ OrReverse = 14,
+ OrInverted = 15,
+ };
+
+ static unsigned NibblesPerPixel(TextureFormat format) {
+ switch (format) {
+ case TextureFormat::RGBA8:
+ return 8;
+
+ case TextureFormat::RGB8:
+ return 6;
+
+ case TextureFormat::RGB5A1:
+ case TextureFormat::RGB565:
+ case TextureFormat::RGBA4:
+ case TextureFormat::IA8:
+ case TextureFormat::RG8:
+ return 4;
+
+ case TextureFormat::I4:
+ case TextureFormat::A4:
+ return 1;
+
+ case TextureFormat::I8:
+ case TextureFormat::A8:
+ case TextureFormat::IA4:
+ default: // placeholder for yet unknown formats
+ return 2;
+ }
+ }
+
+ union {
+ BitField< 0, 1, u32> texture0_enable;
+ BitField< 1, 1, u32> texture1_enable;
+ BitField< 2, 1, u32> texture2_enable;
+ };
+ TextureConfig texture0;
+ INSERT_PADDING_WORDS(0x8);
+ BitField<0, 4, TextureFormat> texture0_format;
+ INSERT_PADDING_WORDS(0x2);
+ TextureConfig texture1;
+ BitField<0, 4, TextureFormat> texture1_format;
+ INSERT_PADDING_WORDS(0x2);
+ TextureConfig texture2;
+ BitField<0, 4, TextureFormat> texture2_format;
+ INSERT_PADDING_WORDS(0x21);
+
+ struct FullTextureConfig {
+ const bool enabled;
+ const TextureConfig config;
+ const TextureFormat format;
+ };
+ const std::array<FullTextureConfig, 3> GetTextures() const {
+ return {{
+ { texture0_enable.ToBool(), texture0, texture0_format },
+ { texture1_enable.ToBool(), texture1, texture1_format },
+ { texture2_enable.ToBool(), texture2, texture2_format }
+ }};
+ }
+
+ // 0xc0-0xff: Texture Combiner (akin to glTexEnv)
+ struct TevStageConfig {
+ enum class Source : u32 {
+ PrimaryColor = 0x0,
+ PrimaryFragmentColor = 0x1,
+ SecondaryFragmentColor = 0x2,
+
+ Texture0 = 0x3,
+ Texture1 = 0x4,
+ Texture2 = 0x5,
+ Texture3 = 0x6,
+
+ PreviousBuffer = 0xd,
+ Constant = 0xe,
+ Previous = 0xf,
+ };
+
+ enum class ColorModifier : u32 {
+ SourceColor = 0x0,
+ OneMinusSourceColor = 0x1,
+ SourceAlpha = 0x2,
+ OneMinusSourceAlpha = 0x3,
+ SourceRed = 0x4,
+ OneMinusSourceRed = 0x5,
+
+ SourceGreen = 0x8,
+ OneMinusSourceGreen = 0x9,
+
+ SourceBlue = 0xc,
+ OneMinusSourceBlue = 0xd,
+ };
+
+ enum class AlphaModifier : u32 {
+ SourceAlpha = 0x0,
+ OneMinusSourceAlpha = 0x1,
+ SourceRed = 0x2,
+ OneMinusSourceRed = 0x3,
+ SourceGreen = 0x4,
+ OneMinusSourceGreen = 0x5,
+ SourceBlue = 0x6,
+ OneMinusSourceBlue = 0x7,
+ };
+
+ enum class Operation : u32 {
+ Replace = 0,
+ Modulate = 1,
+ Add = 2,
+ AddSigned = 3,
+ Lerp = 4,
+ Subtract = 5,
+ Dot3_RGB = 6,
+
+ MultiplyThenAdd = 8,
+ AddThenMultiply = 9,
+ };
+
+ union {
+ BitField< 0, 4, Source> color_source1;
+ BitField< 4, 4, Source> color_source2;
+ BitField< 8, 4, Source> color_source3;
+ BitField<16, 4, Source> alpha_source1;
+ BitField<20, 4, Source> alpha_source2;
+ BitField<24, 4, Source> alpha_source3;
+ };
+
+ union {
+ BitField< 0, 4, ColorModifier> color_modifier1;
+ BitField< 4, 4, ColorModifier> color_modifier2;
+ BitField< 8, 4, ColorModifier> color_modifier3;
+ BitField<12, 3, AlphaModifier> alpha_modifier1;
+ BitField<16, 3, AlphaModifier> alpha_modifier2;
+ BitField<20, 3, AlphaModifier> alpha_modifier3;
+ };
+
+ union {
+ BitField< 0, 4, Operation> color_op;
+ BitField<16, 4, Operation> alpha_op;
+ };
+
+ union {
+ u32 const_color;
+ BitField< 0, 8, u32> const_r;
+ BitField< 8, 8, u32> const_g;
+ BitField<16, 8, u32> const_b;
+ BitField<24, 8, u32> const_a;
+ };
+
+ union {
+ BitField< 0, 2, u32> color_scale;
+ BitField<16, 2, u32> alpha_scale;
+ };
+
+ inline unsigned GetColorMultiplier() const {
+ return (color_scale < 3) ? (1 << color_scale) : 1;
+ }
+
+ inline unsigned GetAlphaMultiplier() const {
+ return (alpha_scale < 3) ? (1 << alpha_scale) : 1;
+ }
+ };
+
+ TevStageConfig tev_stage0;
+ INSERT_PADDING_WORDS(0x3);
+ TevStageConfig tev_stage1;
+ INSERT_PADDING_WORDS(0x3);
+ TevStageConfig tev_stage2;
+ INSERT_PADDING_WORDS(0x3);
+ TevStageConfig tev_stage3;
+ INSERT_PADDING_WORDS(0x3);
+
+ union {
+ // Tev stages 0-3 write their output to the combiner buffer if the corresponding bit in
+ // these masks are set
+ BitField< 8, 4, u32> update_mask_rgb;
+ BitField<12, 4, u32> update_mask_a;
+
+ bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
+ return (stage_index < 4) && (update_mask_rgb & (1 << stage_index));
+ }
+
+ bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
+ return (stage_index < 4) && (update_mask_a & (1 << stage_index));
+ }
+ } tev_combiner_buffer_input;
+
+ INSERT_PADDING_WORDS(0xf);
+ TevStageConfig tev_stage4;
+ INSERT_PADDING_WORDS(0x3);
+ TevStageConfig tev_stage5;
+
+ union {
+ u32 raw;
+ BitField< 0, 8, u32> r;
+ BitField< 8, 8, u32> g;
+ BitField<16, 8, u32> b;
+ BitField<24, 8, u32> a;
+ } tev_combiner_buffer_color;
+
+ INSERT_PADDING_WORDS(0x2);
+
+ const std::array<Regs::TevStageConfig,6> GetTevStages() const {
+ return {{ tev_stage0, tev_stage1,
+ tev_stage2, tev_stage3,
+ tev_stage4, tev_stage5 }};
+ };
+
+ enum class BlendEquation : u32 {
+ Add = 0,
+ Subtract = 1,
+ ReverseSubtract = 2,
+ Min = 3,
+ Max = 4,
+ };
+
+ enum class BlendFactor : u32 {
+ Zero = 0,
+ One = 1,
+ SourceColor = 2,
+ OneMinusSourceColor = 3,
+ DestColor = 4,
+ OneMinusDestColor = 5,
+ SourceAlpha = 6,
+ OneMinusSourceAlpha = 7,
+ DestAlpha = 8,
+ OneMinusDestAlpha = 9,
+ ConstantColor = 10,
+ OneMinusConstantColor = 11,
+ ConstantAlpha = 12,
+ OneMinusConstantAlpha = 13,
+ SourceAlphaSaturate = 14,
+ };
+
+ enum class CompareFunc : u32 {
+ Never = 0,
+ Always = 1,
+ Equal = 2,
+ NotEqual = 3,
+ LessThan = 4,
+ LessThanOrEqual = 5,
+ GreaterThan = 6,
+ GreaterThanOrEqual = 7,
+ };
+
+ enum class StencilAction : u32 {
+ Keep = 0,
+ Zero = 1,
+ Replace = 2,
+ Increment = 3,
+ Decrement = 4,
+ Invert = 5,
+ IncrementWrap = 6,
+ DecrementWrap = 7
+ };
+
+ struct {
+ union {
+ // If false, logic blending is used
+ BitField<8, 1, u32> alphablend_enable;
+ };
+
+ union {
+ BitField< 0, 8, BlendEquation> blend_equation_rgb;
+ BitField< 8, 8, BlendEquation> blend_equation_a;
+
+ BitField<16, 4, BlendFactor> factor_source_rgb;
+ BitField<20, 4, BlendFactor> factor_dest_rgb;
+
+ BitField<24, 4, BlendFactor> factor_source_a;
+ BitField<28, 4, BlendFactor> factor_dest_a;
+ } alpha_blending;
+
+ union {
+ BitField<0, 4, LogicOp> logic_op;
+ };
+
+ union {
+ u32 raw;
+ BitField< 0, 8, u32> r;
+ BitField< 8, 8, u32> g;
+ BitField<16, 8, u32> b;
+ BitField<24, 8, u32> a;
+ } blend_const;
+
+ union {
+ BitField< 0, 1, u32> enable;
+ BitField< 4, 3, CompareFunc> func;
+ BitField< 8, 8, u32> ref;
+ } alpha_test;
+
+ struct {
+ union {
+ // Raw value of this register
+ u32 raw_func;
+
+ // If true, enable stencil testing
+ BitField< 0, 1, u32> enable;
+
+ // Comparison operation for stencil testing
+ BitField< 4, 3, CompareFunc> func;
+
+ // Mask used to control writing to the stencil buffer
+ BitField< 8, 8, u32> write_mask;
+
+ // Value to compare against for stencil testing
+ BitField<16, 8, u32> reference_value;
+
+ // Mask to apply on stencil test inputs
+ BitField<24, 8, u32> input_mask;
+ };
+
+ union {
+ // Raw value of this register
+ u32 raw_op;
+
+ // Action to perform when the stencil test fails
+ BitField< 0, 3, StencilAction> action_stencil_fail;
+
+ // Action to perform when stencil testing passed but depth testing fails
+ BitField< 4, 3, StencilAction> action_depth_fail;
+
+ // Action to perform when both stencil and depth testing pass
+ BitField< 8, 3, StencilAction> action_depth_pass;
+ };
+ } stencil_test;
+
+ union {
+ BitField< 0, 1, u32> depth_test_enable;
+ BitField< 4, 3, CompareFunc> depth_test_func;
+ BitField< 8, 1, u32> red_enable;
+ BitField< 9, 1, u32> green_enable;
+ BitField<10, 1, u32> blue_enable;
+ BitField<11, 1, u32> alpha_enable;
+ BitField<12, 1, u32> depth_write_enable;
+ };
+
+ INSERT_PADDING_WORDS(0x8);
+ } output_merger;
+
+ // Components are laid out in reverse byte order, most significant bits first.
+ enum class ColorFormat : u32 {
+ RGBA8 = 0,
+ RGB8 = 1,
+ RGB5A1 = 2,
+ RGB565 = 3,
+ RGBA4 = 4,
+ };
+
+ enum class DepthFormat : u32 {
+ D16 = 0,
+ D24 = 2,
+ D24S8 = 3,
+ };
+
+ // Returns the number of bytes in the specified color format
+ static unsigned BytesPerColorPixel(ColorFormat format) {
+ switch (format) {
+ case ColorFormat::RGBA8:
+ return 4;
+ case ColorFormat::RGB8:
+ return 3;
+ case ColorFormat::RGB5A1:
+ case ColorFormat::RGB565:
+ case ColorFormat::RGBA4:
+ return 2;
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown color format %u", format);
+ UNIMPLEMENTED();
+ }
+ }
+
+ struct {
+ INSERT_PADDING_WORDS(0x6);
+
+ DepthFormat depth_format; // TODO: Should be a BitField!
+ BitField<16, 3, ColorFormat> color_format;
+
+ INSERT_PADDING_WORDS(0x4);
+
+ u32 depth_buffer_address;
+ u32 color_buffer_address;
+
+ union {
+ // Apparently, the framebuffer width is stored as expected,
+ // while the height is stored as the actual height minus one.
+ // Hence, don't access these fields directly but use the accessors
+ // GetWidth() and GetHeight() instead.
+ BitField< 0, 11, u32> width;
+ BitField<12, 10, u32> height;
+ };
+
+ INSERT_PADDING_WORDS(0x1);
+
+ inline u32 GetColorBufferPhysicalAddress() const {
+ return DecodeAddressRegister(color_buffer_address);
+ }
+ inline u32 GetDepthBufferPhysicalAddress() const {
+ return DecodeAddressRegister(depth_buffer_address);
+ }
+
+ inline u32 GetWidth() const {
+ return width;
+ }
+
+ inline u32 GetHeight() const {
+ return height + 1;
+ }
+ } framebuffer;
+
+ // Returns the number of bytes in the specified depth format
+ static u32 BytesPerDepthPixel(DepthFormat format) {
+ switch (format) {
+ case DepthFormat::D16:
+ return 2;
+ case DepthFormat::D24:
+ return 3;
+ case DepthFormat::D24S8:
+ return 4;
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown depth format %u", format);
+ UNIMPLEMENTED();
+ }
+ }
+
+ // Returns the number of bits per depth component of the specified depth format
+ static u32 DepthBitsPerPixel(DepthFormat format) {
+ switch (format) {
+ case DepthFormat::D16:
+ return 16;
+ case DepthFormat::D24:
+ case DepthFormat::D24S8:
+ return 24;
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown depth format %u", format);
+ UNIMPLEMENTED();
+ }
+ }
+
+ INSERT_PADDING_WORDS(0xe0);
+
+ enum class VertexAttributeFormat : u64 {
+ BYTE = 0,
+ UBYTE = 1,
+ SHORT = 2,
+ FLOAT = 3,
+ };
+
+ struct {
+ BitField<0, 29, u32> base_address;
+
+ u32 GetPhysicalBaseAddress() const {
+ return DecodeAddressRegister(base_address);
+ }
+
+ // Descriptor for internal vertex attributes
+ union {
+ BitField< 0, 2, VertexAttributeFormat> format0; // size of one element
+ BitField< 2, 2, u64> size0; // number of elements minus 1
+ BitField< 4, 2, VertexAttributeFormat> format1;
+ BitField< 6, 2, u64> size1;
+ BitField< 8, 2, VertexAttributeFormat> format2;
+ BitField<10, 2, u64> size2;
+ BitField<12, 2, VertexAttributeFormat> format3;
+ BitField<14, 2, u64> size3;
+ BitField<16, 2, VertexAttributeFormat> format4;
+ BitField<18, 2, u64> size4;
+ BitField<20, 2, VertexAttributeFormat> format5;
+ BitField<22, 2, u64> size5;
+ BitField<24, 2, VertexAttributeFormat> format6;
+ BitField<26, 2, u64> size6;
+ BitField<28, 2, VertexAttributeFormat> format7;
+ BitField<30, 2, u64> size7;
+ BitField<32, 2, VertexAttributeFormat> format8;
+ BitField<34, 2, u64> size8;
+ BitField<36, 2, VertexAttributeFormat> format9;
+ BitField<38, 2, u64> size9;
+ BitField<40, 2, VertexAttributeFormat> format10;
+ BitField<42, 2, u64> size10;
+ BitField<44, 2, VertexAttributeFormat> format11;
+ BitField<46, 2, u64> size11;
+
+ BitField<48, 12, u64> attribute_mask;
+
+ // number of total attributes minus 1
+ BitField<60, 4, u64> num_extra_attributes;
+ };
+
+ inline VertexAttributeFormat GetFormat(int n) const {
+ VertexAttributeFormat formats[] = {
+ format0, format1, format2, format3,
+ format4, format5, format6, format7,
+ format8, format9, format10, format11
+ };
+ return formats[n];
+ }
+
+ inline int GetNumElements(int n) const {
+ u64 sizes[] = {
+ size0, size1, size2, size3,
+ size4, size5, size6, size7,
+ size8, size9, size10, size11
+ };
+ return (int)sizes[n]+1;
+ }
+
+ inline int GetElementSizeInBytes(int n) const {
+ return (GetFormat(n) == VertexAttributeFormat::FLOAT) ? 4 :
+ (GetFormat(n) == VertexAttributeFormat::SHORT) ? 2 : 1;
+ }
+
+ inline int GetStride(int n) const {
+ return GetNumElements(n) * GetElementSizeInBytes(n);
+ }
+
+ inline bool IsDefaultAttribute(int id) const {
+ return (id >= 12) || (attribute_mask & (1ULL << id)) != 0;
+ }
+
+ inline int GetNumTotalAttributes() const {
+ return (int)num_extra_attributes+1;
+ }
+
+ // Attribute loaders map the source vertex data to input attributes
+ // This e.g. allows to load different attributes from different memory locations
+ struct {
+ // Source attribute data offset from the base address
+ u32 data_offset;
+
+ union {
+ BitField< 0, 4, u64> comp0;
+ BitField< 4, 4, u64> comp1;
+ BitField< 8, 4, u64> comp2;
+ BitField<12, 4, u64> comp3;
+ BitField<16, 4, u64> comp4;
+ BitField<20, 4, u64> comp5;
+ BitField<24, 4, u64> comp6;
+ BitField<28, 4, u64> comp7;
+ BitField<32, 4, u64> comp8;
+ BitField<36, 4, u64> comp9;
+ BitField<40, 4, u64> comp10;
+ BitField<44, 4, u64> comp11;
+
+ // bytes for a single vertex in this loader
+ BitField<48, 8, u64> byte_count;
+
+ BitField<60, 4, u64> component_count;
+ };
+
+ inline int GetComponent(int n) const {
+ u64 components[] = {
+ comp0, comp1, comp2, comp3,
+ comp4, comp5, comp6, comp7,
+ comp8, comp9, comp10, comp11
+ };
+ return (int)components[n];
+ }
+ } attribute_loaders[12];
+ } vertex_attributes;
+
+ struct {
+ enum IndexFormat : u32 {
+ BYTE = 0,
+ SHORT = 1,
+ };
+
+ union {
+ BitField<0, 31, u32> offset; // relative to base attribute address
+ BitField<31, 1, IndexFormat> format;
+ };
+ } index_array;
+
+ // Number of vertices to render
+ u32 num_vertices;
+
+ INSERT_PADDING_WORDS(0x1);
+
+ // The index of the first vertex to render
+ u32 vertex_offset;
+
+ INSERT_PADDING_WORDS(0x3);
+
+ // These two trigger rendering of triangles
+ u32 trigger_draw;
+ u32 trigger_draw_indexed;
+
+ INSERT_PADDING_WORDS(0x2);
+
+ // These registers are used to setup the default "fall-back" vertex shader attributes
+ struct {
+ // Index of the current default attribute
+ u32 index;
+
+ // Writing to these registers sets the "current" default attribute.
+ u32 set_value[3];
+ } vs_default_attributes_setup;
+
+ INSERT_PADDING_WORDS(0x2);
+
+ struct {
+ // There are two channels that can be used to configure the next command buffer, which
+ // can be then executed by writing to the "trigger" registers. There are two reasons why a
+ // game might use this feature:
+ // 1) With this, an arbitrary number of additional command buffers may be executed in
+ // sequence without requiring any intervention of the CPU after the initial one is
+ // kicked off.
+ // 2) Games can configure these registers to provide a command list subroutine mechanism.
+
+ BitField< 0, 20, u32> size[2]; ///< Size (in bytes / 8) of each channel's command buffer
+ BitField< 0, 28, u32> addr[2]; ///< Physical address / 8 of each channel's command buffer
+ u32 trigger[2]; ///< Triggers execution of the channel's command buffer when written to
+
+ unsigned GetSize(unsigned index) const {
+ ASSERT(index < 2);
+ return 8 * size[index];
+ }
+
+ PAddr GetPhysicalAddress(unsigned index) const {
+ ASSERT(index < 2);
+ return (PAddr)(8 * addr[index]);
+ }
+ } command_buffer;
+
+ INSERT_PADDING_WORDS(0x20);
+
+ enum class TriangleTopology : u32 {
+ List = 0,
+ Strip = 1,
+ Fan = 2,
+ Shader = 3, // Programmable setup unit implemented in a geometry shader
+ };
+
+ BitField<8, 2, TriangleTopology> triangle_topology;
+
+ u32 restart_primitive;
+
+ INSERT_PADDING_WORDS(0x20);
+
+ struct ShaderConfig {
+ BitField<0, 16, u32> bool_uniforms;
+
+ union {
+ BitField< 0, 8, u32> x;
+ BitField< 8, 8, u32> y;
+ BitField<16, 8, u32> z;
+ BitField<24, 8, u32> w;
+ } int_uniforms[4];
+
+ INSERT_PADDING_WORDS(0x5);
+
+ // Offset to shader program entry point (in words)
+ BitField<0, 16, u32> main_offset;
+
+ union {
+ BitField< 0, 4, u64> attribute0_register;
+ BitField< 4, 4, u64> attribute1_register;
+ BitField< 8, 4, u64> attribute2_register;
+ BitField<12, 4, u64> attribute3_register;
+ BitField<16, 4, u64> attribute4_register;
+ BitField<20, 4, u64> attribute5_register;
+ BitField<24, 4, u64> attribute6_register;
+ BitField<28, 4, u64> attribute7_register;
+ BitField<32, 4, u64> attribute8_register;
+ BitField<36, 4, u64> attribute9_register;
+ BitField<40, 4, u64> attribute10_register;
+ BitField<44, 4, u64> attribute11_register;
+ BitField<48, 4, u64> attribute12_register;
+ BitField<52, 4, u64> attribute13_register;
+ BitField<56, 4, u64> attribute14_register;
+ BitField<60, 4, u64> attribute15_register;
+
+ int GetRegisterForAttribute(int attribute_index) const {
+ u64 fields[] = {
+ attribute0_register, attribute1_register, attribute2_register, attribute3_register,
+ attribute4_register, attribute5_register, attribute6_register, attribute7_register,
+ attribute8_register, attribute9_register, attribute10_register, attribute11_register,
+ attribute12_register, attribute13_register, attribute14_register, attribute15_register,
+ };
+ return (int)fields[attribute_index];
+ }
+ } input_register_map;
+
+ // OUTMAP_MASK, 0x28E, CODETRANSFER_END
+ INSERT_PADDING_WORDS(0x3);
+
+ struct {
+ enum Format : u32
+ {
+ FLOAT24 = 0,
+ FLOAT32 = 1
+ };
+
+ bool IsFloat32() const {
+ return format == FLOAT32;
+ }
+
+ union {
+ // Index of the next uniform to write to
+ // TODO: ctrulib uses 8 bits for this, however that seems to yield lots of invalid indices
+ // TODO: Maybe the uppermost index is for the geometry shader? Investigate!
+ BitField<0, 7, u32> index;
+
+ BitField<31, 1, Format> format;
+ };
+
+ // Writing to these registers sets the current uniform.
+ u32 set_value[8];
+
+ } uniform_setup;
+
+ INSERT_PADDING_WORDS(0x2);
+
+ struct {
+ // Offset of the next instruction to write code to.
+ // Incremented with each instruction write.
+ u32 offset;
+
+ // Writing to these registers sets the "current" word in the shader program.
+ u32 set_word[8];
+ } program;
+
+ INSERT_PADDING_WORDS(0x1);
+
+ // This register group is used to load an internal table of swizzling patterns,
+ // which are indexed by each shader instruction to specify vector component swizzling.
+ struct {
+ // Offset of the next swizzle pattern to write code to.
+ // Incremented with each instruction write.
+ u32 offset;
+
+ // Writing to these registers sets the current swizzle pattern in the table.
+ u32 set_word[8];
+ } swizzle_patterns;
+
+ INSERT_PADDING_WORDS(0x2);
+ };
+
+ ShaderConfig gs;
+ ShaderConfig vs;
+
+ INSERT_PADDING_WORDS(0x20);
+
+ // Map register indices to names readable by humans
+ // Used for debugging purposes, so performance is not an issue here
+ static std::string GetCommandName(int index);
+
+ static inline size_t NumIds() {
+ return sizeof(Regs) / sizeof(u32);
+ }
+
+ u32& operator [] (int index) const {
+ u32* content = (u32*)this;
+ return content[index];
+ }
+
+ u32& operator [] (int index) {
+ u32* content = (u32*)this;
+ return content[index];
+ }
+
+private:
+ /*
+ * Most physical addresses which Pica registers refer to are 8-byte aligned.
+ * This function should be used to get the address from a raw register value.
+ */
+ static inline u32 DecodeAddressRegister(u32 register_value) {
+ return register_value * 8;
+ }
+};
+
+// TODO: MSVC does not support using offsetof() on non-static data members even though this
+// is technically allowed since C++11. This macro should be enabled once MSVC adds
+// support for that.
+#ifndef _MSC_VER
+#define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(Regs, field_name) == position * 4, "Field "#field_name" has invalid position")
+
+ASSERT_REG_POSITION(trigger_irq, 0x10);
+ASSERT_REG_POSITION(cull_mode, 0x40);
+ASSERT_REG_POSITION(viewport_size_x, 0x41);
+ASSERT_REG_POSITION(viewport_size_y, 0x43);
+ASSERT_REG_POSITION(viewport_depth_range, 0x4d);
+ASSERT_REG_POSITION(viewport_depth_far_plane, 0x4e);
+ASSERT_REG_POSITION(vs_output_attributes[0], 0x50);
+ASSERT_REG_POSITION(vs_output_attributes[1], 0x51);
+ASSERT_REG_POSITION(viewport_corner, 0x68);
+ASSERT_REG_POSITION(texture0_enable, 0x80);
+ASSERT_REG_POSITION(texture0, 0x81);
+ASSERT_REG_POSITION(texture0_format, 0x8e);
+ASSERT_REG_POSITION(texture1, 0x91);
+ASSERT_REG_POSITION(texture1_format, 0x96);
+ASSERT_REG_POSITION(texture2, 0x99);
+ASSERT_REG_POSITION(texture2_format, 0x9e);
+ASSERT_REG_POSITION(tev_stage0, 0xc0);
+ASSERT_REG_POSITION(tev_stage1, 0xc8);
+ASSERT_REG_POSITION(tev_stage2, 0xd0);
+ASSERT_REG_POSITION(tev_stage3, 0xd8);
+ASSERT_REG_POSITION(tev_combiner_buffer_input, 0xe0);
+ASSERT_REG_POSITION(tev_stage4, 0xf0);
+ASSERT_REG_POSITION(tev_stage5, 0xf8);
+ASSERT_REG_POSITION(tev_combiner_buffer_color, 0xfd);
+ASSERT_REG_POSITION(output_merger, 0x100);
+ASSERT_REG_POSITION(framebuffer, 0x110);
+ASSERT_REG_POSITION(vertex_attributes, 0x200);
+ASSERT_REG_POSITION(index_array, 0x227);
+ASSERT_REG_POSITION(num_vertices, 0x228);
+ASSERT_REG_POSITION(vertex_offset, 0x22a);
+ASSERT_REG_POSITION(trigger_draw, 0x22e);
+ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f);
+ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232);
+ASSERT_REG_POSITION(command_buffer, 0x238);
+ASSERT_REG_POSITION(triangle_topology, 0x25e);
+ASSERT_REG_POSITION(restart_primitive, 0x25f);
+ASSERT_REG_POSITION(gs, 0x280);
+ASSERT_REG_POSITION(vs, 0x2b0);
+
+#undef ASSERT_REG_POSITION
+#endif // !defined(_MSC_VER)
+
+static_assert(sizeof(Regs::ShaderConfig) == 0x30 * sizeof(u32), "ShaderConfig structure has incorrect size");
+
+// The total number of registers is chosen arbitrarily, but let's make sure it's not some odd value anyway.
+static_assert(sizeof(Regs) <= 0x300 * sizeof(u32), "Register set structure larger than it should be");
+static_assert(sizeof(Regs) >= 0x300 * sizeof(u32), "Register set structure smaller than it should be");
+
+struct float24 {
+ static float24 FromFloat32(float val) {
+ float24 ret;
+ ret.value = val;
+ return ret;
+ }
+
+ // 16 bit mantissa, 7 bit exponent, 1 bit sign
+ // TODO: No idea if this works as intended
+ static float24 FromRawFloat24(u32 hex) {
+ float24 ret;
+ if ((hex & 0xFFFFFF) == 0) {
+ ret.value = 0;
+ } else {
+ u32 mantissa = hex & 0xFFFF;
+ u32 exponent = (hex >> 16) & 0x7F;
+ u32 sign = hex >> 23;
+ ret.value = std::pow(2.0f, (float)exponent-63.0f) * (1.0f + mantissa * std::pow(2.0f, -16.f));
+ if (sign)
+ ret.value = -ret.value;
+ }
+ return ret;
+ }
+
+ static float24 Zero() {
+ return FromFloat32(0.f);
+ }
+
+ // Not recommended for anything but logging
+ float ToFloat32() const {
+ return value;
+ }
+
+ float24 operator * (const float24& flt) const {
+ if ((this->value == 0.f && !std::isnan(flt.value)) ||
+ (flt.value == 0.f && !std::isnan(this->value)))
+ // PICA gives 0 instead of NaN when multiplying by inf
+ return Zero();
+ return float24::FromFloat32(ToFloat32() * flt.ToFloat32());
+ }
+
+ float24 operator / (const float24& flt) const {
+ return float24::FromFloat32(ToFloat32() / flt.ToFloat32());
+ }
+
+ float24 operator + (const float24& flt) const {
+ return float24::FromFloat32(ToFloat32() + flt.ToFloat32());
+ }
+
+ float24 operator - (const float24& flt) const {
+ return float24::FromFloat32(ToFloat32() - flt.ToFloat32());
+ }
+
+ float24& operator *= (const float24& flt) {
+ if ((this->value == 0.f && !std::isnan(flt.value)) ||
+ (flt.value == 0.f && !std::isnan(this->value)))
+ // PICA gives 0 instead of NaN when multiplying by inf
+ *this = Zero();
+ else value *= flt.ToFloat32();
+ return *this;
+ }
+
+ float24& operator /= (const float24& flt) {
+ value /= flt.ToFloat32();
+ return *this;
+ }
+
+ float24& operator += (const float24& flt) {
+ value += flt.ToFloat32();
+ return *this;
+ }
+
+ float24& operator -= (const float24& flt) {
+ value -= flt.ToFloat32();
+ return *this;
+ }
+
+ float24 operator - () const {
+ return float24::FromFloat32(-ToFloat32());
+ }
+
+ bool operator < (const float24& flt) const {
+ return ToFloat32() < flt.ToFloat32();
+ }
+
+ bool operator > (const float24& flt) const {
+ return ToFloat32() > flt.ToFloat32();
+ }
+
+ bool operator >= (const float24& flt) const {
+ return ToFloat32() >= flt.ToFloat32();
+ }
+
+ bool operator <= (const float24& flt) const {
+ return ToFloat32() <= flt.ToFloat32();
+ }
+
+ bool operator == (const float24& flt) const {
+ return ToFloat32() == flt.ToFloat32();
+ }
+
+ bool operator != (const float24& flt) const {
+ return ToFloat32() != flt.ToFloat32();
+ }
+
+private:
+ // Stored as a regular float, merely for convenience
+ // TODO: Perform proper arithmetic on this!
+ float value;
+};
+static_assert(sizeof(float24) == sizeof(float), "Shader JIT assumes float24 is implemented as a 32-bit float");
+
+/// Struct used to describe current Pica state
+struct State {
+ /// Pica registers
+ Regs regs;
+
+ /// Vertex shader memory
+ struct ShaderSetup {
+ struct {
+ // The float uniforms are accessed by the shader JIT using SSE instructions, and are
+ // therefore required to be 16-byte aligned.
+ Math::Vec4<float24> MEMORY_ALIGNED16(f[96]);
+
+ std::array<bool, 16> b;
+ std::array<Math::Vec4<u8>, 4> i;
+ } uniforms;
+
+ Math::Vec4<float24> default_attributes[16];
+
+ std::array<u32, 1024> program_code;
+ std::array<u32, 1024> swizzle_data;
+ };
+
+ ShaderSetup vs;
+ ShaderSetup gs;
+
+ /// Current Pica command list
+ struct {
+ const u32* head_ptr;
+ const u32* current_ptr;
+ u32 length;
+ } cmd_list;
+};
+
+/// Initialize Pica state
+void Init();
+
+/// Shutdown Pica state
+void Shutdown();
+
+extern State g_state; ///< Current Pica state
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "citraimport\GPU\video_core/pica.h"
+#include "citraimport\GPU\video_core/primitive_assembly.h"
+#include "citraimport\GPU\video_core/debug_utils/debug_utils.h"
+#include "citraimport\GPU\video_core/shader/shader_interpreter.h"
+
+namespace Pica {
+
+template<typename VertexType>
+PrimitiveAssembler<VertexType>::PrimitiveAssembler(Regs::TriangleTopology topology)
+ : topology(topology), buffer_index(0) {
+}
+
+template<typename VertexType>
+void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandler triangle_handler)
+{
+ switch (topology) {
+ // TODO: Figure out what's different with TriangleTopology::Shader.
+ case Regs::TriangleTopology::List:
+ case Regs::TriangleTopology::Shader:
+ if (buffer_index < 2) {
+ buffer[buffer_index++] = vtx;
+ } else {
+ buffer_index = 0;
+
+ triangle_handler(buffer[0], buffer[1], vtx);
+ }
+ break;
+
+ case Regs::TriangleTopology::Strip:
+ case Regs::TriangleTopology::Fan:
+ if (strip_ready)
+ triangle_handler(buffer[0], buffer[1], vtx);
+
+ buffer[buffer_index] = vtx;
+
+ if (topology == Regs::TriangleTopology::Strip) {
+ strip_ready |= (buffer_index == 1);
+ buffer_index = !buffer_index;
+ } else if (topology == Regs::TriangleTopology::Fan) {
+ buffer_index = 1;
+ strip_ready = true;
+ }
+ break;
+
+ default:
+ LOG_ERROR(HW_GPU, "Unknown triangle topology %x:", (int)topology);
+ break;
+ }
+}
+
+// explicitly instantiate use cases
+template
+struct PrimitiveAssembler<Shader::OutputVertex>;
+template
+struct PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex>;
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <functional>
+
+#include "citraimport\GPU\video_core/pica.h"
+
+namespace Pica {
+
+/*
+ * Utility class to build triangles from a series of vertices,
+ * according to a given triangle topo
+
+ y.
+ */
+template<typename VertexType>
+struct PrimitiveAssembler {
+ using TriangleHandler = std::function<void(VertexType& v0,
+ VertexType& v1,
+ VertexType& v2)>;
+
+ PrimitiveAssembler(Regs::TriangleTopology topology);
+
+ /*
+ * Queues a vertex, builds primitives from the vertex queue according to the given
+ * triangle topology, and calls triangle_handler for each generated primitive.
+ * NOTE: We could specify the triangle handler in the constructor, but this way we can
+ * keep event and handler code next to each other.
+ */
+ void SubmitVertex(VertexType& vtx, TriangleHandler triangle_handler);
+
+private:
+ Regs::TriangleTopology topology;
+
+ int buffer_index;
+ VertexType buffer[2];
+ bool strip_ready = false;
+};
+
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#define NOMINMAX
+
+#include <algorithm>
+#include <cmath>
+
+#include "citraimport\common/color.h"
+#include "citraimport\common/common_types.h"
+#include "citraimport\common/math_util.h"
+#include "citraimport\common/microprofile.h"
+#include "citraimport\common/profiler.h"
+
+#include "citraimport\GPU\video_core/pica.h"
+#include "citraimport\GPU\video_core/rasterizer.h"
+#include "citraimport\GPU\video_core/utils.h"
+#include "citraimport\GPU\video_core/debug_utils/debug_utils.h"
+#include "citraimport\GPU\video_core/shader/shader_interpreter.h"
+
+#include "citraimport\GPU\HW\gpu.h"
+
+u8* Mem_GetPhysicalPointer(u32 addr);
+
+namespace Pica {
+
+namespace Rasterizer {
+
+static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) {
+ const auto& framebuffer = g_state.regs.framebuffer;
+ const PAddr addr = framebuffer.GetColorBufferPhysicalAddress();
+
+ // Similarly to textures, the render framebuffer is laid out from bottom to top, too.
+ // NOTE: The framebuffer height register contains the actual FB height minus one.
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer.color_format.Value()));
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * framebuffer.width * bytes_per_pixel;
+ u8* dst_pixel = Mem_GetPhysicalPointer(addr) + dst_offset;
+
+ switch (framebuffer.color_format) {
+ case Regs::ColorFormat::RGBA8:
+ Color::EncodeRGBA8(color, dst_pixel);
+ break;
+
+ case Regs::ColorFormat::RGB8:
+ Color::EncodeRGB8(color, dst_pixel);
+ break;
+
+ case Regs::ColorFormat::RGB5A1:
+ Color::EncodeRGB5A1(color, dst_pixel);
+ break;
+
+ case Regs::ColorFormat::RGB565:
+ Color::EncodeRGB565(color, dst_pixel);
+ break;
+
+ case Regs::ColorFormat::RGBA4:
+ Color::EncodeRGBA4(color, dst_pixel);
+ break;
+
+ default:
+ //LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", framebuffer.color_format.Value());
+ //UNIMPLEMENTED();
+ break;
+ }
+}
+
+static const Math::Vec4<u8> GetPixel(int x, int y) {
+ const auto& framebuffer = g_state.regs.framebuffer;
+ const PAddr addr = framebuffer.GetColorBufferPhysicalAddress();
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer.color_format.Value()));
+ u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * framebuffer.width * bytes_per_pixel;
+ u8* src_pixel = Mem_GetPhysicalPointer(addr) + src_offset;
+
+ switch (framebuffer.color_format) {
+ case Regs::ColorFormat::RGBA8:
+ return Color::DecodeRGBA8(src_pixel);
+
+ case Regs::ColorFormat::RGB8:
+ return Color::DecodeRGB8(src_pixel);
+
+ case Regs::ColorFormat::RGB5A1:
+ return Color::DecodeRGB5A1(src_pixel);
+
+ case Regs::ColorFormat::RGB565:
+ return Color::DecodeRGB565(src_pixel);
+
+ case Regs::ColorFormat::RGBA4:
+ return Color::DecodeRGBA4(src_pixel);
+
+ default:
+ //LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", framebuffer.color_format.Value());
+ //UNIMPLEMENTED();
+ break;
+ }
+
+ return {0, 0, 0, 0};
+}
+
+static u32 GetDepth(int x, int y) {
+ const auto& framebuffer = g_state.regs.framebuffer;
+ const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
+ u8* depth_buffer = Mem_GetPhysicalPointer(addr);
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = Regs::BytesPerDepthPixel(framebuffer.depth_format);
+ u32 stride = framebuffer.width * bytes_per_pixel;
+
+ u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ u8* src_pixel = depth_buffer + src_offset;
+
+ switch (framebuffer.depth_format) {
+ case Regs::DepthFormat::D16:
+ return Color::DecodeD16(src_pixel);
+ case Regs::DepthFormat::D24:
+ return Color::DecodeD24(src_pixel);
+ case Regs::DepthFormat::D24S8:
+ return Color::DecodeD24S8(src_pixel).x;
+ default:
+ //LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
+ //UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+static u8 GetStencil(int x, int y) {
+ const auto& framebuffer = g_state.regs.framebuffer;
+ const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
+ u8* depth_buffer = Mem_GetPhysicalPointer(addr);
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(framebuffer.depth_format);
+ u32 stride = framebuffer.width * bytes_per_pixel;
+
+ u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ u8* src_pixel = depth_buffer + src_offset;
+
+ switch (framebuffer.depth_format) {
+ case Regs::DepthFormat::D24S8:
+ return Color::DecodeD24S8(src_pixel).y;
+
+ default:
+ LOG_WARNING(HW_GPU, "GetStencil called for function which doesn't have a stencil component (format %u)", framebuffer.depth_format);
+ return 0;
+ }
+}
+
+static void SetDepth(int x, int y, u32 value) {
+ const auto& framebuffer = g_state.regs.framebuffer;
+ const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
+ u8* depth_buffer = Mem_GetPhysicalPointer(addr);
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = Regs::BytesPerDepthPixel(framebuffer.depth_format);
+ u32 stride = framebuffer.width * bytes_per_pixel;
+
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ u8* dst_pixel = depth_buffer + dst_offset;
+
+ switch (framebuffer.depth_format) {
+ case Regs::DepthFormat::D16:
+ Color::EncodeD16(value, dst_pixel);
+ break;
+
+ case Regs::DepthFormat::D24:
+ Color::EncodeD24(value, dst_pixel);
+ break;
+
+ case Regs::DepthFormat::D24S8:
+ Color::EncodeD24X8(value, dst_pixel);
+ break;
+
+ default:
+ //LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
+ //UNIMPLEMENTED();
+ break;
+ }
+}
+
+static void SetStencil(int x, int y, u8 value) {
+ const auto& framebuffer = g_state.regs.framebuffer;
+ const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
+ u8* depth_buffer = Mem_GetPhysicalPointer(addr);
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(framebuffer.depth_format);
+ u32 stride = framebuffer.width * bytes_per_pixel;
+
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ u8* dst_pixel = depth_buffer + dst_offset;
+
+ switch (framebuffer.depth_format) {
+ case Pica::Regs::DepthFormat::D16:
+ case Pica::Regs::DepthFormat::D24:
+ // Nothing to do
+ break;
+
+ case Pica::Regs::DepthFormat::D24S8:
+ Color::EncodeX24S8(value, dst_pixel);
+ break;
+
+ default:
+ //LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
+ //UNIMPLEMENTED();
+ break;
+ }
+}
+
+static u8 PerformStencilAction(Regs::StencilAction action, u8 old_stencil, u8 ref) {
+ switch (action) {
+ case Regs::StencilAction::Keep:
+ return old_stencil;
+
+ case Regs::StencilAction::Zero:
+ return 0;
+
+ case Regs::StencilAction::Replace:
+ return ref;
+
+ case Regs::StencilAction::Increment:
+ // Saturated increment
+ return std::min<u8>(old_stencil, 254) + 1;
+
+ case Regs::StencilAction::Decrement:
+ // Saturated decrement
+ return std::max<u8>(old_stencil, 1) - 1;
+
+ case Regs::StencilAction::Invert:
+ return ~old_stencil;
+
+ case Regs::StencilAction::IncrementWrap:
+ return old_stencil + 1;
+
+ case Regs::StencilAction::DecrementWrap:
+ return old_stencil - 1;
+
+ default:
+ //LOG_CRITICAL(HW_GPU, "Unknown stencil action %x", (int)action);
+ //UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+// NOTE: Assuming that rasterizer coordinates are 12.4 fixed-point values
+struct Fix12P4 {
+ Fix12P4() {}
+ Fix12P4(u16 val) : val(val) {}
+
+ static u16 FracMask() { return 0xF; }
+ static u16 IntMask() { return (u16)~0xF; }
+
+ operator u16() const {
+ return val;
+ }
+
+ bool operator < (const Fix12P4& oth) const {
+ return (u16)*this < (u16)oth;
+ }
+
+private:
+ u16 val;
+};
+
+/**
+ * Calculate signed area of the triangle spanned by the three argument vertices.
+ * The sign denotes an orientation.
+ *
+ * @todo define orientation concretely.
+ */
+static int SignedArea (const Math::Vec2<Fix12P4>& vtx1,
+ const Math::Vec2<Fix12P4>& vtx2,
+ const Math::Vec2<Fix12P4>& vtx3) {
+ const auto vec1 = Math::MakeVec(vtx2 - vtx1, 0);
+ const auto vec2 = Math::MakeVec(vtx3 - vtx1, 0);
+ // TODO: There is a very small chance this will overflow for sizeof(int) == 4
+ return Math::Cross(vec1, vec2).z;
+};
+
+static Common::Profiling::TimingCategory rasterization_category("Rasterization");
+
+/**
+ * Helper function for ProcessTriangle with the "reversed" flag to allow for implementing
+ * culling via recursion.
+ */
+static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
+ const Shader::OutputVertex& v1,
+ const Shader::OutputVertex& v2,
+ bool reversed = false)
+{
+ const auto& regs = g_state.regs;
+ Common::Profiling::ScopeTimer timer(rasterization_category);
+
+ // vertex positions in rasterizer coordinates
+ static auto FloatToFix = [](float24 flt) {
+ // TODO: Rounding here is necessary to prevent garbage pixels at
+ // triangle borders. Is it that the correct solution, though?
+ return Fix12P4(static_cast<unsigned short>(round(flt.ToFloat32() * 16.0f)));
+ };
+ static auto ScreenToRasterizerCoordinates = [](const Math::Vec3<float24>& vec) {
+ return Math::Vec3<Fix12P4>{FloatToFix(vec.x), FloatToFix(vec.y), FloatToFix(vec.z)};
+ };
+
+ Math::Vec3<Fix12P4> vtxpos[3]{ ScreenToRasterizerCoordinates(v0.screenpos),
+ ScreenToRasterizerCoordinates(v1.screenpos),
+ ScreenToRasterizerCoordinates(v2.screenpos) };
+
+ if (regs.cull_mode == Regs::CullMode::KeepAll) {
+ // Make sure we always end up with a triangle wound counter-clockwise
+ if (!reversed && SignedArea(vtxpos[0].xy(), vtxpos[1].xy(), vtxpos[2].xy()) <= 0) {
+ ProcessTriangleInternal(v0, v2, v1, true);
+ return;
+ }
+ } else {
+ if (!reversed && regs.cull_mode == Regs::CullMode::KeepClockWise) {
+ // Reverse vertex order and use the CCW code path.
+ ProcessTriangleInternal(v0, v2, v1, true);
+ return;
+ }
+
+ // Cull away triangles which are wound clockwise.
+ if (SignedArea(vtxpos[0].xy(), vtxpos[1].xy(), vtxpos[2].xy()) <= 0)
+ return;
+ }
+
+ // TODO: Proper scissor rect test!
+ u16 min_x = std::min({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x});
+ u16 min_y = std::min({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y});
+ u16 max_x = std::max({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x});
+ u16 max_y = std::max({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y});
+
+ min_x &= Fix12P4::IntMask();
+ min_y &= Fix12P4::IntMask();
+ max_x = ((max_x + Fix12P4::FracMask()) & Fix12P4::IntMask());
+ max_y = ((max_y + Fix12P4::FracMask()) & Fix12P4::IntMask());
+
+ // Triangle filling rules: Pixels on the right-sided edge or on flat bottom edges are not
+ // drawn. Pixels on any other triangle border are drawn. This is implemented with three bias
+ // values which are added to the barycentric coordinates w0, w1 and w2, respectively.
+ // NOTE: These are the PSP filling rules. Not sure if the 3DS uses the same ones...
+ auto IsRightSideOrFlatBottomEdge = [](const Math::Vec2<Fix12P4>& vtx,
+ const Math::Vec2<Fix12P4>& line1,
+ const Math::Vec2<Fix12P4>& line2)
+ {
+ if (line1.y == line2.y) {
+ // just check if vertex is above us => bottom line parallel to x-axis
+ return vtx.y < line1.y;
+ } else {
+ // check if vertex is on our left => right side
+ // TODO: Not sure how likely this is to overflow
+ return (int)vtx.x < (int)line1.x + ((int)line2.x - (int)line1.x) * ((int)vtx.y - (int)line1.y) / ((int)line2.y - (int)line1.y);
+ }
+ };
+ int bias0 = IsRightSideOrFlatBottomEdge(vtxpos[0].xy(), vtxpos[1].xy(), vtxpos[2].xy()) ? -1 : 0;
+ int bias1 = IsRightSideOrFlatBottomEdge(vtxpos[1].xy(), vtxpos[2].xy(), vtxpos[0].xy()) ? -1 : 0;
+ int bias2 = IsRightSideOrFlatBottomEdge(vtxpos[2].xy(), vtxpos[0].xy(), vtxpos[1].xy()) ? -1 : 0;
+
+ auto w_inverse = Math::MakeVec(v0.pos.w, v1.pos.w, v2.pos.w);
+
+ auto textures = regs.GetTextures();
+ auto tev_stages = regs.GetTevStages();
+
+ bool stencil_action_enable = g_state.regs.output_merger.stencil_test.enable && g_state.regs.framebuffer.depth_format == Regs::DepthFormat::D24S8;
+ const auto stencil_test = g_state.regs.output_merger.stencil_test;
+
+ // Enter rasterization loop, starting at the center of the topleft bounding box corner.
+ // TODO: Not sure if looping through x first might be faster
+ for (u16 y = min_y + 8; y < max_y; y += 0x10) {
+ for (u16 x = min_x + 8; x < max_x; x += 0x10) {
+
+ // Calculate the barycentric coordinates w0, w1 and w2
+ int w0 = bias0 + SignedArea(vtxpos[1].xy(), vtxpos[2].xy(), {x, y});
+ int w1 = bias1 + SignedArea(vtxpos[2].xy(), vtxpos[0].xy(), {x, y});
+ int w2 = bias2 + SignedArea(vtxpos[0].xy(), vtxpos[1].xy(), {x, y});
+ int wsum = w0 + w1 + w2;
+
+ // If current pixel is not covered by the current primitive
+ if (w0 < 0 || w1 < 0 || w2 < 0)
+ continue;
+
+ auto baricentric_coordinates = Math::MakeVec(float24::FromFloat32(static_cast<float>(w0)),
+ float24::FromFloat32(static_cast<float>(w1)),
+ float24::FromFloat32(static_cast<float>(w2)));
+ float24 interpolated_w_inverse = float24::FromFloat32(1.0f) / Math::Dot(w_inverse, baricentric_coordinates);
+
+ // Perspective correct attribute interpolation:
+ // Attribute values cannot be calculated by simple linear interpolation since
+ // they are not linear in screen space. For example, when interpolating a
+ // texture coordinate across two vertices, something simple like
+ // u = (u0*w0 + u1*w1)/(w0+w1)
+ // will not work. However, the attribute value divided by the
+ // clipspace w-coordinate (u/w) and and the inverse w-coordinate (1/w) are linear
+ // in screenspace. Hence, we can linearly interpolate these two independently and
+ // calculate the interpolated attribute by dividing the results.
+ // I.e.
+ // u_over_w = ((u0/v0.pos.w)*w0 + (u1/v1.pos.w)*w1)/(w0+w1)
+ // one_over_w = (( 1/v0.pos.w)*w0 + ( 1/v1.pos.w)*w1)/(w0+w1)
+ // u = u_over_w / one_over_w
+ //
+ // The generalization to three vertices is straightforward in baricentric coordinates.
+ auto GetInterpolatedAttribute = [&](float24 attr0, float24 attr1, float24 attr2) {
+ auto attr_over_w = Math::MakeVec(attr0, attr1, attr2);
+ float24 interpolated_attr_over_w = Math::Dot(attr_over_w, baricentric_coordinates);
+ return interpolated_attr_over_w * interpolated_w_inverse;
+ };
+
+ Math::Vec4<u8> primary_color{
+ (u8)(GetInterpolatedAttribute(v0.color.r(), v1.color.r(), v2.color.r()).ToFloat32() * 255),
+ (u8)(GetInterpolatedAttribute(v0.color.g(), v1.color.g(), v2.color.g()).ToFloat32() * 255),
+ (u8)(GetInterpolatedAttribute(v0.color.b(), v1.color.b(), v2.color.b()).ToFloat32() * 255),
+ (u8)(GetInterpolatedAttribute(v0.color.a(), v1.color.a(), v2.color.a()).ToFloat32() * 255)
+ };
+
+ Math::Vec2<float24> uv[3];
+ uv[0].u() = GetInterpolatedAttribute(v0.tc0.u(), v1.tc0.u(), v2.tc0.u());
+ uv[0].v() = GetInterpolatedAttribute(v0.tc0.v(), v1.tc0.v(), v2.tc0.v());
+ uv[1].u() = GetInterpolatedAttribute(v0.tc1.u(), v1.tc1.u(), v2.tc1.u());
+ uv[1].v() = GetInterpolatedAttribute(v0.tc1.v(), v1.tc1.v(), v2.tc1.v());
+ uv[2].u() = GetInterpolatedAttribute(v0.tc2.u(), v1.tc2.u(), v2.tc2.u());
+ uv[2].v() = GetInterpolatedAttribute(v0.tc2.v(), v1.tc2.v(), v2.tc2.v());
+
+ Math::Vec4<u8> texture_color[3]{};
+ for (int i = 0; i < 3; ++i) {
+ const auto& texture = textures[i];
+ if (!texture.enabled)
+ continue;
+
+ assert(0 != texture.config.address);
+
+ int s = (int)(uv[i].u() * float24::FromFloat32(static_cast<float>(texture.config.width))).ToFloat32();
+ int t = (int)(uv[i].v() * float24::FromFloat32(static_cast<float>(texture.config.height))).ToFloat32();
+ static auto GetWrappedTexCoord = [](Regs::TextureConfig::WrapMode mode, int val, unsigned size) {
+ switch (mode) {
+ case Regs::TextureConfig::ClampToEdge:
+ val = std::max(val, 0);
+ val = std::min(val, (int)size - 1);
+ return val;
+
+ case Regs::TextureConfig::ClampToBorder:
+ return val;
+
+ case Regs::TextureConfig::Repeat:
+ return (int)((unsigned)val % size);
+
+ case Regs::TextureConfig::MirroredRepeat:
+ {
+ unsigned int coord = ((unsigned)val % (2 * size));
+ if (coord >= size)
+ coord = 2 * size - 1 - coord;
+ return (int)coord;
+ }
+
+ default:
+ //LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x\n", (int)mode);
+ //UNIMPLEMENTED();
+ return 0;
+ }
+ };
+
+ if ((texture.config.wrap_s == Regs::TextureConfig::ClampToBorder && (s < 0 || s >= texture.config.width))
+ || (texture.config.wrap_t == Regs::TextureConfig::ClampToBorder && (t < 0 || t >= texture.config.height))) {
+ auto border_color = texture.config.border_color;
+ texture_color[i] = { border_color.r, border_color.g, border_color.b, border_color.a };
+ } else {
+ // Textures are laid out from bottom to top, hence we invert the t coordinate.
+ // NOTE: This may not be the right place for the inversion.
+ // TODO: Check if this applies to ETC textures, too.
+ s = GetWrappedTexCoord(texture.config.wrap_s, s, texture.config.width);
+ t = texture.config.height - 1 - GetWrappedTexCoord(texture.config.wrap_t, t, texture.config.height);
+
+ u8* texture_data = Mem_GetPhysicalPointer(texture.config.GetPhysicalAddress());
+ auto info = DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format);
+
+ // TODO: Apply the min and mag filters to the texture
+ texture_color[i] = DebugUtils::LookupTexture(texture_data, s, t, info);
+#if PICA_DUMP_TEXTURES
+ DebugUtils::DumpTexture(texture.config, texture_data);
+#endif
+ }
+ }
+
+ // Texture environment - consists of 6 stages of color and alpha combining.
+ //
+ // Color combiners take three input color values from some source (e.g. interpolated
+ // vertex color, texture color, previous stage, etc), perform some very simple
+ // operations on each of them (e.g. inversion) and then calculate the output color
+ // with some basic arithmetic. Alpha combiners can be configured separately but work
+ // analogously.
+ Math::Vec4<u8> combiner_output;
+ Math::Vec4<u8> combiner_buffer = {
+ regs.tev_combiner_buffer_color.r, regs.tev_combiner_buffer_color.g,
+ regs.tev_combiner_buffer_color.b, regs.tev_combiner_buffer_color.a
+ };
+
+ for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
+ const auto& tev_stage = tev_stages[tev_stage_index];
+ using Source = Regs::TevStageConfig::Source;
+ using ColorModifier = Regs::TevStageConfig::ColorModifier;
+ using AlphaModifier = Regs::TevStageConfig::AlphaModifier;
+ using Operation = Regs::TevStageConfig::Operation;
+
+ auto GetSource = [&](Source source) -> Math::Vec4<u8> {
+ switch (source) {
+ case Source::PrimaryColor:
+
+ // HACK: Until we implement fragment lighting, use primary_color
+ case Source::PrimaryFragmentColor:
+ return primary_color;
+
+ // HACK: Until we implement fragment lighting, use zero
+ case Source::SecondaryFragmentColor:
+ return {0, 0, 0, 0};
+
+ case Source::Texture0:
+ return texture_color[0];
+
+ case Source::Texture1:
+ return texture_color[1];
+
+ case Source::Texture2:
+ return texture_color[2];
+
+ case Source::PreviousBuffer:
+ return combiner_buffer;
+
+ case Source::Constant:
+ return {tev_stage.const_r, tev_stage.const_g, tev_stage.const_b, tev_stage.const_a};
+
+ case Source::Previous:
+ return combiner_output;
+
+ default:
+ //LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source);
+ //UNIMPLEMENTED();
+ return {0, 0, 0, 0};
+ }
+ };
+
+ static auto GetColorModifier = [](ColorModifier factor, const Math::Vec4<u8>& values) -> Math::Vec3<u8> {
+ switch (factor) {
+ case ColorModifier::SourceColor:
+ return values.rgb();
+
+ case ColorModifier::OneMinusSourceColor:
+ return (Math::Vec3<u8>(255, 255, 255) - values.rgb()).Cast<u8>();
+
+ case ColorModifier::SourceAlpha:
+ return values.aaa();
+
+ case ColorModifier::OneMinusSourceAlpha:
+ return (Math::Vec3<u8>(255, 255, 255) - values.aaa()).Cast<u8>();
+
+ case ColorModifier::SourceRed:
+ return values.rrr();
+
+ case ColorModifier::OneMinusSourceRed:
+ return (Math::Vec3<u8>(255, 255, 255) - values.rrr()).Cast<u8>();
+
+ case ColorModifier::SourceGreen:
+ return values.ggg();
+
+ case ColorModifier::OneMinusSourceGreen:
+ return (Math::Vec3<u8>(255, 255, 255) - values.ggg()).Cast<u8>();
+
+ case ColorModifier::SourceBlue:
+ return values.bbb();
+
+ case ColorModifier::OneMinusSourceBlue:
+ return (Math::Vec3<u8>(255, 255, 255) - values.bbb()).Cast<u8>();
+ }
+ };
+
+ static auto GetAlphaModifier = [](AlphaModifier factor, const Math::Vec4<u8>& values) -> u8 {
+ switch (factor) {
+ case AlphaModifier::SourceAlpha:
+ return values.a();
+
+ case AlphaModifier::OneMinusSourceAlpha:
+ return 255 - values.a();
+
+ case AlphaModifier::SourceRed:
+ return values.r();
+
+ case AlphaModifier::OneMinusSourceRed:
+ return 255 - values.r();
+
+ case AlphaModifier::SourceGreen:
+ return values.g();
+
+ case AlphaModifier::OneMinusSourceGreen:
+ return 255 - values.g();
+
+ case AlphaModifier::SourceBlue:
+ return values.b();
+
+ case AlphaModifier::OneMinusSourceBlue:
+ return 255 - values.b();
+ }
+ };
+
+ static auto ColorCombine = [](Operation op, const Math::Vec3<u8> input[3]) -> Math::Vec3<u8> {
+ switch (op) {
+ case Operation::Replace:
+ return input[0];
+
+ case Operation::Modulate:
+ return ((input[0] * input[1]) / 255).Cast<u8>();
+
+ case Operation::Add:
+ {
+ auto result = input[0] + input[1];
+ result.r() = std::min(255, result.r());
+ result.g() = std::min(255, result.g());
+ result.b() = std::min(255, result.b());
+ return result.Cast<u8>();
+ }
+
+ case Operation::AddSigned:
+ {
+ // TODO(bunnei): Verify that the color conversion from (float) 0.5f to (byte) 128 is correct
+ auto result = input[0].Cast<int>() + input[1].Cast<int>() - Math::MakeVec<int>(128, 128, 128);
+ result.r() = MathUtil::Clamp<int>(result.r(), 0, 255);
+ result.g() = MathUtil::Clamp<int>(result.g(), 0, 255);
+ result.b() = MathUtil::Clamp<int>(result.b(), 0, 255);
+ return result.Cast<u8>();
+ }
+
+ case Operation::Lerp:
+ return ((input[0] * input[2] + input[1] * (Math::MakeVec<u8>(255, 255, 255) - input[2]).Cast<u8>()) / 255).Cast<u8>();
+
+ case Operation::Subtract:
+ {
+ auto result = input[0].Cast<int>() - input[1].Cast<int>();
+ result.r() = std::max(0, result.r());
+ result.g() = std::max(0, result.g());
+ result.b() = std::max(0, result.b());
+ return result.Cast<u8>();
+ }
+
+ case Operation::MultiplyThenAdd:
+ {
+ auto result = (input[0] * input[1] + 255 * input[2].Cast<int>()) / 255;
+ result.r() = std::min(255, result.r());
+ result.g() = std::min(255, result.g());
+ result.b() = std::min(255, result.b());
+ return result.Cast<u8>();
+ }
+
+ case Operation::AddThenMultiply:
+ {
+ auto result = input[0] + input[1];
+ result.r() = std::min(255, result.r());
+ result.g() = std::min(255, result.g());
+ result.b() = std::min(255, result.b());
+ result = (result * input[2].Cast<int>()) / 255;
+ return result.Cast<u8>();
+ }
+ case Operation::Dot3_RGB:
+ {
+ // Not fully accurate.
+ // Worst case scenario seems to yield a +/-3 error
+ // Some HW results indicate that the per-component computation can't have a higher precision than 1/256,
+ // while dot3_rgb( (0x80,g0,b0),(0x7F,g1,b1) ) and dot3_rgb( (0x80,g0,b0),(0x80,g1,b1) ) give different results
+ int result = ((input[0].r() * 2 - 255) * (input[1].r() * 2 - 255) + 128) / 256 +
+ ((input[0].g() * 2 - 255) * (input[1].g() * 2 - 255) + 128) / 256 +
+ ((input[0].b() * 2 - 255) * (input[1].b() * 2 - 255) + 128) / 256;
+ result = std::max(0, std::min(255, result));
+ return { (u8)result, (u8)result, (u8)result };
+ }
+ default:
+ LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op);
+ UNIMPLEMENTED();
+ return {0, 0, 0};
+ }
+ };
+
+ static auto AlphaCombine = [](Operation op, const std::array<u8,3>& input) -> u8 {
+ switch (op) {
+ case Operation::Replace:
+ return input[0];
+
+ case Operation::Modulate:
+ return input[0] * input[1] / 255;
+
+ case Operation::Add:
+ return std::min(255, input[0] + input[1]);
+
+ case Operation::AddSigned:
+ {
+ // TODO(bunnei): Verify that the color conversion from (float) 0.5f to (byte) 128 is correct
+ auto result = static_cast<int>(input[0]) + static_cast<int>(input[1]) - 128;
+ return static_cast<u8>(MathUtil::Clamp<int>(result, 0, 255));
+ }
+
+ case Operation::Lerp:
+ return (input[0] * input[2] + input[1] * (255 - input[2])) / 255;
+
+ case Operation::Subtract:
+ return std::max(0, (int)input[0] - (int)input[1]);
+
+ case Operation::MultiplyThenAdd:
+ return std::min(255, (input[0] * input[1] + 255 * input[2]) / 255);
+
+ case Operation::AddThenMultiply:
+ return (std::min(255, (input[0] + input[1])) * input[2]) / 255;
+
+ default:
+ LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op);
+ UNIMPLEMENTED();
+ return 0;
+ }
+ };
+
+ // color combiner
+ // NOTE: Not sure if the alpha combiner might use the color output of the previous
+ // stage as input. Hence, we currently don't directly write the result to
+ // combiner_output.rgb(), but instead store it in a temporary variable until
+ // alpha combining has been done.
+ Math::Vec3<u8> color_result[3] = {
+ GetColorModifier(tev_stage.color_modifier1, GetSource(tev_stage.color_source1)),
+ GetColorModifier(tev_stage.color_modifier2, GetSource(tev_stage.color_source2)),
+ GetColorModifier(tev_stage.color_modifier3, GetSource(tev_stage.color_source3))
+ };
+ auto color_output = ColorCombine(tev_stage.color_op, color_result);
+
+ // alpha combiner
+ std::array<u8,3> alpha_result = {{
+ GetAlphaModifier(tev_stage.alpha_modifier1, GetSource(tev_stage.alpha_source1)),
+ GetAlphaModifier(tev_stage.alpha_modifier2, GetSource(tev_stage.alpha_source2)),
+ GetAlphaModifier(tev_stage.alpha_modifier3, GetSource(tev_stage.alpha_source3))
+ }};
+ auto alpha_output = AlphaCombine(tev_stage.alpha_op, alpha_result);
+
+ combiner_output[0] = std::min((unsigned)255, color_output.r() * tev_stage.GetColorMultiplier());
+ combiner_output[1] = std::min((unsigned)255, color_output.g() * tev_stage.GetColorMultiplier());
+ combiner_output[2] = std::min((unsigned)255, color_output.b() * tev_stage.GetColorMultiplier());
+ combiner_output[3] = std::min((unsigned)255, alpha_output * tev_stage.GetAlphaMultiplier());
+
+ if (regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(tev_stage_index)) {
+ combiner_buffer.r() = combiner_output.r();
+ combiner_buffer.g() = combiner_output.g();
+ combiner_buffer.b() = combiner_output.b();
+ }
+
+ if (regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(tev_stage_index)) {
+ combiner_buffer.a() = combiner_output.a();
+ }
+ }
+
+ const auto& output_merger = regs.output_merger;
+ // TODO: Does alpha testing happen before or after stencil?
+ if (output_merger.alpha_test.enable) {
+ bool pass = false;
+
+ switch (output_merger.alpha_test.func) {
+ case Regs::CompareFunc::Never:
+ pass = false;
+ break;
+
+ case Regs::CompareFunc::Always:
+ pass = true;
+ break;
+
+ case Regs::CompareFunc::Equal:
+ pass = combiner_output.a() == output_merger.alpha_test.ref;
+ break;
+
+ case Regs::CompareFunc::NotEqual:
+ pass = combiner_output.a() != output_merger.alpha_test.ref;
+ break;
+
+ case Regs::CompareFunc::LessThan:
+ pass = combiner_output.a() < output_merger.alpha_test.ref;
+ break;
+
+ case Regs::CompareFunc::LessThanOrEqual:
+ pass = combiner_output.a() <= output_merger.alpha_test.ref;
+ break;
+
+ case Regs::CompareFunc::GreaterThan:
+ pass = combiner_output.a() > output_merger.alpha_test.ref;
+ break;
+
+ case Regs::CompareFunc::GreaterThanOrEqual:
+ pass = combiner_output.a() >= output_merger.alpha_test.ref;
+ break;
+ }
+
+ if (!pass)
+ continue;
+ }
+
+ u8 old_stencil = 0;
+
+ auto UpdateStencil = [stencil_test, x, y, &old_stencil](Pica::Regs::StencilAction action) {
+ u8 new_stencil = PerformStencilAction(action, old_stencil, stencil_test.reference_value);
+ SetStencil(x >> 4, y >> 4, (new_stencil & stencil_test.write_mask) | (old_stencil & ~stencil_test.write_mask));
+ };
+
+ if (stencil_action_enable) {
+ old_stencil = GetStencil(x >> 4, y >> 4);
+ u8 dest = old_stencil & stencil_test.input_mask;
+ u8 ref = stencil_test.reference_value & stencil_test.input_mask;
+
+ bool pass = false;
+ switch (stencil_test.func) {
+ case Regs::CompareFunc::Never:
+ pass = false;
+ break;
+
+ case Regs::CompareFunc::Always:
+ pass = true;
+ break;
+
+ case Regs::CompareFunc::Equal:
+ pass = (ref == dest);
+ break;
+
+ case Regs::CompareFunc::NotEqual:
+ pass = (ref != dest);
+ break;
+
+ case Regs::CompareFunc::LessThan:
+ pass = (ref < dest);
+ break;
+
+ case Regs::CompareFunc::LessThanOrEqual:
+ pass = (ref <= dest);
+ break;
+
+ case Regs::CompareFunc::GreaterThan:
+ pass = (ref > dest);
+ break;
+
+ case Regs::CompareFunc::GreaterThanOrEqual:
+ pass = (ref >= dest);
+ break;
+ }
+
+ if (!pass) {
+ UpdateStencil(stencil_test.action_stencil_fail);
+ continue;
+ }
+ }
+
+ // TODO: Does depth indeed only get written even if depth testing is enabled?
+ if (output_merger.depth_test_enable) {
+ unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format);
+ u32 z = (u32)((v0.screenpos[2].ToFloat32() * w0 +
+ v1.screenpos[2].ToFloat32() * w1 +
+ v2.screenpos[2].ToFloat32() * w2) * ((1 << num_bits) - 1) / wsum);
+ u32 ref_z = GetDepth(x >> 4, y >> 4);
+
+ bool pass = false;
+
+ switch (output_merger.depth_test_func) {
+ case Regs::CompareFunc::Never:
+ pass = false;
+ break;
+
+ case Regs::CompareFunc::Always:
+ pass = true;
+ break;
+
+ case Regs::CompareFunc::Equal:
+ pass = z == ref_z;
+ break;
+
+ case Regs::CompareFunc::NotEqual:
+ pass = z != ref_z;
+ break;
+
+ case Regs::CompareFunc::LessThan:
+ pass = z < ref_z;
+ break;
+
+ case Regs::CompareFunc::LessThanOrEqual:
+ pass = z <= ref_z;
+ break;
+
+ case Regs::CompareFunc::GreaterThan:
+ pass = z > ref_z;
+ break;
+
+ case Regs::CompareFunc::GreaterThanOrEqual:
+ pass = z >= ref_z;
+ break;
+ }
+
+ if (!pass) {
+ if (stencil_action_enable)
+ UpdateStencil(stencil_test.action_depth_fail);
+ continue;
+ }
+
+ if (output_merger.depth_write_enable)
+ SetDepth(x >> 4, y >> 4, z);
+ }
+
+ // The stencil depth_pass action is executed even if depth testing is disabled
+ if (stencil_action_enable)
+ UpdateStencil(stencil_test.action_depth_pass);
+
+ auto dest = GetPixel(x >> 4, y >> 4);
+ Math::Vec4<u8> blend_output = combiner_output;
+
+ if (output_merger.alphablend_enable) {
+ auto params = output_merger.alpha_blending;
+
+ auto LookupFactorRGB = [&](Regs::BlendFactor factor) -> Math::Vec3<u8> {
+ switch (factor) {
+ case Regs::BlendFactor::Zero :
+ return Math::Vec3<u8>(0, 0, 0);
+
+ case Regs::BlendFactor::One :
+ return Math::Vec3<u8>(255, 255, 255);
+
+ case Regs::BlendFactor::SourceColor:
+ return combiner_output.rgb();
+
+ case Regs::BlendFactor::OneMinusSourceColor:
+ return Math::Vec3<u8>(255 - combiner_output.r(), 255 - combiner_output.g(), 255 - combiner_output.b());
+
+ case Regs::BlendFactor::DestColor:
+ return dest.rgb();
+
+ case Regs::BlendFactor::OneMinusDestColor:
+ return Math::Vec3<u8>(255 - dest.r(), 255 - dest.g(), 255 - dest.b());
+
+ case Regs::BlendFactor::SourceAlpha:
+ return Math::Vec3<u8>(combiner_output.a(), combiner_output.a(), combiner_output.a());
+
+ case Regs::BlendFactor::OneMinusSourceAlpha:
+ return Math::Vec3<u8>(255 - combiner_output.a(), 255 - combiner_output.a(), 255 - combiner_output.a());
+
+ case Regs::BlendFactor::DestAlpha:
+ return Math::Vec3<u8>(dest.a(), dest.a(), dest.a());
+
+ case Regs::BlendFactor::OneMinusDestAlpha:
+ return Math::Vec3<u8>(255 - dest.a(), 255 - dest.a(), 255 - dest.a());
+
+ case Regs::BlendFactor::ConstantColor:
+ return Math::Vec3<u8>(output_merger.blend_const.r, output_merger.blend_const.g, output_merger.blend_const.b);
+
+ case Regs::BlendFactor::OneMinusConstantColor:
+ return Math::Vec3<u8>(255 - output_merger.blend_const.r, 255 - output_merger.blend_const.g, 255 - output_merger.blend_const.b);
+
+ case Regs::BlendFactor::ConstantAlpha:
+ return Math::Vec3<u8>(output_merger.blend_const.a, output_merger.blend_const.a, output_merger.blend_const.a);
+
+ case Regs::BlendFactor::OneMinusConstantAlpha:
+ return Math::Vec3<u8>(255 - output_merger.blend_const.a, 255 - output_merger.blend_const.a, 255 - output_merger.blend_const.a);
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown color blend factor %x", factor);
+ UNIMPLEMENTED();
+ break;
+ }
+
+ return {};
+ };
+
+ auto LookupFactorA = [&](Regs::BlendFactor factor) -> u8 {
+ switch (factor) {
+ case Regs::BlendFactor::Zero:
+ return 0;
+
+ case Regs::BlendFactor::One:
+ return 255;
+
+ case Regs::BlendFactor::SourceAlpha:
+ return combiner_output.a();
+
+ case Regs::BlendFactor::OneMinusSourceAlpha:
+ return 255 - combiner_output.a();
+
+ case Regs::BlendFactor::DestAlpha:
+ return dest.a();
+
+ case Regs::BlendFactor::OneMinusDestAlpha:
+ return 255 - dest.a();
+
+ case Regs::BlendFactor::ConstantAlpha:
+ return output_merger.blend_const.a;
+
+ case Regs::BlendFactor::OneMinusConstantAlpha:
+ return 255 - output_merger.blend_const.a;
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown alpha blend factor %x", factor);
+ UNIMPLEMENTED();
+ break;
+ }
+
+ return {};
+ };
+
+ static auto EvaluateBlendEquation = [](const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor,
+ const Math::Vec4<u8>& dest, const Math::Vec4<u8>& destfactor,
+ Regs::BlendEquation equation) {
+ Math::Vec4<int> result;
+
+ auto src_result = (src * srcfactor).Cast<int>();
+ auto dst_result = (dest * destfactor).Cast<int>();
+
+ switch (equation) {
+ case Regs::BlendEquation::Add:
+ result = (src_result + dst_result) / 255;
+ break;
+
+ case Regs::BlendEquation::Subtract:
+ result = (src_result - dst_result) / 255;
+ break;
+
+ case Regs::BlendEquation::ReverseSubtract:
+ result = (dst_result - src_result) / 255;
+ break;
+
+ // TODO: How do these two actually work?
+ // OpenGL doesn't include the blend factors in the min/max computations,
+ // but is this what the 3DS actually does?
+ case Regs::BlendEquation::Min:
+ result.r() = std::min(src.r(), dest.r());
+ result.g() = std::min(src.g(), dest.g());
+ result.b() = std::min(src.b(), dest.b());
+ result.a() = std::min(src.a(), dest.a());
+ break;
+
+ case Regs::BlendEquation::Max:
+ result.r() = std::max(src.r(), dest.r());
+ result.g() = std::max(src.g(), dest.g());
+ result.b() = std::max(src.b(), dest.b());
+ result.a() = std::max(src.a(), dest.a());
+ break;
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", equation);
+ UNIMPLEMENTED();
+ }
+
+ return Math::Vec4<u8>(MathUtil::Clamp(result.r(), 0, 255),
+ MathUtil::Clamp(result.g(), 0, 255),
+ MathUtil::Clamp(result.b(), 0, 255),
+ MathUtil::Clamp(result.a(), 0, 255));
+ };
+
+ auto srcfactor = Math::MakeVec(LookupFactorRGB(params.factor_source_rgb),
+ LookupFactorA(params.factor_source_a));
+ auto dstfactor = Math::MakeVec(LookupFactorRGB(params.factor_dest_rgb),
+ LookupFactorA(params.factor_dest_a));
+
+ blend_output = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_rgb);
+ blend_output.a() = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_a).a();
+ } else {
+ static auto LogicOp = [](u8 src, u8 dest, Regs::LogicOp op) -> u8 {
+ switch (op) {
+ case Regs::LogicOp::Clear:
+ return 0;
+
+ case Regs::LogicOp::And:
+ return src & dest;
+
+ case Regs::LogicOp::AndReverse:
+ return src & ~dest;
+
+ case Regs::LogicOp::Copy:
+ return src;
+
+ case Regs::LogicOp::Set:
+ return 255;
+
+ case Regs::LogicOp::CopyInverted:
+ return ~src;
+
+ case Regs::LogicOp::NoOp:
+ return dest;
+
+ case Regs::LogicOp::Invert:
+ return ~dest;
+
+ case Regs::LogicOp::Nand:
+ return ~(src & dest);
+
+ case Regs::LogicOp::Or:
+ return src | dest;
+
+ case Regs::LogicOp::Nor:
+ return ~(src | dest);
+
+ case Regs::LogicOp::Xor:
+ return src ^ dest;
+
+ case Regs::LogicOp::Equiv:
+ return ~(src ^ dest);
+
+ case Regs::LogicOp::AndInverted:
+ return ~src & dest;
+
+ case Regs::LogicOp::OrReverse:
+ return src | ~dest;
+
+ case Regs::LogicOp::OrInverted:
+ return ~src | dest;
+ }
+ };
+
+ blend_output = Math::MakeVec(
+ LogicOp(combiner_output.r(), dest.r(), output_merger.logic_op),
+ LogicOp(combiner_output.g(), dest.g(), output_merger.logic_op),
+ LogicOp(combiner_output.b(), dest.b(), output_merger.logic_op),
+ LogicOp(combiner_output.a(), dest.a(), output_merger.logic_op));
+ }
+
+ const Math::Vec4<u8> result = {
+ output_merger.red_enable ? blend_output.r() : dest.r(),
+ output_merger.green_enable ? blend_output.g() : dest.g(),
+ output_merger.blue_enable ? blend_output.b() : dest.b(),
+ output_merger.alpha_enable ? blend_output.a() : dest.a()
+ };
+
+ DrawPixel(x >> 4, y >> 4, result);
+ }
+ }
+}
+
+void ProcessTriangle(const Shader::OutputVertex& v0,
+ const Shader::OutputVertex& v1,
+ const Shader::OutputVertex& v2) {
+ ProcessTriangleInternal(v0, v1, v2);
+}
+
+} // namespace Rasterizer
+
+} // namespace Pica
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Pica {
+
+namespace Shader {
+ struct OutputVertex;
+}
+
+namespace Rasterizer {
+
+void ProcessTriangle(const Shader::OutputVertex& v0,
+ const Shader::OutputVertex& v1,
+ const Shader::OutputVertex& v2);
+
+} // namespace Rasterizer
+
+} // namespace Pica
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include "citraimport\common/common_types.h"
+
+#include "citraimport\GPU\video_core/hwrasterizer_base.h"
+
+class EmuWindow;
+
+class RendererBase : NonCopyable {
+public:
+
+ /// Used to reference a framebuffer
+ enum kFramebuffer {
+ kFramebuffer_VirtualXFB = 0,
+ kFramebuffer_EFB,
+ kFramebuffer_Texture
+ };
+
+ RendererBase() : m_current_fps(0), m_current_frame(0) {
+ }
+
+ virtual ~RendererBase() {
+ }
+
+ /// Swap buffers (render frame)
+ virtual void SwapBuffers() = 0;
+
+ /**
+ * Set the emulator window to use for renderer
+ * @param window EmuWindow handle to emulator window to use for rendering
+ */
+ virtual void SetWindow(EmuWindow* window) = 0;
+
+ /// Initialize the renderer
+ virtual void Init() = 0;
+
+ /// Shutdown the renderer
+ virtual void ShutDown() = 0;
+
+ // Getter/setter functions:
+ // ------------------------
+
+ f32 GetCurrentframe() const {
+ return m_current_fps;
+ }
+
+ int current_frame() const {
+ return m_current_frame;
+ }
+
+ std::unique_ptr<HWRasterizer> hw_rasterizer;
+
+protected:
+ f32 m_current_fps; ///< Current framerate, should be set by the renderer
+ int m_current_frame; ///< Current frame, should be set by the renderer
+
+};
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include <memory>
+
+//#include <glad/glad.h>
+
+#include "citraimport\common/color.h"
+#include "citraimport\common/math_util.h"
+#include "citraimport\common/microprofile.h"
+#include "citraimport\common/profiler.h"
+
+
+#include "citraimport\GPU\video_core/pica.h"
+#include "citraimport\GPU\video_core/utils.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_rasterizer.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_shaders.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_shader_util.h"
+#include "citraimport\GPU\video_core/renderer_opengl/pica_to_gl.h"
+
+#include "citraimport\settings.h"
+
+u8* Mem_GetPhysicalPointer(u32 addr);
+
+static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) {
+ return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace &&
+ stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace &&
+ stage.color_source1 == Pica::Regs::TevStageConfig::Source::Previous &&
+ stage.alpha_source1 == Pica::Regs::TevStageConfig::Source::Previous &&
+ stage.color_modifier1 == Pica::Regs::TevStageConfig::ColorModifier::SourceColor &&
+ stage.alpha_modifier1 == Pica::Regs::TevStageConfig::AlphaModifier::SourceAlpha &&
+ stage.GetColorMultiplier() == 1 &&
+ stage.GetAlphaMultiplier() == 1);
+}
+
+RasterizerOpenGL::RasterizerOpenGL() : last_fb_color_addr(0), last_fb_depth_addr(0) { }
+RasterizerOpenGL::~RasterizerOpenGL() { }
+
+void RasterizerOpenGL::InitObjects() {
+ // Create the hardware shader program and get attrib/uniform locations
+ shader.Create(GLShaders::g_vertex_shader_hw, GLShaders::g_fragment_shader_hw);
+ attrib_position = glGetAttribLocation(shader.handle, "vert_position");
+ attrib_color = glGetAttribLocation(shader.handle, "vert_color");
+ attrib_texcoords = glGetAttribLocation(shader.handle, "vert_texcoords");
+
+ uniform_alphatest_enabled = glGetUniformLocation(shader.handle, "alphatest_enabled");
+ uniform_alphatest_func = glGetUniformLocation(shader.handle, "alphatest_func");
+ uniform_alphatest_ref = glGetUniformLocation(shader.handle, "alphatest_ref");
+
+ uniform_tex = glGetUniformLocation(shader.handle, "tex");
+
+ uniform_tev_combiner_buffer_color = glGetUniformLocation(shader.handle, "tev_combiner_buffer_color");
+
+ const auto tev_stages = Pica::g_state.regs.GetTevStages();
+ for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
+ auto& uniform_tev_cfg = uniform_tev_cfgs[tev_stage_index];
+
+ std::string tev_ref_str = "tev_cfgs[" + std::to_string(tev_stage_index) + "]";
+ uniform_tev_cfg.enabled = glGetUniformLocation(shader.handle, (tev_ref_str + ".enabled").c_str());
+ uniform_tev_cfg.color_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_sources").c_str());
+ uniform_tev_cfg.alpha_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_sources").c_str());
+ uniform_tev_cfg.color_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_modifiers").c_str());
+ uniform_tev_cfg.alpha_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_modifiers").c_str());
+ uniform_tev_cfg.color_alpha_op = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_op").c_str());
+ uniform_tev_cfg.color_alpha_multiplier = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_multiplier").c_str());
+ uniform_tev_cfg.const_color = glGetUniformLocation(shader.handle, (tev_ref_str + ".const_color").c_str());
+ uniform_tev_cfg.updates_combiner_buffer_color_alpha = glGetUniformLocation(shader.handle, (tev_ref_str + ".updates_combiner_buffer_color_alpha").c_str());
+ }
+
+ // Create sampler objects
+ for (int i = 0; i < texture_samplers.size(); ++i) {
+ texture_samplers[i].Create();
+ state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
+ }
+
+ // Generate VBO and VAO
+ vertex_buffer.Create();
+ vertex_array.Create();
+
+ // Update OpenGL state
+ state.draw.vertex_array = vertex_array.handle;
+ state.draw.vertex_buffer = vertex_buffer.handle;
+ state.draw.shader_program = shader.handle;
+
+ state.Apply();
+
+ // Set the texture samplers to correspond to different texture units
+ glUniform1i(uniform_tex, 0);
+ glUniform1i(uniform_tex + 1, 1);
+ glUniform1i(uniform_tex + 2, 2);
+
+ // Set vertex attributes
+ glVertexAttribPointer(attrib_position, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position));
+ glVertexAttribPointer(attrib_color, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color));
+ glVertexAttribPointer(attrib_texcoords, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0));
+ glVertexAttribPointer(attrib_texcoords + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1));
+ glVertexAttribPointer(attrib_texcoords + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2));
+ glEnableVertexAttribArray(attrib_position);
+ glEnableVertexAttribArray(attrib_color);
+ glEnableVertexAttribArray(attrib_texcoords);
+ glEnableVertexAttribArray(attrib_texcoords + 1);
+ glEnableVertexAttribArray(attrib_texcoords + 2);
+
+ // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation
+ fb_color_texture.texture.Create();
+ ReconfigureColorTexture(fb_color_texture, Pica::Regs::ColorFormat::RGBA8, 1, 1);
+
+ state.texture_units[0].texture_2d = fb_color_texture.texture.handle;
+ state.Apply();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ state.texture_units[0].texture_2d = 0;
+ state.Apply();
+
+ fb_depth_texture.texture.Create();
+ ReconfigureDepthTexture(fb_depth_texture, Pica::Regs::DepthFormat::D16, 1, 1);
+
+ state.texture_units[0].texture_2d = fb_depth_texture.texture.handle;
+ state.Apply();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
+
+ state.texture_units[0].texture_2d = 0;
+ state.Apply();
+
+ // Configure OpenGL framebuffer
+ framebuffer.Create();
+
+ state.draw.framebuffer = framebuffer.handle;
+ state.Apply();
+
+ glActiveTexture(GL_TEXTURE0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_color_texture.texture.handle, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.handle, 0);
+
+ assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE,
+ "OpenGL rasterizer framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER));
+}
+
+void RasterizerOpenGL::Reset() {
+ const auto& regs = Pica::g_state.regs;
+
+ SyncCullMode();
+ SyncBlendEnabled();
+ SyncBlendFuncs();
+ SyncBlendColor();
+ SyncAlphaTest();
+ SyncLogicOp();
+ SyncStencilTest();
+ SyncDepthTest();
+
+ // TEV stage 0
+ SyncTevSources(0, regs.tev_stage0);
+ SyncTevModifiers(0, regs.tev_stage0);
+ SyncTevOps(0, regs.tev_stage0);
+ SyncTevColor(0, regs.tev_stage0);
+ SyncTevMultipliers(0, regs.tev_stage0);
+
+ // TEV stage 1
+ SyncTevSources(1, regs.tev_stage1);
+ SyncTevModifiers(1, regs.tev_stage1);
+ SyncTevOps(1, regs.tev_stage1);
+ SyncTevColor(1, regs.tev_stage1);
+ SyncTevMultipliers(1, regs.tev_stage1);
+
+ // TEV stage 2
+ SyncTevSources(2, regs.tev_stage2);
+ SyncTevModifiers(2, regs.tev_stage2);
+ SyncTevOps(2, regs.tev_stage2);
+ SyncTevColor(2, regs.tev_stage2);
+ SyncTevMultipliers(2, regs.tev_stage2);
+
+ // TEV stage 3
+ SyncTevSources(3, regs.tev_stage3);
+ SyncTevModifiers(3, regs.tev_stage3);
+ SyncTevOps(3, regs.tev_stage3);
+ SyncTevColor(3, regs.tev_stage3);
+ SyncTevMultipliers(3, regs.tev_stage3);
+
+ // TEV stage 4
+ SyncTevSources(4, regs.tev_stage4);
+ SyncTevModifiers(4, regs.tev_stage4);
+ SyncTevOps(4, regs.tev_stage4);
+ SyncTevColor(4, regs.tev_stage4);
+ SyncTevMultipliers(4, regs.tev_stage4);
+
+ // TEV stage 5
+ SyncTevSources(5, regs.tev_stage5);
+ SyncTevModifiers(5, regs.tev_stage5);
+ SyncTevOps(5, regs.tev_stage5);
+ SyncTevColor(5, regs.tev_stage5);
+ SyncTevMultipliers(5, regs.tev_stage5);
+
+ SyncCombinerColor();
+ SyncCombinerWriteFlags();
+
+ res_cache.FullFlush();
+}
+
+void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0,
+ const Pica::Shader::OutputVertex& v1,
+ const Pica::Shader::OutputVertex& v2) {
+ vertex_batch.emplace_back(v0);
+ vertex_batch.emplace_back(v1);
+ vertex_batch.emplace_back(v2);
+}
+
+void RasterizerOpenGL::DrawTriangles() {
+ SyncFramebuffer();
+ SyncDrawState();
+
+ glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW);
+ glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size());
+
+ vertex_batch.clear();
+
+ // Flush the resource cache at the current depth and color framebuffer addresses for render-to-texture
+ const auto& regs = Pica::g_state.regs;
+
+ PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
+ u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format)
+ * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
+
+ PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress();
+ u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(regs.framebuffer.depth_format)
+ * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
+
+ res_cache.NotifyFlush(cur_fb_color_addr, cur_fb_color_size, true);
+ res_cache.NotifyFlush(cur_fb_depth_addr, cur_fb_depth_size, true);
+}
+
+void RasterizerOpenGL::CommitFramebuffer() {
+ CommitColorBuffer();
+ CommitDepthBuffer();
+}
+
+void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
+ const auto& regs = Pica::g_state.regs;
+
+ if (!Settings::values.use_hw_renderer)
+ return;
+
+ switch(id) {
+ // Culling
+ case PICA_REG_INDEX(cull_mode):
+ SyncCullMode();
+ break;
+
+ // Blending
+ case PICA_REG_INDEX(output_merger.alphablend_enable):
+ SyncBlendEnabled();
+ break;
+ case PICA_REG_INDEX(output_merger.alpha_blending):
+ SyncBlendFuncs();
+ break;
+ case PICA_REG_INDEX(output_merger.blend_const):
+ SyncBlendColor();
+ break;
+
+ // Alpha test
+ case PICA_REG_INDEX(output_merger.alpha_test):
+ SyncAlphaTest();
+ break;
+
+ // Stencil test
+ case PICA_REG_INDEX(output_merger.stencil_test.raw_func):
+ case PICA_REG_INDEX(output_merger.stencil_test.raw_op):
+ SyncStencilTest();
+ break;
+
+ // Depth test
+ case PICA_REG_INDEX(output_merger.depth_test_enable):
+ SyncDepthTest();
+ break;
+
+ // Logic op
+ case PICA_REG_INDEX(output_merger.logic_op):
+ SyncLogicOp();
+ break;
+
+ // TEV stage 0
+ case PICA_REG_INDEX(tev_stage0.color_source1):
+ SyncTevSources(0, regs.tev_stage0);
+ break;
+ case PICA_REG_INDEX(tev_stage0.color_modifier1):
+ SyncTevModifiers(0, regs.tev_stage0);
+ break;
+ case PICA_REG_INDEX(tev_stage0.color_op):
+ SyncTevOps(0, regs.tev_stage0);
+ break;
+ case PICA_REG_INDEX(tev_stage0.const_r):
+ SyncTevColor(0, regs.tev_stage0);
+ break;
+ case PICA_REG_INDEX(tev_stage0.color_scale):
+ SyncTevMultipliers(0, regs.tev_stage0);
+ break;
+
+ // TEV stage 1
+ case PICA_REG_INDEX(tev_stage1.color_source1):
+ SyncTevSources(1, regs.tev_stage1);
+ break;
+ case PICA_REG_INDEX(tev_stage1.color_modifier1):
+ SyncTevModifiers(1, regs.tev_stage1);
+ break;
+ case PICA_REG_INDEX(tev_stage1.color_op):
+ SyncTevOps(1, regs.tev_stage1);
+ break;
+ case PICA_REG_INDEX(tev_stage1.const_r):
+ SyncTevColor(1, regs.tev_stage1);
+ break;
+ case PICA_REG_INDEX(tev_stage1.color_scale):
+ SyncTevMultipliers(1, regs.tev_stage1);
+ break;
+
+ // TEV stage 2
+ case PICA_REG_INDEX(tev_stage2.color_source1):
+ SyncTevSources(2, regs.tev_stage2);
+ break;
+ case PICA_REG_INDEX(tev_stage2.color_modifier1):
+ SyncTevModifiers(2, regs.tev_stage2);
+ break;
+ case PICA_REG_INDEX(tev_stage2.color_op):
+ SyncTevOps(2, regs.tev_stage2);
+ break;
+ case PICA_REG_INDEX(tev_stage2.const_r):
+ SyncTevColor(2, regs.tev_stage2);
+ break;
+ case PICA_REG_INDEX(tev_stage2.color_scale):
+ SyncTevMultipliers(2, regs.tev_stage2);
+ break;
+
+ // TEV stage 3
+ case PICA_REG_INDEX(tev_stage3.color_source1):
+ SyncTevSources(3, regs.tev_stage3);
+ break;
+ case PICA_REG_INDEX(tev_stage3.color_modifier1):
+ SyncTevModifiers(3, regs.tev_stage3);
+ break;
+ case PICA_REG_INDEX(tev_stage3.color_op):
+ SyncTevOps(3, regs.tev_stage3);
+ break;
+ case PICA_REG_INDEX(tev_stage3.const_r):
+ SyncTevColor(3, regs.tev_stage3);
+ break;
+ case PICA_REG_INDEX(tev_stage3.color_scale):
+ SyncTevMultipliers(3, regs.tev_stage3);
+ break;
+
+ // TEV stage 4
+ case PICA_REG_INDEX(tev_stage4.color_source1):
+ SyncTevSources(4, regs.tev_stage4);
+ break;
+ case PICA_REG_INDEX(tev_stage4.color_modifier1):
+ SyncTevModifiers(4, regs.tev_stage4);
+ break;
+ case PICA_REG_INDEX(tev_stage4.color_op):
+ SyncTevOps(4, regs.tev_stage4);
+ break;
+ case PICA_REG_INDEX(tev_stage4.const_r):
+ SyncTevColor(4, regs.tev_stage4);
+ break;
+ case PICA_REG_INDEX(tev_stage4.color_scale):
+ SyncTevMultipliers(4, regs.tev_stage4);
+ break;
+
+ // TEV stage 5
+ case PICA_REG_INDEX(tev_stage5.color_source1):
+ SyncTevSources(5, regs.tev_stage5);
+ break;
+ case PICA_REG_INDEX(tev_stage5.color_modifier1):
+ SyncTevModifiers(5, regs.tev_stage5);
+ break;
+ case PICA_REG_INDEX(tev_stage5.color_op):
+ SyncTevOps(5, regs.tev_stage5);
+ break;
+ case PICA_REG_INDEX(tev_stage5.const_r):
+ SyncTevColor(5, regs.tev_stage5);
+ break;
+ case PICA_REG_INDEX(tev_stage5.color_scale):
+ SyncTevMultipliers(5, regs.tev_stage5);
+ break;
+
+ // TEV combiner buffer color
+ case PICA_REG_INDEX(tev_combiner_buffer_color):
+ SyncCombinerColor();
+ break;
+
+ // TEV combiner buffer write flags
+ case PICA_REG_INDEX(tev_combiner_buffer_input):
+ SyncCombinerWriteFlags();
+ break;
+ }
+}
+
+void RasterizerOpenGL::NotifyPreRead(PAddr addr, u32 size) {
+ const auto& regs = Pica::g_state.regs;
+
+ if (!Settings::values.use_hw_renderer)
+ return;
+
+ PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
+ u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format)
+ * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
+
+ PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress();
+ u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(regs.framebuffer.depth_format)
+ * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
+
+ // If source memory region overlaps 3DS framebuffers, commit them before the copy happens
+ if (MathUtil::IntervalsIntersect(addr, size, cur_fb_color_addr, cur_fb_color_size))
+ CommitColorBuffer();
+
+ if (MathUtil::IntervalsIntersect(addr, size, cur_fb_depth_addr, cur_fb_depth_size))
+ CommitDepthBuffer();
+}
+
+void RasterizerOpenGL::NotifyFlush(PAddr addr, u32 size) {
+ const auto& regs = Pica::g_state.regs;
+
+ if (!Settings::values.use_hw_renderer)
+ return;
+
+ PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
+ u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format)
+ * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
+
+ PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress();
+ u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(regs.framebuffer.depth_format)
+ * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
+
+ // If modified memory region overlaps 3DS framebuffers, reload their contents into OpenGL
+ if (MathUtil::IntervalsIntersect(addr, size, cur_fb_color_addr, cur_fb_color_size))
+ ReloadColorBuffer();
+
+ if (MathUtil::IntervalsIntersect(addr, size, cur_fb_depth_addr, cur_fb_depth_size))
+ ReloadDepthBuffer();
+
+ // Notify cache of flush in case the region touches a cached resource
+ res_cache.NotifyFlush(addr, size);
+}
+
+void RasterizerOpenGL::SamplerInfo::Create() {
+ sampler.Create();
+ mag_filter = min_filter = TextureConfig::Linear;
+ wrap_s = wrap_t = TextureConfig::Repeat;
+ border_color = 0;
+
+ glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // default is GL_LINEAR_MIPMAP_LINEAR
+ // Other attributes have correct defaults
+}
+
+void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Pica::Regs::TextureConfig& config) {
+ GLuint s = sampler.handle;
+
+ if (mag_filter != config.mag_filter) {
+ mag_filter = config.mag_filter;
+ glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureFilterMode(mag_filter));
+ }
+ if (min_filter != config.min_filter) {
+ min_filter = config.min_filter;
+ glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, PicaToGL::TextureFilterMode(min_filter));
+ }
+
+ if (wrap_s != config.wrap_s) {
+ wrap_s = config.wrap_s;
+ glSamplerParameteri(s, GL_TEXTURE_WRAP_S, PicaToGL::WrapMode(wrap_s));
+ }
+ if (wrap_t != config.wrap_t) {
+ wrap_t = config.wrap_t;
+ glSamplerParameteri(s, GL_TEXTURE_WRAP_T, PicaToGL::WrapMode(wrap_t));
+ }
+
+ if (wrap_s == TextureConfig::ClampToBorder || wrap_t == TextureConfig::ClampToBorder) {
+ if (border_color != config.border_color.raw) {
+ auto gl_color = PicaToGL::ColorRGBA8(border_color);
+ glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, gl_color.data());
+ }
+ }
+}
+
+void RasterizerOpenGL::ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height) {
+ GLint internal_format;
+
+ texture.format = format;
+ texture.width = width;
+ texture.height = height;
+
+ switch (format) {
+ case Pica::Regs::ColorFormat::RGBA8:
+ internal_format = GL_RGBA;
+ texture.gl_format = GL_RGBA;
+ texture.gl_type = GL_UNSIGNED_INT_8_8_8_8;
+ break;
+
+ case Pica::Regs::ColorFormat::RGB8:
+ // This pixel format uses BGR since GL_UNSIGNED_BYTE specifies byte-order, unlike every
+ // specific OpenGL type used in this function using native-endian (that is, little-endian
+ // mostly everywhere) for words or half-words.
+ // TODO: check how those behave on big-endian processors.
+ internal_format = GL_RGB;
+ texture.gl_format = GL_BGR;
+ texture.gl_type = GL_UNSIGNED_BYTE;
+ break;
+
+ case Pica::Regs::ColorFormat::RGB5A1:
+ internal_format = GL_RGBA;
+ texture.gl_format = GL_RGBA;
+ texture.gl_type = GL_UNSIGNED_SHORT_5_5_5_1;
+ break;
+
+ case Pica::Regs::ColorFormat::RGB565:
+ internal_format = GL_RGB;
+ texture.gl_format = GL_RGB;
+ texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+
+ case Pica::Regs::ColorFormat::RGBA4:
+ internal_format = GL_RGBA;
+ texture.gl_format = GL_RGBA;
+ texture.gl_type = GL_UNSIGNED_SHORT_4_4_4_4;
+ break;
+
+ default:
+ //LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer texture color format %x", format);
+ //UNIMPLEMENTED();
+ break;
+ }
+
+ state.texture_units[0].texture_2d = texture.texture.handle;
+ state.Apply();
+
+ glActiveTexture(GL_TEXTURE0);
+ glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0,
+ texture.gl_format, texture.gl_type, nullptr);
+
+ state.texture_units[0].texture_2d = 0;
+ state.Apply();
+}
+
+void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height) {
+ GLint internal_format;
+
+ texture.format = format;
+ texture.width = width;
+ texture.height = height;
+
+ switch (format) {
+ case Pica::Regs::DepthFormat::D16:
+ internal_format = GL_DEPTH_COMPONENT16;
+ texture.gl_format = GL_DEPTH_COMPONENT;
+ texture.gl_type = GL_UNSIGNED_SHORT;
+ break;
+
+ case Pica::Regs::DepthFormat::D24:
+ internal_format = GL_DEPTH_COMPONENT24;
+ texture.gl_format = GL_DEPTH_COMPONENT;
+ texture.gl_type = GL_UNSIGNED_INT;
+ break;
+
+ case Pica::Regs::DepthFormat::D24S8:
+ internal_format = GL_DEPTH24_STENCIL8;
+ texture.gl_format = GL_DEPTH_STENCIL;
+ texture.gl_type = GL_UNSIGNED_INT_24_8;
+ break;
+
+ default:
+ //LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer texture depth format %x", format);
+ //UNIMPLEMENTED();
+ break;
+ }
+
+ state.texture_units[0].texture_2d = texture.texture.handle;
+ state.Apply();
+
+ glActiveTexture(GL_TEXTURE0);
+ glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0,
+ texture.gl_format, texture.gl_type, nullptr);
+
+ state.texture_units[0].texture_2d = 0;
+ state.Apply();
+}
+
+void RasterizerOpenGL::SyncFramebuffer() {
+ const auto& regs = Pica::g_state.regs;
+
+ PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
+ Pica::Regs::ColorFormat new_fb_color_format = regs.framebuffer.color_format;
+
+ PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress();
+ Pica::Regs::DepthFormat new_fb_depth_format = regs.framebuffer.depth_format;
+
+ bool fb_size_changed = fb_color_texture.width != regs.framebuffer.GetWidth() ||
+ fb_color_texture.height != regs.framebuffer.GetHeight();
+
+ bool color_fb_prop_changed = fb_color_texture.format != new_fb_color_format ||
+ fb_size_changed;
+
+ bool depth_fb_prop_changed = fb_depth_texture.format != new_fb_depth_format ||
+ fb_size_changed;
+
+ bool color_fb_modified = last_fb_color_addr != cur_fb_color_addr ||
+ color_fb_prop_changed;
+
+ bool depth_fb_modified = last_fb_depth_addr != cur_fb_depth_addr ||
+ depth_fb_prop_changed;
+
+ // Commit if framebuffer modified in any way
+ if (color_fb_modified)
+ CommitColorBuffer();
+
+ if (depth_fb_modified)
+ CommitDepthBuffer();
+
+ // Reconfigure framebuffer textures if any property has changed
+ if (color_fb_prop_changed) {
+ ReconfigureColorTexture(fb_color_texture, new_fb_color_format,
+ regs.framebuffer.GetWidth(), regs.framebuffer.GetHeight());
+ }
+
+ if (depth_fb_prop_changed) {
+ ReconfigureDepthTexture(fb_depth_texture, new_fb_depth_format,
+ regs.framebuffer.GetWidth(), regs.framebuffer.GetHeight());
+
+ // Only attach depth buffer as stencil if it supports stencil
+ switch (new_fb_depth_format) {
+ case Pica::Regs::DepthFormat::D16:
+ case Pica::Regs::DepthFormat::D24:
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
+ break;
+
+ case Pica::Regs::DepthFormat::D24S8:
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.handle, 0);
+ break;
+
+ default:
+ //LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer depth format %x", new_fb_depth_format);
+ //UNIMPLEMENTED();
+ break;
+ }
+ }
+
+ // Load buffer data again if fb modified in any way
+ if (color_fb_modified) {
+ last_fb_color_addr = cur_fb_color_addr;
+
+ ReloadColorBuffer();
+ }
+
+ if (depth_fb_modified) {
+ last_fb_depth_addr = cur_fb_depth_addr;
+
+ ReloadDepthBuffer();
+ }
+}
+
+void RasterizerOpenGL::SyncCullMode() {
+ const auto& regs = Pica::g_state.regs;
+
+ switch (regs.cull_mode) {
+ case Pica::Regs::CullMode::KeepAll:
+ state.cull.enabled = false;
+ break;
+
+ case Pica::Regs::CullMode::KeepClockWise:
+ state.cull.enabled = true;
+ state.cull.mode = GL_BACK;
+ break;
+
+ case Pica::Regs::CullMode::KeepCounterClockWise:
+ state.cull.enabled = true;
+ state.cull.mode = GL_FRONT;
+ break;
+
+ default:
+ //LOG_CRITICAL(Render_OpenGL, "Unknown cull mode %d", regs.cull_mode.Value());
+ //UNIMPLEMENTED();
+ break;
+ }
+}
+
+void RasterizerOpenGL::SyncBlendEnabled() {
+ state.blend.enabled = (Pica::g_state.regs.output_merger.alphablend_enable == 1);
+}
+
+void RasterizerOpenGL::SyncBlendFuncs() {
+ const auto& regs = Pica::g_state.regs;
+ state.blend.src_rgb_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_rgb);
+ state.blend.dst_rgb_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_dest_rgb);
+ state.blend.src_a_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_a);
+ state.blend.dst_a_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_dest_a);
+}
+
+void RasterizerOpenGL::SyncBlendColor() {
+ auto blend_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.output_merger.blend_const.raw);
+ state.blend.color.red = blend_color[0];
+ state.blend.color.green = blend_color[1];
+ state.blend.color.blue = blend_color[2];
+ state.blend.color.alpha = blend_color[3];
+}
+
+void RasterizerOpenGL::SyncAlphaTest() {
+ const auto& regs = Pica::g_state.regs;
+ glUniform1i(uniform_alphatest_enabled, regs.output_merger.alpha_test.enable);
+ glUniform1i(uniform_alphatest_func, (GLint)regs.output_merger.alpha_test.func.Value());
+ glUniform1f(uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f);
+}
+
+void RasterizerOpenGL::SyncLogicOp() {
+ state.logic_op = PicaToGL::LogicOp(Pica::g_state.regs.output_merger.logic_op);
+}
+
+void RasterizerOpenGL::SyncStencilTest() {
+ const auto& regs = Pica::g_state.regs;
+ state.stencil.test_enabled = regs.output_merger.stencil_test.enable && regs.framebuffer.depth_format == Pica::Regs::DepthFormat::D24S8;
+ state.stencil.test_func = PicaToGL::CompareFunc(regs.output_merger.stencil_test.func);
+ state.stencil.test_ref = regs.output_merger.stencil_test.reference_value;
+ state.stencil.test_mask = regs.output_merger.stencil_test.input_mask;
+ state.stencil.write_mask = regs.output_merger.stencil_test.write_mask;
+ state.stencil.action_stencil_fail = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_stencil_fail);
+ state.stencil.action_depth_fail = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_fail);
+ state.stencil.action_depth_pass = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_pass);
+}
+
+void RasterizerOpenGL::SyncDepthTest() {
+ const auto& regs = Pica::g_state.regs;
+ state.depth.test_enabled = (regs.output_merger.depth_test_enable == 1);
+ state.depth.test_func = PicaToGL::CompareFunc(regs.output_merger.depth_test_func);
+ state.color_mask.red_enabled = regs.output_merger.red_enable;
+ state.color_mask.green_enabled = regs.output_merger.green_enable;
+ state.color_mask.blue_enabled = regs.output_merger.blue_enable;
+ state.color_mask.alpha_enabled = regs.output_merger.alpha_enable;
+ state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE;
+}
+
+void RasterizerOpenGL::SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
+ GLint color_srcs[3] = { (GLint)config.color_source1.Value(),
+ (GLint)config.color_source2.Value(),
+ (GLint)config.color_source3.Value() };
+ GLint alpha_srcs[3] = { (GLint)config.alpha_source1.Value(),
+ (GLint)config.alpha_source2.Value(),
+ (GLint)config.alpha_source3.Value() };
+
+ glUniform3iv(uniform_tev_cfgs[stage_index].color_sources, 1, color_srcs);
+ glUniform3iv(uniform_tev_cfgs[stage_index].alpha_sources, 1, alpha_srcs);
+}
+
+void RasterizerOpenGL::SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
+ GLint color_mods[3] = { (GLint)config.color_modifier1.Value(),
+ (GLint)config.color_modifier2.Value(),
+ (GLint)config.color_modifier3.Value() };
+ GLint alpha_mods[3] = { (GLint)config.alpha_modifier1.Value(),
+ (GLint)config.alpha_modifier2.Value(),
+ (GLint)config.alpha_modifier3.Value() };
+
+ glUniform3iv(uniform_tev_cfgs[stage_index].color_modifiers, 1, color_mods);
+ glUniform3iv(uniform_tev_cfgs[stage_index].alpha_modifiers, 1, alpha_mods);
+}
+
+void RasterizerOpenGL::SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
+ glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_op, (GLint)config.color_op.Value(), (GLint)config.alpha_op.Value());
+}
+
+void RasterizerOpenGL::SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
+ auto const_color = PicaToGL::ColorRGBA8(config.const_color);
+ glUniform4fv(uniform_tev_cfgs[stage_index].const_color, 1, const_color.data());
+}
+
+void RasterizerOpenGL::SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
+ glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_multiplier, config.GetColorMultiplier(), config.GetAlphaMultiplier());
+}
+
+void RasterizerOpenGL::SyncCombinerColor() {
+ auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw);
+ glUniform4fv(uniform_tev_combiner_buffer_color, 1, combiner_color.data());
+}
+
+void RasterizerOpenGL::SyncCombinerWriteFlags() {
+ const auto& regs = Pica::g_state.regs;
+ const auto tev_stages = regs.GetTevStages();
+ for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
+ glUniform2i(uniform_tev_cfgs[tev_stage_index].updates_combiner_buffer_color_alpha,
+ regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(tev_stage_index),
+ regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(tev_stage_index));
+ }
+}
+
+void RasterizerOpenGL::SyncDrawState() {
+ const auto& regs = Pica::g_state.regs;
+
+ // Sync the viewport
+ GLsizei viewport_width = (GLsizei)Pica::float24::FromRawFloat24(regs.viewport_size_x).ToFloat32() * 2;
+ GLsizei viewport_height = (GLsizei)Pica::float24::FromRawFloat24(regs.viewport_size_y).ToFloat32() * 2;
+
+ // OpenGL uses different y coordinates, so negate corner offset and flip origin
+ // TODO: Ensure viewport_corner.x should not be negated or origin flipped
+ // TODO: Use floating-point viewports for accuracy if supported
+ glViewport((GLsizei)static_cast<float>(regs.viewport_corner.x),
+ -(GLsizei)static_cast<float>(regs.viewport_corner.y)
+ + regs.framebuffer.GetHeight() - viewport_height,
+ viewport_width, viewport_height);
+
+ // Sync bound texture(s), upload if not cached
+ const auto pica_textures = regs.GetTextures();
+ for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
+ const auto& texture = pica_textures[texture_index];
+
+ if (texture.enabled) {
+ texture_samplers[texture_index].SyncWithConfig(texture.config);
+ res_cache.LoadAndBindTexture(state, texture_index, texture);
+ } else {
+ state.texture_units[texture_index].texture_2d = 0;
+ }
+ }
+
+ // Skip processing TEV stages that simply pass the previous stage results through
+ const auto tev_stages = regs.GetTevStages();
+ for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
+ glUniform1i(uniform_tev_cfgs[tev_stage_index].enabled, !IsPassThroughTevStage(tev_stages[tev_stage_index]));
+ }
+
+ state.Apply();
+}
+
+void RasterizerOpenGL::ReloadColorBuffer() {
+ u8* color_buffer = Mem_GetPhysicalPointer(Pica::g_state.regs.framebuffer.GetColorBufferPhysicalAddress());
+
+ if (color_buffer == nullptr)
+ return;
+
+ u32 bytes_per_pixel = Pica::Regs::BytesPerColorPixel(fb_color_texture.format);
+
+ std::unique_ptr<u8[]> temp_fb_color_buffer(new u8[fb_color_texture.width * fb_color_texture.height * bytes_per_pixel]);
+
+ // Directly copy pixels. Internal OpenGL color formats are consistent so no conversion is necessary.
+ for (int y = 0; y < fb_color_texture.height; ++y) {
+ for (int x = 0; x < fb_color_texture.width; ++x) {
+ const u32 coarse_y = y & ~7;
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel;
+ u32 gl_pixel_index = (x + y * fb_color_texture.width) * bytes_per_pixel;
+
+ u8* pixel = color_buffer + dst_offset;
+ memcpy(&temp_fb_color_buffer[gl_pixel_index], pixel, bytes_per_pixel);
+ }
+ }
+
+ state.texture_units[0].texture_2d = fb_color_texture.texture.handle;
+ state.Apply();
+
+ glActiveTexture(GL_TEXTURE0);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_color_texture.width, fb_color_texture.height,
+ fb_color_texture.gl_format, fb_color_texture.gl_type, temp_fb_color_buffer.get());
+
+ state.texture_units[0].texture_2d = 0;
+ state.Apply();
+}
+
+void RasterizerOpenGL::ReloadDepthBuffer() {
+ PAddr depth_buffer_addr = Pica::g_state.regs.framebuffer.GetDepthBufferPhysicalAddress();
+
+ if (depth_buffer_addr == 0)
+ return;
+
+ // TODO: Appears to work, but double-check endianness of depth values and order of depth-stencil
+ u8* depth_buffer = Mem_GetPhysicalPointer(depth_buffer_addr);
+
+ if (depth_buffer == nullptr)
+ return;
+
+ u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format);
+
+ // OpenGL needs 4 bpp alignment for D24
+ u32 gl_bpp = bytes_per_pixel == 3 ? 4 : bytes_per_pixel;
+
+ std::unique_ptr<u8[]> temp_fb_depth_buffer(new u8[fb_depth_texture.width * fb_depth_texture.height * gl_bpp]);
+
+ u8* temp_fb_depth_data = bytes_per_pixel == 3 ? (temp_fb_depth_buffer.get() + 1) : temp_fb_depth_buffer.get();
+
+ if (fb_depth_texture.format == Pica::Regs::DepthFormat::D24S8) {
+ for (int y = 0; y < fb_depth_texture.height; ++y) {
+ for (int x = 0; x < fb_depth_texture.width; ++x) {
+ const u32 coarse_y = y & ~7;
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
+ u32 gl_pixel_index = (x + y * fb_depth_texture.width);
+
+ u8* pixel = depth_buffer + dst_offset;
+ u32 depth_stencil = *(u32*)pixel;
+ ((u32*)temp_fb_depth_data)[gl_pixel_index] = (depth_stencil << 8) | (depth_stencil >> 24);
+ }
+ }
+ } else {
+ for (int y = 0; y < fb_depth_texture.height; ++y) {
+ for (int x = 0; x < fb_depth_texture.width; ++x) {
+ const u32 coarse_y = y & ~7;
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
+ u32 gl_pixel_index = (x + y * fb_depth_texture.width) * gl_bpp;
+
+ u8* pixel = depth_buffer + dst_offset;
+ memcpy(&temp_fb_depth_data[gl_pixel_index], pixel, bytes_per_pixel);
+ }
+ }
+ }
+
+ state.texture_units[0].texture_2d = fb_depth_texture.texture.handle;
+ state.Apply();
+
+ glActiveTexture(GL_TEXTURE0);
+ if (fb_depth_texture.format == Pica::Regs::DepthFormat::D24S8) {
+ // TODO(Subv): There is a bug with Intel Windows drivers that makes glTexSubImage2D not change the stencil buffer.
+ // The bug has been reported to Intel (https://communities.intel.com/message/324464)
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, fb_depth_texture.width, fb_depth_texture.height, 0,
+ GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, temp_fb_depth_buffer.get());
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_depth_texture.width, fb_depth_texture.height,
+ fb_depth_texture.gl_format, fb_depth_texture.gl_type, temp_fb_depth_buffer.get());
+ }
+
+ state.texture_units[0].texture_2d = 0;
+ state.Apply();
+}
+
+Common::Profiling::TimingCategory buffer_commit_category("Framebuffer Commit");
+
+void RasterizerOpenGL::CommitColorBuffer() {
+ if (last_fb_color_addr != 0) {
+ u8* color_buffer = Mem_GetPhysicalPointer(last_fb_color_addr);
+
+ if (color_buffer != nullptr) {
+ Common::Profiling::ScopeTimer timer(buffer_commit_category);
+
+ u32 bytes_per_pixel = Pica::Regs::BytesPerColorPixel(fb_color_texture.format);
+
+ std::unique_ptr<u8[]> temp_gl_color_buffer(new u8[fb_color_texture.width * fb_color_texture.height * bytes_per_pixel]);
+
+ state.texture_units[0].texture_2d = fb_color_texture.texture.handle;
+ state.Apply();
+
+ glActiveTexture(GL_TEXTURE0);
+ glGetTexImage(GL_TEXTURE_2D, 0, fb_color_texture.gl_format, fb_color_texture.gl_type, temp_gl_color_buffer.get());
+
+ state.texture_units[0].texture_2d = 0;
+ state.Apply();
+
+ // Directly copy pixels. Internal OpenGL color formats are consistent so no conversion is necessary.
+ for (int y = 0; y < fb_color_texture.height; ++y) {
+ for (int x = 0; x < fb_color_texture.width; ++x) {
+ const u32 coarse_y = y & ~7;
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel;
+ u32 gl_pixel_index = x * bytes_per_pixel + y * fb_color_texture.width * bytes_per_pixel;
+
+ u8* pixel = color_buffer + dst_offset;
+ memcpy(pixel, &temp_gl_color_buffer[gl_pixel_index], bytes_per_pixel);
+ }
+ }
+ }
+ }
+}
+
+void RasterizerOpenGL::CommitDepthBuffer() {
+ if (last_fb_depth_addr != 0) {
+ // TODO: Output seems correct visually, but doesn't quite match sw renderer output. One of them is wrong.
+ u8* depth_buffer = Mem_GetPhysicalPointer(last_fb_depth_addr);
+
+ if (depth_buffer != nullptr) {
+ Common::Profiling::ScopeTimer timer(buffer_commit_category);
+
+ u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format);
+
+ // OpenGL needs 4 bpp alignment for D24
+ u32 gl_bpp = bytes_per_pixel == 3 ? 4 : bytes_per_pixel;
+
+ std::unique_ptr<u8[]> temp_gl_depth_buffer(new u8[fb_depth_texture.width * fb_depth_texture.height * gl_bpp]);
+
+ state.texture_units[0].texture_2d = fb_depth_texture.texture.handle;
+ state.Apply();
+
+ glActiveTexture(GL_TEXTURE0);
+ glGetTexImage(GL_TEXTURE_2D, 0, fb_depth_texture.gl_format, fb_depth_texture.gl_type, temp_gl_depth_buffer.get());
+
+ state.texture_units[0].texture_2d = 0;
+ state.Apply();
+
+ u8* temp_gl_depth_data = bytes_per_pixel == 3 ? (temp_gl_depth_buffer.get() + 1) : temp_gl_depth_buffer.get();
+
+ if (fb_depth_texture.format == Pica::Regs::DepthFormat::D24S8) {
+ for (int y = 0; y < fb_depth_texture.height; ++y) {
+ for (int x = 0; x < fb_depth_texture.width; ++x) {
+ const u32 coarse_y = y & ~7;
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
+ u32 gl_pixel_index = (x + y * fb_depth_texture.width);
+
+ u8* pixel = depth_buffer + dst_offset;
+ u32 depth_stencil = ((u32*)temp_gl_depth_data)[gl_pixel_index];
+ *(u32*)pixel = (depth_stencil >> 8) | (depth_stencil << 24);
+ }
+ }
+ } else {
+ for (int y = 0; y < fb_depth_texture.height; ++y) {
+ for (int x = 0; x < fb_depth_texture.width; ++x) {
+ const u32 coarse_y = y & ~7;
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
+ u32 gl_pixel_index = (x + y * fb_depth_texture.width) * gl_bpp;
+
+ u8* pixel = depth_buffer + dst_offset;
+ memcpy(pixel, &temp_gl_depth_data[gl_pixel_index], bytes_per_pixel);
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+
+#include "citraimport\common/common_types.h"
+
+#include "citraimport\GPU\video_core/hwrasterizer_base.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_rasterizer_cache.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_state.h"
+#include "citraimport\GPU\video_core/shader/shader_interpreter.h"
+
+class RasterizerOpenGL : public HWRasterizer {
+public:
+
+ RasterizerOpenGL();
+ ~RasterizerOpenGL() override;
+
+ /// Initialize API-specific GPU objects
+ void InitObjects() override;
+
+ /// Reset the rasterizer, such as flushing all caches and updating all state
+ void Reset() override;
+
+ /// Queues the primitive formed by the given vertices for rendering
+ void AddTriangle(const Pica::Shader::OutputVertex& v0,
+ const Pica::Shader::OutputVertex& v1,
+ const Pica::Shader::OutputVertex& v2) override;
+
+ /// Draw the current batch of triangles
+ void DrawTriangles() override;
+
+ /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer
+ void CommitFramebuffer() override;
+
+ /// Notify rasterizer that the specified PICA register has been changed
+ void NotifyPicaRegisterChanged(u32 id) override;
+
+ /// Notify rasterizer that the specified 3DS memory region will be read from after this notification
+ void NotifyPreRead(PAddr addr, u32 size) override;
+
+ /// Notify rasterizer that a 3DS memory region has been changed
+ void NotifyFlush(PAddr addr, u32 size) override;
+
+private:
+ /// Structure used for managing texture environment states
+ struct TEVConfigUniforms {
+ GLuint enabled;
+ GLuint color_sources;
+ GLuint alpha_sources;
+ GLuint color_modifiers;
+ GLuint alpha_modifiers;
+ GLuint color_alpha_op;
+ GLuint color_alpha_multiplier;
+ GLuint const_color;
+ GLuint updates_combiner_buffer_color_alpha;
+ };
+
+ /// Structure used for storing information about color textures
+ struct TextureInfo {
+ OGLTexture texture;
+ GLsizei width;
+ GLsizei height;
+ Pica::Regs::ColorFormat format;
+ GLenum gl_format;
+ GLenum gl_type;
+ };
+
+ /// Structure used for storing information about depth textures
+ struct DepthTextureInfo {
+ OGLTexture texture;
+ GLsizei width;
+ GLsizei height;
+ Pica::Regs::DepthFormat format;
+ GLenum gl_format;
+ GLenum gl_type;
+ };
+
+ struct SamplerInfo {
+ using TextureConfig = Pica::Regs::TextureConfig;
+
+ OGLSampler sampler;
+
+ /// Creates the sampler object, initializing its state so that it's in sync with the SamplerInfo struct.
+ void Create();
+ /// Syncs the sampler object with the config, updating any necessary state.
+ void SyncWithConfig(const TextureConfig& config);
+
+ private:
+ TextureConfig::TextureFilter mag_filter;
+ TextureConfig::TextureFilter min_filter;
+ TextureConfig::WrapMode wrap_s;
+ TextureConfig::WrapMode wrap_t;
+ u32 border_color;
+ };
+
+ /// Structure that the hardware rendered vertices are composed of
+ struct HardwareVertex {
+ HardwareVertex(const Pica::Shader::OutputVertex& v) {
+ position[0] = v.pos.x.ToFloat32();
+ position[1] = v.pos.y.ToFloat32();
+ position[2] = v.pos.z.ToFloat32();
+ position[3] = v.pos.w.ToFloat32();
+ color[0] = v.color.x.ToFloat32();
+ color[1] = v.color.y.ToFloat32();
+ color[2] = v.color.z.ToFloat32();
+ color[3] = v.color.w.ToFloat32();
+ tex_coord0[0] = v.tc0.x.ToFloat32();
+ tex_coord0[1] = v.tc0.y.ToFloat32();
+ tex_coord1[0] = v.tc1.x.ToFloat32();
+ tex_coord1[1] = v.tc1.y.ToFloat32();
+ tex_coord2[0] = v.tc2.x.ToFloat32();
+ tex_coord2[1] = v.tc2.y.ToFloat32();
+ }
+
+ GLfloat position[4];
+ GLfloat color[4];
+ GLfloat tex_coord0[2];
+ GLfloat tex_coord1[2];
+ GLfloat tex_coord2[2];
+ };
+
+ /// Reconfigure the OpenGL color texture to use the given format and dimensions
+ void ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height);
+
+ /// Reconfigure the OpenGL depth texture to use the given format and dimensions
+ void ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height);
+
+ /// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer
+ void SyncFramebuffer();
+
+ /// Syncs the cull mode to match the PICA register
+ void SyncCullMode();
+
+ /// Syncs the blend enabled status to match the PICA register
+ void SyncBlendEnabled();
+
+ /// Syncs the blend functions to match the PICA register
+ void SyncBlendFuncs();
+
+ /// Syncs the blend color to match the PICA register
+ void SyncBlendColor();
+
+ /// Syncs the alpha test states to match the PICA register
+ void SyncAlphaTest();
+
+ /// Syncs the logic op states to match the PICA register
+ void SyncLogicOp();
+
+ /// Syncs the stencil test states to match the PICA register
+ void SyncStencilTest();
+
+ /// Syncs the depth test states to match the PICA register
+ void SyncDepthTest();
+
+ /// Syncs the specified TEV stage's color and alpha sources to match the PICA register
+ void SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
+
+ /// Syncs the specified TEV stage's color and alpha modifiers to match the PICA register
+ void SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
+
+ /// Syncs the specified TEV stage's color and alpha combiner operations to match the PICA register
+ void SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
+
+ /// Syncs the specified TEV stage's constant color to match the PICA register
+ void SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
+
+ /// Syncs the specified TEV stage's color and alpha multipliers to match the PICA register
+ void SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
+
+ /// Syncs the TEV combiner color buffer to match the PICA register
+ void SyncCombinerColor();
+
+ /// Syncs the TEV combiner write flags to match the PICA register
+ void SyncCombinerWriteFlags();
+
+ /// Syncs the remaining OpenGL drawing state to match the current PICA state
+ void SyncDrawState();
+
+ /// Copies the 3DS color framebuffer into the OpenGL color framebuffer texture
+ void ReloadColorBuffer();
+
+ /// Copies the 3DS depth framebuffer into the OpenGL depth framebuffer texture
+ void ReloadDepthBuffer();
+
+ /**
+ * Save the current OpenGL color framebuffer to the current PICA framebuffer in 3DS memory
+ * Loads the OpenGL framebuffer textures into temporary buffers
+ * Then copies into the 3DS framebuffer using proper Morton order
+ */
+ void CommitColorBuffer();
+
+ /**
+ * Save the current OpenGL depth framebuffer to the current PICA framebuffer in 3DS memory
+ * Loads the OpenGL framebuffer textures into temporary buffers
+ * Then copies into the 3DS framebuffer using proper Morton order
+ */
+ void CommitDepthBuffer();
+
+ RasterizerCacheOpenGL res_cache;
+
+ std::vector<HardwareVertex> vertex_batch;
+
+ OpenGLState state;
+
+ PAddr last_fb_color_addr;
+ PAddr last_fb_depth_addr;
+
+ // Hardware rasterizer
+ std::array<SamplerInfo, 3> texture_samplers;
+ TextureInfo fb_color_texture;
+ DepthTextureInfo fb_depth_texture;
+ OGLShader shader;
+ OGLVertexArray vertex_array;
+ OGLBuffer vertex_buffer;
+ OGLFramebuffer framebuffer;
+
+ // Hardware vertex shader
+ GLuint attrib_position;
+ GLuint attrib_color;
+ GLuint attrib_texcoords;
+
+ // Hardware fragment shader
+ GLuint uniform_alphatest_enabled;
+ GLuint uniform_alphatest_func;
+ GLuint uniform_alphatest_ref;
+ GLuint uniform_tex;
+ GLuint uniform_tev_combiner_buffer_color;
+ TEVConfigUniforms uniform_tev_cfgs[6];
+};
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#define NOMINMAX
+
+#include "citraimport\common/hash.h"
+#include "citraimport\common/make_unique.h"
+#include "citraimport\common/math_util.h"
+#include "citraimport\common/microprofile.h"
+#include "citraimport\common/vector_math.h"
+
+#include "citraimport\GPU\video_core/debug_utils/debug_utils.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_rasterizer_cache.h"
+#include "citraimport\GPU\video_core/renderer_opengl/pica_to_gl.h"
+
+u8* Mem_GetPhysicalPointer(u32 addr);
+
+RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
+ FullFlush();
+}
+
+void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::DebugUtils::TextureInfo& info) {
+ const auto cached_texture = texture_cache.find(info.physical_address);
+
+ if (cached_texture != texture_cache.end()) {
+ state.texture_units[texture_unit].texture_2d = cached_texture->second->texture.handle;
+ state.Apply();
+ } else {
+
+ std::unique_ptr<CachedTexture> new_texture = Common::make_unique<CachedTexture>();
+
+ new_texture->texture.Create();
+ state.texture_units[texture_unit].texture_2d = new_texture->texture.handle;
+ state.Apply();
+ glActiveTexture(GL_TEXTURE0 + texture_unit);
+
+ u8* texture_src_data = Mem_GetPhysicalPointer(info.physical_address);
+
+ new_texture->width = info.width;
+ new_texture->height = info.height;
+ new_texture->size = info.stride * info.height;
+ new_texture->addr = info.physical_address;
+ new_texture->hash = Common::ComputeHash64(texture_src_data, new_texture->size);
+
+ std::unique_ptr<Math::Vec4<u8>[]> temp_texture_buffer_rgba(new Math::Vec4<u8>[info.width * info.height]);
+
+ for (int y = 0; y < info.height; ++y) {
+ for (int x = 0; x < info.width; ++x) {
+ temp_texture_buffer_rgba[x + info.width * y] = Pica::DebugUtils::LookupTexture(texture_src_data, x, info.height - 1 - y, info);
+ }
+ }
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, info.width, info.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_texture_buffer_rgba.get());
+
+ texture_cache.emplace(info.physical_address, std::move(new_texture));
+ }
+}
+
+void RasterizerCacheOpenGL::NotifyFlush(PAddr addr, u32 size, bool ignore_hash) {
+ // Flush any texture that falls in the flushed region
+ // TODO: Optimize by also inserting upper bound (addr + size) of each texture into the same map and also narrow using lower_bound
+ auto cache_upper_bound = texture_cache.upper_bound(addr + size);
+
+ for (auto it = texture_cache.begin(); it != cache_upper_bound;) {
+ const auto& info = *it->second;
+
+ // Flush the texture only if the memory region intersects and a change is detected
+ if (MathUtil::IntervalsIntersect(addr, size, info.addr, info.size) &&
+ (ignore_hash || info.hash != Common::ComputeHash64(Mem_GetPhysicalPointer(info.addr), info.size))) {
+
+ it = texture_cache.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+void RasterizerCacheOpenGL::FullFlush() {
+ texture_cache.clear();
+}
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <map>
+#include <memory>
+
+#include "citraimport\GPU\video_core/pica.h"
+#include "citraimport\GPU\video_core/debug_utils/debug_utils.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_resource_manager.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_state.h"
+
+class RasterizerCacheOpenGL : NonCopyable {
+public:
+ ~RasterizerCacheOpenGL();
+
+ /// Loads a texture from 3DS memory to OpenGL and caches it (if not already cached)
+ void LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::DebugUtils::TextureInfo& info);
+
+ void LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config) {
+ LoadAndBindTexture(state, texture_unit, Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format));
+ }
+
+ /// Flush any cached resource that touches the flushed region
+ void NotifyFlush(PAddr addr, u32 size, bool ignore_hash = false);
+
+ /// Flush all cached OpenGL resources tracked by this cache manager
+ void FullFlush();
+
+private:
+ struct CachedTexture {
+ OGLTexture texture;
+ GLuint width;
+ GLuint height;
+ u32 size;
+ u64 hash;
+ PAddr addr;
+ };
+
+ std::map<PAddr, std::unique_ptr<CachedTexture>> texture_cache;
+};
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <utility>
+
+#include <citraimport\glad\include\glad/glad.h>
+
+#include "citraimport\common/common_types.h"
+
+#include "citraimport\GPU\video_core/renderer_opengl/gl_shader_util.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_state.h"
+
+class OGLTexture : private NonCopyable {
+public:
+ OGLTexture() = default;
+ OGLTexture(OGLTexture&& o) { std::swap(handle, o.handle); }
+ ~OGLTexture() { Release(); }
+ OGLTexture& operator=(OGLTexture&& o) { std::swap(handle, o.handle); return *this; }
+
+ /// Creates a new internal OpenGL resource and stores the handle
+ void Create() {
+ if (handle != 0) return;
+ glGenTextures(1, &handle);
+ }
+
+ /// Deletes the internal OpenGL resource
+ void Release() {
+ if (handle == 0) return;
+ glDeleteTextures(1, &handle);
+ OpenGLState::ResetTexture(handle);
+ handle = 0;
+ }
+
+ GLuint handle = 0;
+};
+
+class OGLSampler : private NonCopyable {
+public:
+ OGLSampler() = default;
+ OGLSampler(OGLSampler&& o) { std::swap(handle, o.handle); }
+ ~OGLSampler() { Release(); }
+ OGLSampler& operator=(OGLSampler&& o) { std::swap(handle, o.handle); return *this; }
+
+ /// Creates a new internal OpenGL resource and stores the handle
+ void Create() {
+ if (handle != 0) return;
+ glGenSamplers(1, &handle);
+ }
+
+ /// Deletes the internal OpenGL resource
+ void Release() {
+ if (handle == 0) return;
+ glDeleteSamplers(1, &handle);
+ OpenGLState::ResetSampler(handle);
+ handle = 0;
+ }
+
+ GLuint handle = 0;
+};
+
+class OGLShader : private NonCopyable {
+public:
+ OGLShader() = default;
+ OGLShader(OGLShader&& o) { std::swap(handle, o.handle); }
+ ~OGLShader() { Release(); }
+ OGLShader& operator=(OGLShader&& o) { std::swap(handle, o.handle); return *this; }
+
+ /// Creates a new internal OpenGL resource and stores the handle
+ void Create(const char* vert_shader, const char* frag_shader) {
+ if (handle != 0) return;
+ handle = ShaderUtil::LoadShaders(vert_shader, frag_shader);
+ }
+
+ /// Deletes the internal OpenGL resource
+ void Release() {
+ if (handle == 0) return;
+ glDeleteProgram(handle);
+ OpenGLState::ResetProgram(handle);
+ handle = 0;
+ }
+
+ GLuint handle = 0;
+};
+
+class OGLBuffer : private NonCopyable {
+public:
+ OGLBuffer() = default;
+ OGLBuffer(OGLBuffer&& o) { std::swap(handle, o.handle); }
+ ~OGLBuffer() { Release(); }
+ OGLBuffer& operator=(OGLBuffer&& o) { std::swap(handle, o.handle); return *this; }
+
+ /// Creates a new internal OpenGL resource and stores the handle
+ void Create() {
+ if (handle != 0) return;
+ glGenBuffers(1, &handle);
+ }
+
+ /// Deletes the internal OpenGL resource
+ void Release() {
+ if (handle == 0) return;
+ glDeleteBuffers(1, &handle);
+ OpenGLState::ResetBuffer(handle);
+ handle = 0;
+ }
+
+ GLuint handle = 0;
+};
+
+class OGLVertexArray : private NonCopyable {
+public:
+ OGLVertexArray() = default;
+ OGLVertexArray(OGLVertexArray&& o) { std::swap(handle, o.handle); }
+ ~OGLVertexArray() { Release(); }
+ OGLVertexArray& operator=(OGLVertexArray&& o) { std::swap(handle, o.handle); return *this; }
+
+ /// Creates a new internal OpenGL resource and stores the handle
+ void Create() {
+ if (handle != 0) return;
+ glGenVertexArrays(1, &handle);
+ }
+
+ /// Deletes the internal OpenGL resource
+ void Release() {
+ if (handle == 0) return;
+ glDeleteVertexArrays(1, &handle);
+ OpenGLState::ResetVertexArray(handle);
+ handle = 0;
+ }
+
+ GLuint handle = 0;
+};
+
+class OGLFramebuffer : private NonCopyable {
+public:
+ OGLFramebuffer() = default;
+ OGLFramebuffer(OGLFramebuffer&& o) { std::swap(handle, o.handle); }
+ ~OGLFramebuffer() { Release(); }
+ OGLFramebuffer& operator=(OGLFramebuffer&& o) { std::swap(handle, o.handle); return *this; }
+
+ /// Creates a new internal OpenGL resource and stores the handle
+ void Create() {
+ if (handle != 0) return;
+ glGenFramebuffers(1, &handle);
+ }
+
+ /// Deletes the internal OpenGL resource
+ void Release() {
+ if (handle == 0) return;
+ glDeleteFramebuffers(1, &handle);
+ OpenGLState::ResetFramebuffer(handle);
+ handle = 0;
+ }
+
+ GLuint handle = 0;
+};
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <vector>
+
+#include "log.h"
+
+#include "citraimport\GPU\video_core/renderer_opengl/gl_shader_util.h"
+
+namespace ShaderUtil {
+
+GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
+
+ // Create the shaders
+ GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
+ GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
+
+ GLint result = GL_FALSE;
+ int info_log_length;
+
+ // Compile Vertex Shader
+ LOG("Compiling vertex shader...");
+
+ glShaderSource(vertex_shader_id, 1, &vertex_shader, nullptr);
+ glCompileShader(vertex_shader_id);
+
+ // Check Vertex Shader
+ glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &result);
+ glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
+
+ if (info_log_length > 1) {
+ std::vector<char> vertex_shader_error(info_log_length);
+ glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]);
+ if (result) {
+ LOG("%s", &vertex_shader_error[0]);
+ } else {
+ LOG("Error compiling vertex shader:\n%s", &vertex_shader_error[0]);
+ }
+ }
+
+ // Compile Fragment Shader
+ LOG("Compiling fragment shader...");
+
+ glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr);
+ glCompileShader(fragment_shader_id);
+
+ // Check Fragment Shader
+ glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &result);
+ glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
+
+ if (info_log_length > 1) {
+ std::vector<char> fragment_shader_error(info_log_length);
+ glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr, &fragment_shader_error[0]);
+ if (result) {
+ LOG("%s", &fragment_shader_error[0]);
+ } else {
+ LOG("Error compiling fragment shader:\n%s", &fragment_shader_error[0]);
+ }
+ }
+
+ // Link the program
+ LOG("Linking program...");
+
+ GLuint program_id = glCreateProgram();
+ glAttachShader(program_id, vertex_shader_id);
+ glAttachShader(program_id, fragment_shader_id);
+ glLinkProgram(program_id);
+
+ // Check the program
+ glGetProgramiv(program_id, GL_LINK_STATUS, &result);
+ glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_log_length);
+
+ if (info_log_length > 1) {
+ std::vector<char> program_error(info_log_length);
+ glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]);
+ if (result) {
+ LOG("%s", &program_error[0]);
+ } else {
+ LOG("Error linking shader:\n%s", &program_error[0]);
+ }
+ }
+
+ glDeleteShader(vertex_shader_id);
+ glDeleteShader(fragment_shader_id);
+
+ return program_id;
+}
+
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <citraimport\glad\include\glad/glad.h>
+
+namespace ShaderUtil {
+
+GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path);
+
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace GLShaders {
+
+const char g_vertex_shader[] = R"(
+#version 150 core
+
+in vec2 vert_position;
+in vec2 vert_tex_coord;
+out vec2 frag_tex_coord;
+
+// This is a truncated 3x3 matrix for 2D transformations:
+// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
+// The third column performs translation.
+// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
+// implicitly be [0, 0, 1]
+uniform mat3x2 modelview_matrix;
+
+void main() {
+ // Multiply input position by the rotscale part of the matrix and then manually translate by
+ // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
+ // to `vec3(vert_position.xy, 1.0)`
+ gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
+ frag_tex_coord = vert_tex_coord;
+}
+)";
+
+const char g_fragment_shader[] = R"(
+#version 150 core
+
+in vec2 frag_tex_coord;
+out vec4 color;
+
+uniform sampler2D color_texture;
+
+void main() {
+ color = texture(color_texture, frag_tex_coord);
+}
+)";
+
+const char g_vertex_shader_hw[] = R"(
+#version 150 core
+
+#define NUM_VTX_ATTR 7
+
+in vec4 vert_position;
+in vec4 vert_color;
+in vec2 vert_texcoords[3];
+
+out vec4 o[NUM_VTX_ATTR];
+
+void main() {
+ o[2] = vert_color;
+ o[3] = vec4(vert_texcoords[0].xy, vert_texcoords[1].xy);
+ o[5] = vec4(0.0, 0.0, vert_texcoords[2].xy);
+
+ gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w);
+}
+)";
+
+// TODO: Create a shader constructor and cache that builds this program with minimal conditionals instead of using tev_cfg uniforms
+const char g_fragment_shader_hw[] = R"(
+#version 150 core
+
+#define NUM_VTX_ATTR 7
+#define NUM_TEV_STAGES 6
+
+#define SOURCE_PRIMARYCOLOR 0x0
+#define SOURCE_PRIMARYFRAGMENTCOLOR 0x1
+#define SOURCE_SECONDARYFRAGMENTCOLOR 0x2
+#define SOURCE_TEXTURE0 0x3
+#define SOURCE_TEXTURE1 0x4
+#define SOURCE_TEXTURE2 0x5
+#define SOURCE_TEXTURE3 0x6
+#define SOURCE_PREVIOUSBUFFER 0xd
+#define SOURCE_CONSTANT 0xe
+#define SOURCE_PREVIOUS 0xf
+
+#define COLORMODIFIER_SOURCECOLOR 0x0
+#define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1
+#define COLORMODIFIER_SOURCEALPHA 0x2
+#define COLORMODIFIER_ONEMINUSSOURCEALPHA 0x3
+#define COLORMODIFIER_SOURCERED 0x4
+#define COLORMODIFIER_ONEMINUSSOURCERED 0x5
+#define COLORMODIFIER_SOURCEGREEN 0x8
+#define COLORMODIFIER_ONEMINUSSOURCEGREEN 0x9
+#define COLORMODIFIER_SOURCEBLUE 0xc
+#define COLORMODIFIER_ONEMINUSSOURCEBLUE 0xd
+
+#define ALPHAMODIFIER_SOURCEALPHA 0x0
+#define ALPHAMODIFIER_ONEMINUSSOURCEALPHA 0x1
+#define ALPHAMODIFIER_SOURCERED 0x2
+#define ALPHAMODIFIER_ONEMINUSSOURCERED 0x3
+#define ALPHAMODIFIER_SOURCEGREEN 0x4
+#define ALPHAMODIFIER_ONEMINUSSOURCEGREEN 0x5
+#define ALPHAMODIFIER_SOURCEBLUE 0x6
+#define ALPHAMODIFIER_ONEMINUSSOURCEBLUE 0x7
+
+#define OPERATION_REPLACE 0
+#define OPERATION_MODULATE 1
+#define OPERATION_ADD 2
+#define OPERATION_ADDSIGNED 3
+#define OPERATION_LERP 4
+#define OPERATION_SUBTRACT 5
+#define OPERATION_MULTIPLYTHENADD 8
+#define OPERATION_ADDTHENMULTIPLY 9
+
+#define COMPAREFUNC_NEVER 0
+#define COMPAREFUNC_ALWAYS 1
+#define COMPAREFUNC_EQUAL 2
+#define COMPAREFUNC_NOTEQUAL 3
+#define COMPAREFUNC_LESSTHAN 4
+#define COMPAREFUNC_LESSTHANOREQUAL 5
+#define COMPAREFUNC_GREATERTHAN 6
+#define COMPAREFUNC_GREATERTHANOREQUAL 7
+
+in vec4 o[NUM_VTX_ATTR];
+out vec4 color;
+
+uniform bool alphatest_enabled;
+uniform int alphatest_func;
+uniform float alphatest_ref;
+
+uniform sampler2D tex[3];
+
+uniform vec4 tev_combiner_buffer_color;
+
+struct TEVConfig
+{
+ bool enabled;
+ ivec3 color_sources;
+ ivec3 alpha_sources;
+ ivec3 color_modifiers;
+ ivec3 alpha_modifiers;
+ ivec2 color_alpha_op;
+ ivec2 color_alpha_multiplier;
+ vec4 const_color;
+ bvec2 updates_combiner_buffer_color_alpha;
+};
+
+uniform TEVConfig tev_cfgs[NUM_TEV_STAGES];
+
+vec4 g_combiner_buffer;
+vec4 g_last_tex_env_out;
+vec4 g_const_color;
+
+vec4 GetSource(int source) {
+ if (source == SOURCE_PRIMARYCOLOR) {
+ return o[2];
+ } else if (source == SOURCE_PRIMARYFRAGMENTCOLOR) {
+ // HACK: Until we implement fragment lighting, use primary_color
+ return o[2];
+ } else if (source == SOURCE_SECONDARYFRAGMENTCOLOR) {
+ // HACK: Until we implement fragment lighting, use zero
+ return vec4(0.0, 0.0, 0.0, 0.0);
+ } else if (source == SOURCE_TEXTURE0) {
+ return texture(tex[0], o[3].xy);
+ } else if (source == SOURCE_TEXTURE1) {
+ return texture(tex[1], o[3].zw);
+ } else if (source == SOURCE_TEXTURE2) {
+ // TODO: Unverified
+ return texture(tex[2], o[5].zw);
+ } else if (source == SOURCE_TEXTURE3) {
+ // TODO: no 4th texture?
+ } else if (source == SOURCE_PREVIOUSBUFFER) {
+ return g_combiner_buffer;
+ } else if (source == SOURCE_CONSTANT) {
+ return g_const_color;
+ } else if (source == SOURCE_PREVIOUS) {
+ return g_last_tex_env_out;
+ }
+
+ return vec4(0.0);
+}
+
+vec3 GetColorModifier(int factor, vec4 color) {
+ if (factor == COLORMODIFIER_SOURCECOLOR) {
+ return color.rgb;
+ } else if (factor == COLORMODIFIER_ONEMINUSSOURCECOLOR) {
+ return vec3(1.0) - color.rgb;
+ } else if (factor == COLORMODIFIER_SOURCEALPHA) {
+ return color.aaa;
+ } else if (factor == COLORMODIFIER_ONEMINUSSOURCEALPHA) {
+ return vec3(1.0) - color.aaa;
+ } else if (factor == COLORMODIFIER_SOURCERED) {
+ return color.rrr;
+ } else if (factor == COLORMODIFIER_ONEMINUSSOURCERED) {
+ return vec3(1.0) - color.rrr;
+ } else if (factor == COLORMODIFIER_SOURCEGREEN) {
+ return color.ggg;
+ } else if (factor == COLORMODIFIER_ONEMINUSSOURCEGREEN) {
+ return vec3(1.0) - color.ggg;
+ } else if (factor == COLORMODIFIER_SOURCEBLUE) {
+ return color.bbb;
+ } else if (factor == COLORMODIFIER_ONEMINUSSOURCEBLUE) {
+ return vec3(1.0) - color.bbb;
+ }
+
+ return vec3(0.0);
+}
+
+float GetAlphaModifier(int factor, vec4 color) {
+ if (factor == ALPHAMODIFIER_SOURCEALPHA) {
+ return color.a;
+ } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEALPHA) {
+ return 1.0 - color.a;
+ } else if (factor == ALPHAMODIFIER_SOURCERED) {
+ return color.r;
+ } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCERED) {
+ return 1.0 - color.r;
+ } else if (factor == ALPHAMODIFIER_SOURCEGREEN) {
+ return color.g;
+ } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEGREEN) {
+ return 1.0 - color.g;
+ } else if (factor == ALPHAMODIFIER_SOURCEBLUE) {
+ return color.b;
+ } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEBLUE) {
+ return 1.0 - color.b;
+ }
+
+ return 0.0;
+}
+
+vec3 ColorCombine(int op, vec3 color[3]) {
+ if (op == OPERATION_REPLACE) {
+ return color[0];
+ } else if (op == OPERATION_MODULATE) {
+ return color[0] * color[1];
+ } else if (op == OPERATION_ADD) {
+ return min(color[0] + color[1], 1.0);
+ } else if (op == OPERATION_ADDSIGNED) {
+ return clamp(color[0] + color[1] - vec3(0.5), 0.0, 1.0);
+ } else if (op == OPERATION_LERP) {
+ return color[0] * color[2] + color[1] * (vec3(1.0) - color[2]);
+ } else if (op == OPERATION_SUBTRACT) {
+ return max(color[0] - color[1], 0.0);
+ } else if (op == OPERATION_MULTIPLYTHENADD) {
+ return min(color[0] * color[1] + color[2], 1.0);
+ } else if (op == OPERATION_ADDTHENMULTIPLY) {
+ return min(color[0] + color[1], 1.0) * color[2];
+ }
+
+ return vec3(0.0);
+}
+
+float AlphaCombine(int op, float alpha[3]) {
+ if (op == OPERATION_REPLACE) {
+ return alpha[0];
+ } else if (op == OPERATION_MODULATE) {
+ return alpha[0] * alpha[1];
+ } else if (op == OPERATION_ADD) {
+ return min(alpha[0] + alpha[1], 1.0);
+ } else if (op == OPERATION_ADDSIGNED) {
+ return clamp(alpha[0] + alpha[1] - 0.5, 0.0, 1.0);
+ } else if (op == OPERATION_LERP) {
+ return alpha[0] * alpha[2] + alpha[1] * (1.0 - alpha[2]);
+ } else if (op == OPERATION_SUBTRACT) {
+ return max(alpha[0] - alpha[1], 0.0);
+ } else if (op == OPERATION_MULTIPLYTHENADD) {
+ return min(alpha[0] * alpha[1] + alpha[2], 1.0);
+ } else if (op == OPERATION_ADDTHENMULTIPLY) {
+ return min(alpha[0] + alpha[1], 1.0) * alpha[2];
+ }
+
+ return 0.0;
+}
+
+void main(void) {
+ g_combiner_buffer = tev_combiner_buffer_color;
+
+ for (int tex_env_idx = 0; tex_env_idx < NUM_TEV_STAGES; ++tex_env_idx) {
+ if (tev_cfgs[tex_env_idx].enabled) {
+ g_const_color = tev_cfgs[tex_env_idx].const_color;
+
+ vec3 color_results[3] = vec3[3](GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.x, GetSource(tev_cfgs[tex_env_idx].color_sources.x)),
+ GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.y, GetSource(tev_cfgs[tex_env_idx].color_sources.y)),
+ GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.z, GetSource(tev_cfgs[tex_env_idx].color_sources.z)));
+ vec3 color_output = ColorCombine(tev_cfgs[tex_env_idx].color_alpha_op.x, color_results);
+
+ float alpha_results[3] = float[3](GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.x, GetSource(tev_cfgs[tex_env_idx].alpha_sources.x)),
+ GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.y, GetSource(tev_cfgs[tex_env_idx].alpha_sources.y)),
+ GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.z, GetSource(tev_cfgs[tex_env_idx].alpha_sources.z)));
+ float alpha_output = AlphaCombine(tev_cfgs[tex_env_idx].color_alpha_op.y, alpha_results);
+
+ g_last_tex_env_out = vec4(min(color_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.x, 1.0), min(alpha_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.y, 1.0));
+ }
+
+ if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.x) {
+ g_combiner_buffer.rgb = g_last_tex_env_out.rgb;
+ }
+
+ if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.y) {
+ g_combiner_buffer.a = g_last_tex_env_out.a;
+ }
+ }
+
+ if (alphatest_enabled) {
+ if (alphatest_func == COMPAREFUNC_NEVER) {
+ discard;
+ } else if (alphatest_func == COMPAREFUNC_ALWAYS) {
+
+ } else if (alphatest_func == COMPAREFUNC_EQUAL) {
+ if (g_last_tex_env_out.a != alphatest_ref) {
+ discard;
+ }
+ } else if (alphatest_func == COMPAREFUNC_NOTEQUAL) {
+ if (g_last_tex_env_out.a == alphatest_ref) {
+ discard;
+ }
+ } else if (alphatest_func == COMPAREFUNC_LESSTHAN) {
+ if (g_last_tex_env_out.a >= alphatest_ref) {
+ discard;
+ }
+ } else if (alphatest_func == COMPAREFUNC_LESSTHANOREQUAL) {
+ if (g_last_tex_env_out.a > alphatest_ref) {
+ discard;
+ }
+ } else if (alphatest_func == COMPAREFUNC_GREATERTHAN) {
+ if (g_last_tex_env_out.a <= alphatest_ref) {
+ discard;
+ }
+ } else if (alphatest_func == COMPAREFUNC_GREATERTHANOREQUAL) {
+ if (g_last_tex_env_out.a < alphatest_ref) {
+ discard;
+ }
+ }
+ }
+
+ color = g_last_tex_env_out;
+}
+)";
+
+}
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "citraimport\GPU\video_core/pica.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_state.h"
+
+OpenGLState OpenGLState::cur_state;
+
+OpenGLState::OpenGLState() {
+ // These all match default OpenGL values
+ cull.enabled = false;
+ cull.mode = GL_BACK;
+
+ depth.test_enabled = false;
+ depth.test_func = GL_LESS;
+ depth.write_mask = GL_TRUE;
+
+ color_mask.red_enabled = GL_TRUE;
+ color_mask.green_enabled = GL_TRUE;
+ color_mask.blue_enabled = GL_TRUE;
+ color_mask.alpha_enabled = GL_TRUE;
+
+ stencil.test_enabled = false;
+ stencil.test_func = GL_ALWAYS;
+ stencil.test_ref = 0;
+ stencil.test_mask = -1;
+ stencil.write_mask = -1;
+ stencil.action_depth_fail = GL_KEEP;
+ stencil.action_depth_pass = GL_KEEP;
+ stencil.action_stencil_fail = GL_KEEP;
+
+ blend.enabled = false;
+ blend.src_rgb_func = GL_ONE;
+ blend.dst_rgb_func = GL_ZERO;
+ blend.src_a_func = GL_ONE;
+ blend.dst_a_func = GL_ZERO;
+ blend.color.red = 0.0f;
+ blend.color.green = 0.0f;
+ blend.color.blue = 0.0f;
+ blend.color.alpha = 0.0f;
+
+ logic_op = GL_COPY;
+
+ for (auto& texture_unit : texture_units) {
+ texture_unit.texture_2d = 0;
+ texture_unit.sampler = 0;
+ }
+
+ draw.framebuffer = 0;
+ draw.vertex_array = 0;
+ draw.vertex_buffer = 0;
+ draw.shader_program = 0;
+}
+
+void OpenGLState::Apply() {
+ // Culling
+ if (cull.enabled != cur_state.cull.enabled) {
+ if (cull.enabled) {
+ glEnable(GL_CULL_FACE);
+ } else {
+ glDisable(GL_CULL_FACE);
+ }
+ }
+
+ if (cull.mode != cur_state.cull.mode) {
+ glCullFace(cull.mode);
+ }
+
+ // Depth test
+ if (depth.test_enabled != cur_state.depth.test_enabled) {
+ if (depth.test_enabled) {
+ glEnable(GL_DEPTH_TEST);
+ } else {
+ glDisable(GL_DEPTH_TEST);
+ }
+ }
+
+ if (depth.test_func != cur_state.depth.test_func) {
+ glDepthFunc(depth.test_func);
+ }
+
+ // Depth mask
+ if (depth.write_mask != cur_state.depth.write_mask) {
+ glDepthMask(depth.write_mask);
+ }
+
+ // Color mask
+ if (color_mask.red_enabled != cur_state.color_mask.red_enabled ||
+ color_mask.green_enabled != cur_state.color_mask.green_enabled ||
+ color_mask.blue_enabled != cur_state.color_mask.blue_enabled ||
+ color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) {
+ glColorMask(color_mask.red_enabled, color_mask.green_enabled,
+ color_mask.blue_enabled, color_mask.alpha_enabled);
+ }
+
+ // Stencil test
+ if (stencil.test_enabled != cur_state.stencil.test_enabled) {
+ if (stencil.test_enabled) {
+ glEnable(GL_STENCIL_TEST);
+ } else {
+ glDisable(GL_STENCIL_TEST);
+ }
+ }
+
+ if (stencil.test_func != cur_state.stencil.test_func ||
+ stencil.test_ref != cur_state.stencil.test_ref ||
+ stencil.test_mask != cur_state.stencil.test_mask) {
+ glStencilFunc(stencil.test_func, stencil.test_ref, stencil.test_mask);
+ }
+
+ if (stencil.action_depth_fail != cur_state.stencil.action_depth_fail ||
+ stencil.action_depth_pass != cur_state.stencil.action_depth_pass ||
+ stencil.action_stencil_fail != cur_state.stencil.action_stencil_fail) {
+ glStencilOp(stencil.action_stencil_fail, stencil.action_depth_fail, stencil.action_depth_pass);
+ }
+
+ // Stencil mask
+ if (stencil.write_mask != cur_state.stencil.write_mask) {
+ glStencilMask(stencil.write_mask);
+ }
+
+ // Blending
+ if (blend.enabled != cur_state.blend.enabled) {
+ if (blend.enabled) {
+ glEnable(GL_BLEND);
+
+ cur_state.logic_op = GL_COPY;
+ glLogicOp(cur_state.logic_op);
+ glDisable(GL_COLOR_LOGIC_OP);
+ } else {
+ glDisable(GL_BLEND);
+ glEnable(GL_COLOR_LOGIC_OP);
+ }
+ }
+
+ if (blend.color.red != cur_state.blend.color.red ||
+ blend.color.green != cur_state.blend.color.green ||
+ blend.color.blue != cur_state.blend.color.blue ||
+ blend.color.alpha != cur_state.blend.color.alpha) {
+ glBlendColor(blend.color.red, blend.color.green,
+ blend.color.blue, blend.color.alpha);
+ }
+
+ if (blend.src_rgb_func != cur_state.blend.src_rgb_func ||
+ blend.dst_rgb_func != cur_state.blend.dst_rgb_func ||
+ blend.src_a_func != cur_state.blend.src_a_func ||
+ blend.dst_a_func != cur_state.blend.dst_a_func) {
+ glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func,
+ blend.src_a_func, blend.dst_a_func);
+ }
+
+ if (logic_op != cur_state.logic_op) {
+ glLogicOp(logic_op);
+ }
+
+ // Textures
+ for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) {
+ if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) {
+ glActiveTexture(GL_TEXTURE0 + i);
+ glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d);
+ }
+ if (texture_units[i].sampler != cur_state.texture_units[i].sampler) {
+ glBindSampler(i, texture_units[i].sampler);
+ }
+ }
+
+ // Framebuffer
+ if (draw.framebuffer != cur_state.draw.framebuffer) {
+ glBindFramebuffer(GL_FRAMEBUFFER, draw.framebuffer);
+ }
+
+ // Vertex array
+ if (draw.vertex_array != cur_state.draw.vertex_array) {
+ glBindVertexArray(draw.vertex_array);
+ }
+
+ // Vertex buffer
+ if (draw.vertex_buffer != cur_state.draw.vertex_buffer) {
+ glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer);
+ }
+
+ // Shader program
+ if (draw.shader_program != cur_state.draw.shader_program) {
+ glUseProgram(draw.shader_program);
+ }
+
+ cur_state = *this;
+}
+
+void OpenGLState::ResetTexture(GLuint id) {
+ for (auto& unit : cur_state.texture_units) {
+ if (unit.texture_2d == id) {
+ unit.texture_2d = 0;
+ }
+ }
+}
+
+void OpenGLState::ResetSampler(GLuint id) {
+ for (auto& unit : cur_state.texture_units) {
+ if (unit.sampler == id) {
+ unit.sampler = 0;
+ }
+ }
+}
+
+void OpenGLState::ResetProgram(GLuint id) {
+ if (cur_state.draw.shader_program == id) {
+ cur_state.draw.shader_program = 0;
+ }
+}
+
+void OpenGLState::ResetBuffer(GLuint id) {
+ if (cur_state.draw.vertex_buffer == id) {
+ cur_state.draw.vertex_buffer = 0;
+ }
+}
+
+void OpenGLState::ResetVertexArray(GLuint id) {
+ if (cur_state.draw.vertex_array == id) {
+ cur_state.draw.vertex_array = 0;
+ }
+}
+
+void OpenGLState::ResetFramebuffer(GLuint id) {
+ if (cur_state.draw.framebuffer == id) {
+ cur_state.draw.framebuffer = 0;
+ }
+}
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <citraimport\glad\include\glad/glad.h>
+
+class OpenGLState {
+public:
+ struct {
+ bool enabled; // GL_CULL_FACE
+ GLenum mode; // GL_CULL_FACE_MODE
+ } cull;
+
+ struct {
+ bool test_enabled; // GL_DEPTH_TEST
+ GLenum test_func; // GL_DEPTH_FUNC
+ GLboolean write_mask; // GL_DEPTH_WRITEMASK
+ } depth;
+
+ struct {
+ GLboolean red_enabled;
+ GLboolean green_enabled;
+ GLboolean blue_enabled;
+ GLboolean alpha_enabled;
+ } color_mask; // GL_COLOR_WRITEMASK
+
+ struct {
+ bool test_enabled; // GL_STENCIL_TEST
+ GLenum test_func; // GL_STENCIL_FUNC
+ GLint test_ref; // GL_STENCIL_REF
+ GLuint test_mask; // GL_STENCIL_VALUE_MASK
+ GLuint write_mask; // GL_STENCIL_WRITEMASK
+ GLenum action_stencil_fail; // GL_STENCIL_FAIL
+ GLenum action_depth_fail; // GL_STENCIL_PASS_DEPTH_FAIL
+ GLenum action_depth_pass; // GL_STENCIL_PASS_DEPTH_PASS
+ } stencil;
+
+ struct {
+ bool enabled; // GL_BLEND
+ GLenum src_rgb_func; // GL_BLEND_SRC_RGB
+ GLenum dst_rgb_func; // GL_BLEND_DST_RGB
+ GLenum src_a_func; // GL_BLEND_SRC_ALPHA
+ GLenum dst_a_func; // GL_BLEND_DST_ALPHA
+
+ struct {
+ GLclampf red;
+ GLclampf green;
+ GLclampf blue;
+ GLclampf alpha;
+ } color; // GL_BLEND_COLOR
+ } blend;
+
+ GLenum logic_op; // GL_LOGIC_OP_MODE
+
+ // 3 texture units - one for each that is used in PICA fragment shader emulation
+ struct {
+ GLuint texture_2d; // GL_TEXTURE_BINDING_2D
+ GLuint sampler; // GL_SAMPLER_BINDING
+ } texture_units[3];
+
+ struct {
+ GLuint framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING
+ GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
+ GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
+ GLuint shader_program; // GL_CURRENT_PROGRAM
+ } draw;
+
+ OpenGLState();
+
+ /// Get the currently active OpenGL state
+ static const OpenGLState& GetCurState() {
+ return cur_state;
+ }
+
+ /// Apply this state as the current OpenGL state
+ void Apply();
+
+ static void ResetTexture(GLuint id);
+ static void ResetSampler(GLuint id);
+ static void ResetProgram(GLuint id);
+ static void ResetBuffer(GLuint id);
+ static void ResetVertexArray(GLuint id);
+ static void ResetFramebuffer(GLuint id);
+
+private:
+ static OpenGLState cur_state;
+};
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <citraimport\glad\include/glad/glad.h>
+
+#include "citraimport\common/common_types.h"
+
+#include "citraimport\GPU\video_core/pica.h"
+
+namespace PicaToGL {
+
+inline GLenum TextureFilterMode(Pica::Regs::TextureConfig::TextureFilter mode) {
+ static const GLenum filter_mode_table[] = {
+ GL_NEAREST, // TextureFilter::Nearest
+ GL_LINEAR // TextureFilter::Linear
+ };
+
+ // Range check table for input
+ if (mode >= ARRAY_SIZE(filter_mode_table)) {
+ //LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode %d", mode);
+ //UNREACHABLE();
+
+ return GL_LINEAR;
+ }
+
+ GLenum gl_mode = filter_mode_table[mode];
+
+ // Check for dummy values indicating an unknown mode
+ if (gl_mode == 0) {
+ //LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode %d", mode);
+ //UNIMPLEMENTED();
+
+ return GL_LINEAR;
+ }
+
+ return gl_mode;
+}
+
+inline GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) {
+ static const GLenum wrap_mode_table[] = {
+ GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge
+ GL_CLAMP_TO_BORDER,// WrapMode::ClampToBorder
+ GL_REPEAT, // WrapMode::Repeat
+ GL_MIRRORED_REPEAT // WrapMode::MirroredRepeat
+ };
+
+ // Range check table for input
+ if (mode >= ARRAY_SIZE(wrap_mode_table)) {
+ //LOG_CRITICAL(Render_OpenGL, "Unknown texture wrap mode %d", mode);
+ //UNREACHABLE();
+
+ return GL_CLAMP_TO_EDGE;
+ }
+
+ GLenum gl_mode = wrap_mode_table[mode];
+
+ // Check for dummy values indicating an unknown mode
+ if (gl_mode == 0) {
+ //LOG_CRITICAL(Render_OpenGL, "Unknown texture wrap mode %d", mode);
+ //UNIMPLEMENTED();
+
+ return GL_CLAMP_TO_EDGE;
+ }
+
+ return gl_mode;
+}
+
+inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) {
+ static const GLenum blend_func_table[] = {
+ GL_ZERO, // BlendFactor::Zero
+ GL_ONE, // BlendFactor::One
+ GL_SRC_COLOR, // BlendFactor::SourceColor
+ GL_ONE_MINUS_SRC_COLOR, // BlendFactor::OneMinusSourceColor
+ GL_DST_COLOR, // BlendFactor::DestColor
+ GL_ONE_MINUS_DST_COLOR, // BlendFactor::OneMinusDestColor
+ GL_SRC_ALPHA, // BlendFactor::SourceAlpha
+ GL_ONE_MINUS_SRC_ALPHA, // BlendFactor::OneMinusSourceAlpha
+ GL_DST_ALPHA, // BlendFactor::DestAlpha
+ GL_ONE_MINUS_DST_ALPHA, // BlendFactor::OneMinusDestAlpha
+ GL_CONSTANT_COLOR, // BlendFactor::ConstantColor
+ GL_ONE_MINUS_CONSTANT_COLOR, // BlendFactor::OneMinusConstantColor
+ GL_CONSTANT_ALPHA, // BlendFactor::ConstantAlpha
+ GL_ONE_MINUS_CONSTANT_ALPHA, // BlendFactor::OneMinusConstantAlpha
+ GL_SRC_ALPHA_SATURATE, // BlendFactor::SourceAlphaSaturate
+ };
+
+ // Range check table for input
+ if ((unsigned)factor >= ARRAY_SIZE(blend_func_table)) {
+ //LOG_CRITICAL(Render_OpenGL, "Unknown blend factor %d", factor);
+ //UNREACHABLE();
+
+ return GL_ONE;
+ }
+
+ return blend_func_table[(unsigned)factor];
+}
+
+inline GLenum LogicOp(Pica::Regs::LogicOp op) {
+ static const GLenum logic_op_table[] = {
+ GL_CLEAR, // Clear
+ GL_AND, // And
+ GL_AND_REVERSE, // AndReverse
+ GL_COPY, // Copy
+ GL_SET, // Set
+ GL_COPY_INVERTED, // CopyInverted
+ GL_NOOP, // NoOp
+ GL_INVERT, // Invert
+ GL_NAND, // Nand
+ GL_OR, // Or
+ GL_NOR, // Nor
+ GL_XOR, // Xor
+ GL_EQUIV, // Equiv
+ GL_AND_INVERTED, // AndInverted
+ GL_OR_REVERSE, // OrReverse
+ GL_OR_INVERTED, // OrInverted
+ };
+
+ // Range check table for input
+ if ((unsigned)op >= ARRAY_SIZE(logic_op_table)) {
+ //LOG_CRITICAL(Render_OpenGL, "Unknown logic op %d", op);
+ //UNREACHABLE();
+
+ return GL_COPY;
+ }
+
+ return logic_op_table[(unsigned)op];
+}
+
+inline GLenum CompareFunc(Pica::Regs::CompareFunc func) {
+ static const GLenum compare_func_table[] = {
+ GL_NEVER, // CompareFunc::Never
+ GL_ALWAYS, // CompareFunc::Always
+ GL_EQUAL, // CompareFunc::Equal
+ GL_NOTEQUAL, // CompareFunc::NotEqual
+ GL_LESS, // CompareFunc::LessThan
+ GL_LEQUAL, // CompareFunc::LessThanOrEqual
+ GL_GREATER, // CompareFunc::GreaterThan
+ GL_GEQUAL, // CompareFunc::GreaterThanOrEqual
+ };
+
+ // Range check table for input
+ if ((unsigned)func >= ARRAY_SIZE(compare_func_table)) {
+ //LOG_CRITICAL(Render_OpenGL, "Unknown compare function %d", func);
+ //UNREACHABLE();
+
+ return GL_ALWAYS;
+ }
+
+ return compare_func_table[(unsigned)func];
+}
+
+inline GLenum StencilOp(Pica::Regs::StencilAction action) {
+ static const GLenum stencil_op_table[] = {
+ GL_KEEP, // StencilAction::Keep
+ GL_ZERO, // StencilAction::Zero
+ GL_REPLACE, // StencilAction::Replace
+ GL_INCR, // StencilAction::Increment
+ GL_DECR, // StencilAction::Decrement
+ GL_INVERT, // StencilAction::Invert
+ GL_INCR_WRAP, // StencilAction::IncrementWrap
+ GL_DECR_WRAP // StencilAction::DecrementWrap
+ };
+
+ // Range check table for input
+ if ((unsigned)action >= ARRAY_SIZE(stencil_op_table)) {
+ //LOG_CRITICAL(Render_OpenGL, "Unknown stencil op %d", action);
+ //UNREACHABLE();
+
+ return GL_KEEP;
+ }
+
+ return stencil_op_table[(unsigned)action];
+}
+
+inline std::array<GLfloat, 4> ColorRGBA8(const u32 color) {
+ return { { (color >> 0 & 0xFF) / 255.0f,
+ (color >> 8 & 0xFF) / 255.0f,
+ (color >> 16 & 0xFF) / 255.0f,
+ (color >> 24 & 0xFF) / 255.0f
+ } };
+}
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+using namespace std;
+
+
+#define NOMINMAX
+#include <algorithm>
+#include <cstddef>
+#include <cstdlib>
+
+#include <cassert>
+
+#include "citraimport\common/assert.h"
+#include "citraimport\common/emu_window.h"
+#include "citraimport\common/logging/log.h"
+#include "citraimport\common/profiler_reporting.h"
+
+#include "citraimport\GPU\video_core/video_core.h"
+#include "citraimport\GPU\video_core/debug_utils/debug_utils.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_rasterizer.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_shader_util.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_shaders.h"
+#include "citraimport\GPU\video_core/renderer_opengl/renderer_opengl.h"
+
+#include "citraimport\settings.h"
+
+#include "citraimport\GPU\HW\lcd.h"
+#include "citraimport\GPU\HW\hw.h"
+
+//import
+u8* Mem_GetPhysicalPointer(u32 addr);
+
+/**
+ * Vertex structure that the drawn screen rectangles are composed of.
+ */
+struct ScreenRectVertex {
+ ScreenRectVertex(GLfloat x, GLfloat y, GLfloat u, GLfloat v) {
+ position[0] = x;
+ position[1] = y;
+ tex_coord[0] = u;
+ tex_coord[1] = v;
+ }
+
+ GLfloat position[2];
+ GLfloat tex_coord[2];
+};
+
+/**
+ * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
+ * corner and (width, height) on the lower-bottom.
+ *
+ * The projection part of the matrix is trivial, hence these operations are represented
+ * by a 3x2 matrix.
+ */
+static std::array<GLfloat, 3*2> MakeOrthographicMatrix(const float width, const float height) {
+ std::array<GLfloat, 3*2> matrix;
+
+ matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
+ matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;
+ // Last matrix row is implicitly assumed to be [0, 0, 1].
+
+ return matrix;
+}
+
+/// RendererOpenGL constructor
+RendererOpenGL::RendererOpenGL() {
+ hw_rasterizer.reset(new RasterizerOpenGL());
+ resolution_width = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth);
+ resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight;
+
+ //ichfly
+ Settings::values.use_hw_renderer = true;
+ Settings::values.use_shader_jit = true;
+}
+
+/// RendererOpenGL destructor
+RendererOpenGL::~RendererOpenGL() {
+}
+
+/// Swap buffers (render frame)
+void RendererOpenGL::SwapBuffers() {
+ // Maintain the rasterizer's state as a priority
+ OpenGLState prev_state = OpenGLState::GetCurState();
+ state.Apply();
+
+ for(int i : {0, 1}) {
+ const auto& framebuffer = GPU::g_regs.framebuffer_config[i];
+
+ // Main LCD (0): 0x1ED02204, Sub LCD (1): 0x1ED02A04
+ u32 lcd_color_addr = (i == 0) ? LCD_REG_INDEX(color_fill_top) : LCD_REG_INDEX(color_fill_bottom);
+ lcd_color_addr = HW::VADDR_LCD + 4 * lcd_color_addr;
+ LCD::Regs::ColorFill color_fill = {0};
+ //LCD::Read(color_fill.raw, lcd_color_addr); //todo
+
+ if (color_fill.is_enabled) {
+ LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, textures[i]);
+
+ // Resize the texture in case the framebuffer size has changed
+ textures[i].width = 1;
+ textures[i].height = 1;
+ } else {
+ if (textures[i].width != (GLsizei)framebuffer.width ||
+ textures[i].height != (GLsizei)framebuffer.height ||
+ textures[i].format != framebuffer.color_format) {
+ // Reallocate texture if the framebuffer size has changed.
+ // This is expected to not happen very often and hence should not be a
+ // performance problem.
+ ConfigureFramebufferTexture(textures[i], framebuffer);
+ }
+ LoadFBToActiveGLTexture(framebuffer, textures[i]);
+
+ // Resize the texture in case the framebuffer size has changed
+ textures[i].width = framebuffer.width;
+ textures[i].height = framebuffer.height;
+ }
+ }
+
+ DrawScreens();
+
+ auto& profiler = Common::Profiling::GetProfilingManager();
+ profiler.FinishFrame();
+ {
+ auto aggregator = Common::Profiling::GetTimingResultsAggregator();
+ aggregator->AddFrame(profiler.GetPreviousFrameResults());
+ }
+
+ // Swap buffers
+ render_window->PollEvents();
+ render_window->SwapBuffers();
+
+ prev_state.Apply();
+
+ profiler.BeginFrame();
+
+ bool hw_renderer_enabled = VideoCore::g_hw_renderer_enabled;
+ if (Settings::values.use_hw_renderer != hw_renderer_enabled) {
+ // TODO: Save new setting value to config file for next startup
+ Settings::values.use_hw_renderer = hw_renderer_enabled;
+
+ if (Settings::values.use_hw_renderer) {
+ hw_rasterizer->Reset();
+ }
+ }
+}
+
+/**
+ * Loads framebuffer from emulated memory into the active OpenGL texture.
+ */
+void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
+ const TextureInfo& texture) {
+
+ const PAddr framebuffer_addr = framebuffer.active_fb == 0 ?
+ framebuffer.address_left1 : framebuffer.address_left2;
+
+ LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x",
+ framebuffer.stride * framebuffer.height,
+ framebuffer_addr, (int)framebuffer.width,
+ (int)framebuffer.height, (int)framebuffer.format);
+
+ const u8* framebuffer_data = Mem_GetPhysicalPointer(framebuffer_addr);
+
+ int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format);
+ size_t pixel_stride = framebuffer.stride / bpp;
+
+ // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately
+ assert(pixel_stride * bpp == framebuffer.stride);
+
+ // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default
+ // only allows rows to have a memory alignement of 4.
+ assert(pixel_stride % 4 == 0);
+
+ state.texture_units[0].texture_2d = texture.handle;
+ state.Apply();
+
+ glActiveTexture(GL_TEXTURE0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride);
+
+ // Update existing texture
+ // TODO: Test what happens on hardware when you change the framebuffer dimensions so that they
+ // differ from the LCD resolution.
+ // TODO: Applications could theoretically crash Citra here by specifying too large
+ // framebuffer sizes. We should make sure that this cannot happen.
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height,
+ texture.gl_format, texture.gl_type, framebuffer_data);
+
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+
+ state.texture_units[0].texture_2d = 0;
+ state.Apply();
+}
+
+/**
+ * Fills active OpenGL texture with the given RGB color.
+ * Since the color is solid, the texture can be 1x1 but will stretch across whatever it's rendered on.
+ * This has the added benefit of being *really fast*.
+ */
+void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b,
+ const TextureInfo& texture) {
+ state.texture_units[0].texture_2d = texture.handle;
+ state.Apply();
+
+ glActiveTexture(GL_TEXTURE0);
+ u8 framebuffer_data[3] = { color_r, color_g, color_b };
+
+ // Update existing texture
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer_data);
+}
+
+/**
+ * Initializes the OpenGL state and creates persistent objects.
+ */
+void RendererOpenGL::InitOpenGLObjects() {
+ glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f);
+
+ // Link shaders and get variable locations
+ program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader);
+ uniform_modelview_matrix = glGetUniformLocation(program_id, "modelview_matrix");
+ uniform_color_texture = glGetUniformLocation(program_id, "color_texture");
+ attrib_position = glGetAttribLocation(program_id, "vert_position");
+ attrib_tex_coord = glGetAttribLocation(program_id, "vert_tex_coord");
+
+ // Generate VBO handle for drawing
+ glGenBuffers(1, &vertex_buffer_handle);
+
+ // Generate VAO
+ glGenVertexArrays(1, &vertex_array_handle);
+
+ state.draw.vertex_array = vertex_array_handle;
+ state.draw.vertex_buffer = vertex_buffer_handle;
+ state.Apply();
+
+ // Attach vertex data to VAO
+ glBufferData(GL_ARRAY_BUFFER, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
+ glVertexAttribPointer(attrib_position, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), (GLvoid*)offsetof(ScreenRectVertex, position));
+ glVertexAttribPointer(attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), (GLvoid*)offsetof(ScreenRectVertex, tex_coord));
+ glEnableVertexAttribArray(attrib_position);
+ glEnableVertexAttribArray(attrib_tex_coord);
+
+ // Allocate textures for each screen
+ for (auto& texture : textures) {
+ glGenTextures(1, &texture.handle);
+
+ // Allocation of storage is deferred until the first frame, when we
+ // know the framebuffer size.
+
+ state.texture_units[0].texture_2d = texture.handle;
+ state.Apply();
+
+ glActiveTexture(GL_TEXTURE0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ state.texture_units[0].texture_2d = 0;
+ state.Apply();
+
+ hw_rasterizer->InitObjects();
+}
+
+void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
+ const GPU::Regs::FramebufferConfig& framebuffer) {
+ GPU::Regs::PixelFormat format = framebuffer.color_format;
+ GLint internal_format;
+
+ texture.format = format;
+ texture.width = framebuffer.width;
+ texture.height = framebuffer.height;
+
+ switch (format) {
+ case GPU::Regs::PixelFormat::RGBA8:
+ internal_format = GL_RGBA;
+ texture.gl_format = GL_RGBA;
+ texture.gl_type = GL_UNSIGNED_INT_8_8_8_8;
+ break;
+
+ case GPU::Regs::PixelFormat::RGB8:
+ // This pixel format uses BGR since GL_UNSIGNED_BYTE specifies byte-order, unlike every
+ // specific OpenGL type used in this function using native-endian (that is, little-endian
+ // mostly everywhere) for words or half-words.
+ // TODO: check how those behave on big-endian processors.
+ internal_format = GL_RGB;
+ texture.gl_format = GL_BGR;
+ texture.gl_type = GL_UNSIGNED_BYTE;
+ break;
+
+ case GPU::Regs::PixelFormat::RGB565:
+ internal_format = GL_RGB;
+ texture.gl_format = GL_RGB;
+ texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+
+ case GPU::Regs::PixelFormat::RGB5A1:
+ internal_format = GL_RGBA;
+ texture.gl_format = GL_RGBA;
+ texture.gl_type = GL_UNSIGNED_SHORT_5_5_5_1;
+ break;
+
+ case GPU::Regs::PixelFormat::RGBA4:
+ internal_format = GL_RGBA;
+ texture.gl_format = GL_RGBA;
+ texture.gl_type = GL_UNSIGNED_SHORT_4_4_4_4;
+ break;
+
+ default:
+ //UNIMPLEMENTED();
+ break;
+ }
+
+ state.texture_units[0].texture_2d = texture.handle;
+ state.Apply();
+
+ glActiveTexture(GL_TEXTURE0);
+ glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0,
+ texture.gl_format, texture.gl_type, nullptr);
+}
+
+/**
+ * Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD rotation.
+ */
+void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h) {
+ std::array<ScreenRectVertex, 4> vertices = {{
+ ScreenRectVertex(x, y, 1.f, 0.f),
+ ScreenRectVertex(x+w, y, 1.f, 1.f),
+ ScreenRectVertex(x, y+h, 0.f, 0.f),
+ ScreenRectVertex(x+w, y+h, 0.f, 1.f),
+ }};
+
+ state.texture_units[0].texture_2d = texture.handle;
+ state.Apply();
+
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+/**
+ * Draws the emulated screens to the emulator window.
+ */
+void RendererOpenGL::DrawScreens() {
+ auto layout = render_window->GetFramebufferLayout();
+
+ glViewport(0, 0, layout.width, layout.height);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ state.draw.shader_program = program_id;
+ state.Apply();
+
+ // Set projection matrix
+ std::array<GLfloat, 3 * 2> ortho_matrix = MakeOrthographicMatrix((float)layout.width,
+ (float)layout.height);
+ glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
+
+ // Bind texture in Texture Unit 0
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1i(uniform_color_texture, 0);
+
+ DrawSingleScreenRotated(textures[0], (float)layout.top_screen.left, (float)layout.top_screen.top,
+ (float)layout.top_screen.GetWidth(), (float)layout.top_screen.GetHeight());
+ DrawSingleScreenRotated(textures[1], (float)layout.bottom_screen.left,(float)layout.bottom_screen.top,
+ (float)layout.bottom_screen.GetWidth(), (float)layout.bottom_screen.GetHeight());
+
+ m_current_frame++;
+}
+
+/// Updates the framerate
+void RendererOpenGL::UpdateFramerate() {
+}
+
+/**
+ * Set the emulator window to use for renderer
+ * @param window EmuWindow handle to emulator window to use for rendering
+ */
+void RendererOpenGL::SetWindow(EmuWindow* window) {
+ render_window = window;
+}
+
+/// Initialize the renderer
+void RendererOpenGL::Init() {
+ render_window->MakeCurrent();
+
+ // TODO: Make frontends initialize this, so they can use gladLoadGLLoader with their own loaders
+ if (!gladLoadGL()) {
+ LOG_CRITICAL(Render_OpenGL, "Failed to initialize GL functions! Exiting...");
+ return;
+ }
+
+ LOG_INFO(Render_OpenGL, "GL_VERSION: %s", glGetString(GL_VERSION));
+ LOG_INFO(Render_OpenGL, "GL_VENDOR: %s", glGetString(GL_VENDOR));
+ LOG_INFO(Render_OpenGL, "GL_RENDERER: %s", glGetString(GL_RENDERER));
+ InitOpenGLObjects();
+}
+
+/// Shutdown the renderer
+void RendererOpenGL::ShutDown() {
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+//#include <glad/glad.h>
+
+
+#include "citraimport\GPU\video_core/renderer_base.h"
+#include "citraimport\GPU\video_core/renderer_opengl/gl_state.h"
+
+#include "citraimport\GPU\HW/gpu.h"
+
+class EmuWindow;
+
+class RendererOpenGL : public RendererBase {
+public:
+
+ RendererOpenGL();
+ ~RendererOpenGL() override;
+
+ /// Swap buffers (render frame)
+ void SwapBuffers() override;
+
+ /**
+ * Set the emulator window to use for renderer
+ * @param window EmuWindow handle to emulator window to use for rendering
+ */
+ void SetWindow(EmuWindow* window) override;
+
+ /// Initialize the renderer
+ void Init() override;
+
+ /// Shutdown the renderer
+ void ShutDown() override;
+
+private:
+ /// Structure used for storing information about the textures for each 3DS screen
+ struct TextureInfo {
+ GLuint handle;
+ GLsizei width;
+ GLsizei height;
+ GPU::Regs::PixelFormat format;
+ GLenum gl_format;
+ GLenum gl_type;
+ };
+
+ void InitOpenGLObjects();
+ void ConfigureFramebufferTexture(TextureInfo& texture,
+ const GPU::Regs::FramebufferConfig& framebuffer);
+ void DrawScreens();
+ void DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h);
+ void UpdateFramerate();
+
+ // Loads framebuffer from emulated memory into the active OpenGL texture.
+ void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
+ const TextureInfo& texture);
+ // Fills active OpenGL texture with the given RGB color.
+ void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b,
+ const TextureInfo& texture);
+
+ EmuWindow* render_window; ///< Handle to render window
+
+ int resolution_width; ///< Current resolution width
+ int resolution_height; ///< Current resolution height
+
+ OpenGLState state;
+
+ // OpenGL object IDs
+ GLuint vertex_array_handle;
+ GLuint vertex_buffer_handle;
+ GLuint program_id;
+ std::array<TextureInfo, 2> textures; ///< Textures for top and bottom screens respectively
+ // Shader uniform location indices
+ GLuint uniform_modelview_matrix;
+ GLuint uniform_color_texture;
+ // Shader attribute input indices
+ GLuint attrib_position;
+ GLuint attrib_tex_coord;
+};
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include <unordered_map>
+
+//#include <boost/range/algorithm/fill.hpp>
+
+#include "citraimport\common/hash.h"
+#include "citraimport\common/make_unique.h"
+#include "citraimport\common/microprofile.h"
+#include "citraimport\common/profiler.h"
+
+#include "citraimport\GPU\video_core/debug_utils/debug_utils.h"
+#include "citraimport\GPU\video_core/pica.h"
+#include "citraimport\GPU\video_core/video_core.h"
+
+#include "shader.h"
+#include "shader_interpreter.h"
+
+#ifdef ARCHITECTURE_x86_64
+#include "shader_jit_x64.h"
+#endif // ARCHITECTURE_x86_64
+
+namespace Pica {
+
+namespace Shader {
+
+#ifdef ARCHITECTURE_x86_64
+static std::unordered_map<u64, CompiledShader*> shader_map;
+static JitCompiler jit;
+static CompiledShader* jit_shader;
+#endif // ARCHITECTURE_x86_64
+
+void Setup(UnitState<false>& state) {
+#ifdef ARCHITECTURE_x86_64
+ if (VideoCore::g_shader_jit_enabled) {
+ u64 cache_key = (Common::ComputeHash64(&g_state.vs.program_code, sizeof(g_state.vs.program_code)) ^
+ Common::ComputeHash64(&g_state.vs.swizzle_data, sizeof(g_state.vs.swizzle_data)) ^
+ g_state.regs.vs.main_offset);
+
+ auto iter = shader_map.find(cache_key);
+ if (iter != shader_map.end()) {
+ jit_shader = iter->second;
+ } else {
+ jit_shader = jit.Compile();
+ shader_map.emplace(cache_key, jit_shader);
+ }
+ }
+#endif // ARCHITECTURE_x86_64
+}
+
+void Shutdown() {
+#ifdef ARCHITECTURE_x86_64
+ shader_map.clear();
+#endif // ARCHITECTURE_x86_64
+}
+
+static Common::Profiling::TimingCategory shader_category("Vertex Shader");
+
+OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attributes) {
+ auto& config = g_state.regs.vs;
+
+ Common::Profiling::ScopeTimer timer(shader_category);
+
+ state.program_counter = config.main_offset;
+ state.debug.max_offset = 0;
+ state.debug.max_opdesc_id = 0;
+
+ // Setup input register table
+ const auto& attribute_register_map = config.input_register_map;
+
+ // TODO: Instead of this cumbersome logic, just load the input data directly like
+ // for (int attr = 0; attr < num_attributes; ++attr) { input_attr[0] = state.registers.input[attribute_register_map.attribute0_register]; }
+ if (num_attributes > 0) state.registers.input[attribute_register_map.attribute0_register] = input.attr[0];
+ if (num_attributes > 1) state.registers.input[attribute_register_map.attribute1_register] = input.attr[1];
+ if (num_attributes > 2) state.registers.input[attribute_register_map.attribute2_register] = input.attr[2];
+ if (num_attributes > 3) state.registers.input[attribute_register_map.attribute3_register] = input.attr[3];
+ if (num_attributes > 4) state.registers.input[attribute_register_map.attribute4_register] = input.attr[4];
+ if (num_attributes > 5) state.registers.input[attribute_register_map.attribute5_register] = input.attr[5];
+ if (num_attributes > 6) state.registers.input[attribute_register_map.attribute6_register] = input.attr[6];
+ if (num_attributes > 7) state.registers.input[attribute_register_map.attribute7_register] = input.attr[7];
+ if (num_attributes > 8) state.registers.input[attribute_register_map.attribute8_register] = input.attr[8];
+ if (num_attributes > 9) state.registers.input[attribute_register_map.attribute9_register] = input.attr[9];
+ if (num_attributes > 10) state.registers.input[attribute_register_map.attribute10_register] = input.attr[10];
+ if (num_attributes > 11) state.registers.input[attribute_register_map.attribute11_register] = input.attr[11];
+ if (num_attributes > 12) state.registers.input[attribute_register_map.attribute12_register] = input.attr[12];
+ if (num_attributes > 13) state.registers.input[attribute_register_map.attribute13_register] = input.attr[13];
+ if (num_attributes > 14) state.registers.input[attribute_register_map.attribute14_register] = input.attr[14];
+ if (num_attributes > 15) state.registers.input[attribute_register_map.attribute15_register] = input.attr[15];
+
+ state.conditional_code[0] = false;
+ state.conditional_code[1] = false;
+
+#ifdef ARCHITECTURE_x86_64
+ if (VideoCore::g_shader_jit_enabled)
+ jit_shader(&state.registers);
+ else
+ RunInterpreter(state);
+#else
+ RunInterpreter(state);
+#endif // ARCHITECTURE_x86_64
+
+ // Setup output data
+ OutputVertex ret;
+ // TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to
+ // figure out what those circumstances are and enable the remaining outputs then.
+ for (int i = 0; i < 7; ++i) {
+ const auto& output_register_map = g_state.regs.vs_output_attributes[i]; // TODO: Don't hardcode VS here
+
+ u32 semantics[4] = {
+ output_register_map.map_x, output_register_map.map_y,
+ output_register_map.map_z, output_register_map.map_w
+ };
+
+ for (int comp = 0; comp < 4; ++comp) {
+ float24* out = ((float24*)&ret) + semantics[comp];
+ if (semantics[comp] != Regs::VSOutputAttributes::INVALID) {
+ *out = state.registers.output[i][comp];
+ } else {
+ // Zero output so that attributes which aren't output won't have denormals in them,
+ // which would slow us down later.
+ memset(out, 0, sizeof(*out));
+ }
+ }
+ }
+
+ // The hardware takes the absolute and saturates vertex colors like this, *before* doing interpolation
+ for (int i = 0; i < 4; ++i) {
+ ret.color[i] = float24::FromFloat32(
+ std::fmin(std::fabs(ret.color[i].ToFloat32()), 1.0f));
+ }
+
+ LOG_TRACE(Render_Software, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), quat (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)",
+ ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(),
+ ret.quat.x.ToFloat32(), ret.quat.y.ToFloat32(), ret.quat.z.ToFloat32(), ret.quat.w.ToFloat32(),
+ ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(),
+ ret.tc0.u().ToFloat32(), ret.tc0.v().ToFloat32());
+
+ return ret;
+}
+
+DebugData<true> ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const State::ShaderSetup& setup) {
+ UnitState<true> state;
+
+ state.program_counter = config.main_offset;
+ state.debug.max_offset = 0;
+ state.debug.max_opdesc_id = 0;
+
+ // Setup input register table
+ const auto& attribute_register_map = config.input_register_map;
+ float24 dummy_register;
+ for (int i = 0; i < 16;++i)
+ state.registers.input[i] = &dummy_register;
+
+ if (num_attributes > 0) state.registers.input[attribute_register_map.attribute0_register] = &input.attr[0].x;
+ if (num_attributes > 1) state.registers.input[attribute_register_map.attribute1_register] = &input.attr[1].x;
+ if (num_attributes > 2) state.registers.input[attribute_register_map.attribute2_register] = &input.attr[2].x;
+ if (num_attributes > 3) state.registers.input[attribute_register_map.attribute3_register] = &input.attr[3].x;
+ if (num_attributes > 4) state.registers.input[attribute_register_map.attribute4_register] = &input.attr[4].x;
+ if (num_attributes > 5) state.registers.input[attribute_register_map.attribute5_register] = &input.attr[5].x;
+ if (num_attributes > 6) state.registers.input[attribute_register_map.attribute6_register] = &input.attr[6].x;
+ if (num_attributes > 7) state.registers.input[attribute_register_map.attribute7_register] = &input.attr[7].x;
+ if (num_attributes > 8) state.registers.input[attribute_register_map.attribute8_register] = &input.attr[8].x;
+ if (num_attributes > 9) state.registers.input[attribute_register_map.attribute9_register] = &input.attr[9].x;
+ if (num_attributes > 10) state.registers.input[attribute_register_map.attribute10_register] = &input.attr[10].x;
+ if (num_attributes > 11) state.registers.input[attribute_register_map.attribute11_register] = &input.attr[11].x;
+ if (num_attributes > 12) state.registers.input[attribute_register_map.attribute12_register] = &input.attr[12].x;
+ if (num_attributes > 13) state.registers.input[attribute_register_map.attribute13_register] = &input.attr[13].x;
+ if (num_attributes > 14) state.registers.input[attribute_register_map.attribute14_register] = &input.attr[14].x;
+ if (num_attributes > 15) state.registers.input[attribute_register_map.attribute15_register] = &input.attr[15].x;
+
+ state.conditional_code[0] = false;
+ state.conditional_code[1] = false;
+
+ RunInterpreter(state);
+ return state.debug;
+}
+
+} // namespace Shader
+
+} // namespace Pica
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+
+//#include <boost/container/static_vector.hpp>
+
+#include <citraimport\nihstro/shader_binary.h>
+
+#include "citraimport\common/common_funcs.h"
+#include "citraimport\common/common_types.h"
+#include "citraimport\common/vector_math.h"
+
+#include "citraimport\GPU\video_core/pica.h"
+
+using nihstro::RegisterType;
+using nihstro::SourceRegister;
+using nihstro::DestRegister;
+
+namespace Pica {
+
+namespace Shader {
+
+struct InputVertex {
+ Math::Vec4<float24> attr[16];
+};
+
+struct OutputVertex {
+ OutputVertex() = default;
+
+ // VS output attributes
+ Math::Vec4<float24> pos;
+ Math::Vec4<float24> quat;
+ Math::Vec4<float24> color;
+ Math::Vec2<float24> tc0;
+ Math::Vec2<float24> tc1;
+ float24 pad[6];
+ Math::Vec2<float24> tc2;
+
+ // Padding for optimal alignment
+ float24 pad2[4];
+
+ // Attributes used to store intermediate results
+
+ // position after perspective divide
+ Math::Vec3<float24> screenpos;
+ float24 pad3;
+
+ // Linear interpolation
+ // factor: 0=this, 1=vtx
+ void Lerp(float24 factor, const OutputVertex& vtx) {
+ pos = pos * factor + vtx.pos * (float24::FromFloat32(1) - factor);
+
+ // TODO: Should perform perspective correct interpolation here...
+ tc0 = tc0 * factor + vtx.tc0 * (float24::FromFloat32(1) - factor);
+ tc1 = tc1 * factor + vtx.tc1 * (float24::FromFloat32(1) - factor);
+ tc2 = tc2 * factor + vtx.tc2 * (float24::FromFloat32(1) - factor);
+
+ screenpos = screenpos * factor + vtx.screenpos * (float24::FromFloat32(1) - factor);
+
+ color = color * factor + vtx.color * (float24::FromFloat32(1) - factor);
+ }
+
+ // Linear interpolation
+ // factor: 0=v0, 1=v1
+ static OutputVertex Lerp(float24 factor, const OutputVertex& v0, const OutputVertex& v1) {
+ OutputVertex ret = v0;
+ ret.Lerp(factor, v1);
+ return ret;
+ }
+};
+static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD");
+static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size");
+
+
+// Helper structure used to keep track of data useful for inspection of shader emulation
+template<bool full_debugging>
+struct DebugData;
+
+template<>
+struct DebugData<false> {
+ // TODO: Hide these behind and interface and move them to DebugData<true>
+ u32 max_offset; // maximum program counter ever reached
+ u32 max_opdesc_id; // maximum swizzle pattern index ever used
+};
+
+template<>
+struct DebugData<true> {
+ // Records store the input and output operands of a particular instruction.
+ struct Record {
+ enum Type {
+ // Floating point arithmetic operands
+ SRC1 = 0x1,
+ SRC2 = 0x2,
+ SRC3 = 0x4,
+
+ // Initial and final output operand value
+ DEST_IN = 0x8,
+ DEST_OUT = 0x10,
+
+ // Current and next instruction offset (in words)
+ CUR_INSTR = 0x20,
+ NEXT_INSTR = 0x40,
+
+ // Output address register value
+ ADDR_REG_OUT = 0x80,
+
+ // Result of a comparison instruction
+ CMP_RESULT = 0x100,
+
+ // Input values for conditional flow control instructions
+ COND_BOOL_IN = 0x200,
+ COND_CMP_IN = 0x400,
+
+ // Input values for a loop
+ LOOP_INT_IN = 0x800,
+ };
+
+ Math::Vec4<float24> src1;
+ Math::Vec4<float24> src2;
+ Math::Vec4<float24> src3;
+
+ Math::Vec4<float24> dest_in;
+ Math::Vec4<float24> dest_out;
+
+ s32 address_registers[2];
+ bool conditional_code[2];
+ bool cond_bool;
+ bool cond_cmp[2];
+ Math::Vec4<u8> loop_int;
+
+ u32 instruction_offset;
+ u32 next_instruction;
+
+ // set of enabled fields (as a combination of Type flags)
+ unsigned mask = 0;
+ };
+
+ u32 max_offset; // maximum program counter ever reached
+ u32 max_opdesc_id; // maximum swizzle pattern index ever used
+
+ // List of records for each executed shader instruction
+ std::vector<DebugData<true>::Record> records;
+};
+
+// Type alias for better readability
+using DebugDataRecord = DebugData<true>::Record;
+
+// Helper function to set a DebugData<true>::Record field based on the template enum parameter.
+template<DebugDataRecord::Type type, typename ValueType>
+inline void SetField(DebugDataRecord& record, ValueType value);
+
+template<>
+inline void SetField<DebugDataRecord::SRC1>(DebugDataRecord& record, float24* value) {
+ record.src1.x = value[0];
+ record.src1.y = value[1];
+ record.src1.z = value[2];
+ record.src1.w = value[3];
+}
+
+template<>
+inline void SetField<DebugDataRecord::SRC2>(DebugDataRecord& record, float24* value) {
+ record.src2.x = value[0];
+ record.src2.y = value[1];
+ record.src2.z = value[2];
+ record.src2.w = value[3];
+}
+
+template<>
+inline void SetField<DebugDataRecord::SRC3>(DebugDataRecord& record, float24* value) {
+ record.src3.x = value[0];
+ record.src3.y = value[1];
+ record.src3.z = value[2];
+ record.src3.w = value[3];
+}
+
+template<>
+inline void SetField<DebugDataRecord::DEST_IN>(DebugDataRecord& record, float24* value) {
+ record.dest_in.x = value[0];
+ record.dest_in.y = value[1];
+ record.dest_in.z = value[2];
+ record.dest_in.w = value[3];
+}
+
+template<>
+inline void SetField<DebugDataRecord::DEST_OUT>(DebugDataRecord& record, float24* value) {
+ record.dest_out.x = value[0];
+ record.dest_out.y = value[1];
+ record.dest_out.z = value[2];
+ record.dest_out.w = value[3];
+}
+
+template<>
+inline void SetField<DebugDataRecord::ADDR_REG_OUT>(DebugDataRecord& record, s32* value) {
+ record.address_registers[0] = value[0];
+ record.address_registers[1] = value[1];
+}
+
+template<>
+inline void SetField<DebugDataRecord::CMP_RESULT>(DebugDataRecord& record, bool* value) {
+ record.conditional_code[0] = value[0];
+ record.conditional_code[1] = value[1];
+}
+
+template<>
+inline void SetField<DebugDataRecord::COND_BOOL_IN>(DebugDataRecord& record, bool value) {
+ record.cond_bool = value;
+}
+
+template<>
+inline void SetField<DebugDataRecord::COND_CMP_IN>(DebugDataRecord& record, bool* value) {
+ record.cond_cmp[0] = value[0];
+ record.cond_cmp[1] = value[1];
+}
+
+template<>
+inline void SetField<DebugDataRecord::LOOP_INT_IN>(DebugDataRecord& record, Math::Vec4<u8> value) {
+ record.loop_int = value;
+}
+
+template<>
+inline void SetField<DebugDataRecord::CUR_INSTR>(DebugDataRecord& record, u32 value) {
+ record.instruction_offset = value;
+}
+
+template<>
+inline void SetField<DebugDataRecord::NEXT_INSTR>(DebugDataRecord& record, u32 value) {
+ record.next_instruction = value;
+}
+
+// Helper function to set debug information on the current shader iteration.
+template<DebugDataRecord::Type type, typename ValueType>
+inline void Record(DebugData<false>& debug_data, u32 offset, ValueType value) {
+ // Debugging disabled => nothing to do
+}
+
+template<DebugDataRecord::Type type, typename ValueType>
+inline void Record(DebugData<true>& debug_data, u32 offset, ValueType value) {
+ if (offset >= debug_data.records.size())
+ debug_data.records.resize(offset + 1);
+
+ SetField<type, ValueType>(debug_data.records[offset], value);
+ debug_data.records[offset].mask |= type;
+}
+
+
+/**
+ * This structure contains the state information that needs to be unique for a shader unit. The 3DS
+ * has four shader units that process shaders in parallel. At the present, Citra only implements a
+ * single shader unit that processes all shaders serially. Putting the state information in a struct
+ * here will make it easier for us to parallelize the shader processing later.
+ */
+template<bool Debug>
+struct UnitState {
+ struct Registers {
+ // The registers are accessed by the shader JIT using SSE instructions, and are therefore
+ // required to be 16-byte aligned.
+ Math::Vec4<float24> MEMORY_ALIGNED16(input[16]);
+ Math::Vec4<float24> MEMORY_ALIGNED16(output[16]);
+ Math::Vec4<float24> MEMORY_ALIGNED16(temporary[16]);
+ } registers;
+ static_assert(std::is_pod<Registers>::value, "Structure is not POD");
+
+ u32 program_counter;
+ bool conditional_code[2];
+
+ // Two Address registers and one loop counter
+ // TODO: How many bits do these actually have?
+ s32 address_registers[3];
+
+ enum {
+ INVALID_ADDRESS = 0xFFFFFFFF
+ };
+
+ struct CallStackElement {
+ u32 final_address; // Address upon which we jump to return_address
+ u32 return_address; // Where to jump when leaving scope
+ u8 repeat_counter; // How often to repeat until this call stack element is removed
+ u8 loop_increment; // Which value to add to the loop counter after an iteration
+ // TODO: Should this be a signed value? Does it even matter?
+ u32 loop_address; // The address where we'll return to after each loop iteration
+ };
+
+ // TODO: Is there a maximal size for this?
+ std::vector<CallStackElement> call_stack;
+
+ DebugData<Debug> debug;
+
+ static size_t InputOffset(const SourceRegister& reg) {
+ switch (reg.GetRegisterType()) {
+ case RegisterType::Input:
+ return offsetof(UnitState::Registers, input) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
+
+ case RegisterType::Temporary:
+ return offsetof(UnitState::Registers, temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
+
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+ }
+
+ static size_t OutputOffset(const DestRegister& reg) {
+ switch (reg.GetRegisterType()) {
+ case RegisterType::Output:
+ return offsetof(UnitState::Registers, output) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
+
+ case RegisterType::Temporary:
+ return offsetof(UnitState::Registers, temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
+
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+ }
+};
+
+/**
+ * Performs any shader unit setup that only needs to happen once per shader (as opposed to once per
+ * vertex, which would happen within the `Run` function).
+ * @param state Shader unit state, must be setup per shader and per shader unit
+ */
+void Setup(UnitState<false>& state);
+
+/// Performs any cleanup when the emulator is shutdown
+void Shutdown();
+
+/**
+ * Runs the currently setup shader
+ * @param state Shader unit state, must be setup per shader and per shader unit
+ * @param input Input vertex into the shader
+ * @param num_attributes The number of vertex shader attributes
+ * @return The output vertex, after having been processed by the vertex shader
+ */
+OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attributes);
+
+/**
+ * Produce debug information based on the given shader and input vertex
+ * @param input Input vertex into the shader
+ * @param num_attributes The number of vertex shader attributes
+ * @param config Configuration object for the shader pipeline
+ * @param setup Setup object for the shader pipeline
+ * @return Debug information for this shader with regards to the given vertex
+ */
+DebugData<true> ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const State::ShaderSetup& setup);
+
+} // namespace Shader
+
+} // namespace Pica
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <citraimport\common/file_util.h>
+
+#include <citraimport\nihstro/shader_bytecode.h>
+
+#include "citraimport\GPU\video_core/pica.h"
+#include "citraimport\GPU\video_core/shader/shader.h"
+#include "citraimport\GPU\video_core/shader/shader_interpreter.h"
+
+using nihstro::OpCode;
+using nihstro::Instruction;
+using nihstro::RegisterType;
+using nihstro::SourceRegister;
+using nihstro::SwizzlePattern;
+
+namespace Pica {
+
+namespace Shader {
+
+template<bool Debug>
+void RunInterpreter(UnitState<Debug>& state) {
+ const auto& uniforms = g_state.vs.uniforms;
+ const auto& swizzle_data = g_state.vs.swizzle_data;
+ const auto& program_code = g_state.vs.program_code;
+
+ // Placeholder for invalid inputs
+ static float24 dummy_vec4_float24[4];
+
+ unsigned iteration = 0;
+ bool exit_loop = false;
+ while (!exit_loop) {
+ if (!state.call_stack.empty()) {
+ auto& top = state.call_stack.back();
+ if (state.program_counter == top.final_address) {
+ state.address_registers[2] += top.loop_increment;
+
+ if (top.repeat_counter-- == 0) {
+ state.program_counter = top.return_address;
+ state.call_stack.pop_back();
+ } else {
+ state.program_counter = top.loop_address;
+ }
+
+ // TODO: Is "trying again" accurate to hardware?
+ continue;
+ }
+ }
+
+ const Instruction instr = { program_code[state.program_counter] };
+ const SwizzlePattern swizzle = { swizzle_data[instr.common.operand_desc_id] };
+
+ static auto call = [](UnitState<Debug>& state, u32 offset, u32 num_instructions,
+ u32 return_offset, u8 repeat_count, u8 loop_increment) {
+ state.program_counter = offset - 1; // -1 to make sure when incrementing the PC we end up at the correct offset
+ //ASSERT(state.call_stack.size() < state.call_stack.capacity()); //not needed here
+ state.call_stack.push_back({ offset + num_instructions, return_offset, repeat_count, loop_increment, offset });
+ };
+ Record<DebugDataRecord::CUR_INSTR>(state.debug, iteration, state.program_counter);
+ if (iteration > 0)
+ Record<DebugDataRecord::NEXT_INSTR>(state.debug, iteration - 1, state.program_counter);
+
+ state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + state.program_counter);
+
+ auto LookupSourceRegister = [&](const SourceRegister& source_reg) -> const float24* {
+ switch (source_reg.GetRegisterType()) {
+ case RegisterType::Input:
+ return &state.registers.input[source_reg.GetIndex()].x;
+
+ case RegisterType::Temporary:
+ return &state.registers.temporary[source_reg.GetIndex()].x;
+
+ case RegisterType::FloatUniform:
+ return &uniforms.f[source_reg.GetIndex()].x;
+
+ default:
+ return dummy_vec4_float24;
+ }
+ };
+
+ switch (instr.opcode.Value().GetInfo().type) {
+ case OpCode::Type::Arithmetic:
+ {
+ const bool is_inverted = (0 != (instr.opcode.Value().GetInfo().subtype & OpCode::Info::SrcInversed));
+
+ const int address_offset = (instr.common.address_register_index == 0)
+ ? 0 : state.address_registers[instr.common.address_register_index - 1];
+
+ const float24* src1_ = LookupSourceRegister(instr.common.GetSrc1(is_inverted) + (!is_inverted * address_offset));
+ const float24* src2_ = LookupSourceRegister(instr.common.GetSrc2(is_inverted) + ( is_inverted * address_offset));
+
+ const bool negate_src1 = ((bool)swizzle.negate_src1 != false);
+ const bool negate_src2 = ((bool)swizzle.negate_src2 != false);
+
+ float24 src1[4] = {
+ src1_[(int)swizzle.GetSelectorSrc1(0)],
+ src1_[(int)swizzle.GetSelectorSrc1(1)],
+ src1_[(int)swizzle.GetSelectorSrc1(2)],
+ src1_[(int)swizzle.GetSelectorSrc1(3)],
+ };
+ if (negate_src1) {
+ src1[0] = src1[0] * float24::FromFloat32(-1);
+ src1[1] = src1[1] * float24::FromFloat32(-1);
+ src1[2] = src1[2] * float24::FromFloat32(-1);
+ src1[3] = src1[3] * float24::FromFloat32(-1);
+ }
+ float24 src2[4] = {
+ src2_[(int)swizzle.GetSelectorSrc2(0)],
+ src2_[(int)swizzle.GetSelectorSrc2(1)],
+ src2_[(int)swizzle.GetSelectorSrc2(2)],
+ src2_[(int)swizzle.GetSelectorSrc2(3)],
+ };
+ if (negate_src2) {
+ src2[0] = src2[0] * float24::FromFloat32(-1);
+ src2[1] = src2[1] * float24::FromFloat32(-1);
+ src2[2] = src2[2] * float24::FromFloat32(-1);
+ src2[3] = src2[3] * float24::FromFloat32(-1);
+ }
+
+ float24* dest = (instr.common.dest.Value() < 0x10) ? &state.registers.output[instr.common.dest.Value().GetIndex()][0]
+ : (instr.common.dest.Value() < 0x20) ? &state.registers.temporary[instr.common.dest.Value().GetIndex()][0]
+ : dummy_vec4_float24;
+
+ state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id);
+
+ switch (instr.opcode.Value().EffectiveOpCode()) {
+ case OpCode::Id::ADD:
+ {
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::SRC2>(state.debug, iteration, src2);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = src1[i] + src2[i];
+ }
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+ }
+
+ case OpCode::Id::MUL:
+ {
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::SRC2>(state.debug, iteration, src2);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = src1[i] * src2[i];
+ }
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+ }
+
+ case OpCode::Id::FLR:
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = float24::FromFloat32(std::floor(src1[i].ToFloat32()));
+ }
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+
+ case OpCode::Id::MAX:
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::SRC2>(state.debug, iteration, src2);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ // NOTE: Exact form required to match NaN semantics to hardware:
+ // max(0, NaN) -> NaN
+ // max(NaN, 0) -> 0
+ dest[i] = (src1[i] > src2[i]) ? src1[i] : src2[i];
+ }
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+
+ case OpCode::Id::MIN:
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::SRC2>(state.debug, iteration, src2);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ // NOTE: Exact form required to match NaN semantics to hardware:
+ // min(0, NaN) -> NaN
+ // min(NaN, 0) -> 0
+ dest[i] = (src1[i] < src2[i]) ? src1[i] : src2[i];
+ }
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+
+ case OpCode::Id::DP3:
+ case OpCode::Id::DP4:
+ case OpCode::Id::DPH:
+ case OpCode::Id::DPHI:
+ {
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::SRC2>(state.debug, iteration, src2);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+
+ OpCode::Id opcode = instr.opcode.Value().EffectiveOpCode();
+ if (opcode == OpCode::Id::DPH || opcode == OpCode::Id::DPHI)
+ src1[3] = float24::FromFloat32(1.0f);
+
+ float24 dot = float24::FromFloat32(0.f);
+ int num_components = (opcode == OpCode::Id::DP3) ? 3 : 4;
+ for (int i = 0; i < num_components; ++i)
+ dot = dot + src1[i] * src2[i];
+
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = dot;
+ }
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+ }
+
+ // Reciprocal
+ case OpCode::Id::RCP:
+ {
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+ float24 rcp_res = float24::FromFloat32(1.0f / src1[0].ToFloat32());
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = rcp_res;
+ }
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+ }
+
+ // Reciprocal Square Root
+ case OpCode::Id::RSQ:
+ {
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+ float24 rsq_res = float24::FromFloat32(1.0f / std::sqrt(src1[0].ToFloat32()));
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = rsq_res;
+ }
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+ }
+
+ case OpCode::Id::MOVA:
+ {
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ for (int i = 0; i < 2; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ // TODO: Figure out how the rounding is done on hardware
+ state.address_registers[i] = static_cast<s32>(src1[i].ToFloat32());
+ }
+ Record<DebugDataRecord::ADDR_REG_OUT>(state.debug, iteration, state.address_registers);
+ break;
+ }
+
+ case OpCode::Id::MOV:
+ {
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = src1[i];
+ }
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+ }
+
+ case OpCode::Id::SGE:
+ case OpCode::Id::SGEI:
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::SRC2>(state.debug, iteration, src2);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = (src1[i] >= src2[i]) ? float24::FromFloat32(1.0f) : float24::FromFloat32(0.0f);
+ }
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+
+ case OpCode::Id::SLT:
+ case OpCode::Id::SLTI:
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::SRC2>(state.debug, iteration, src2);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = (src1[i] < src2[i]) ? float24::FromFloat32(1.0f) : float24::FromFloat32(0.0f);
+ }
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+
+ case OpCode::Id::CMP:
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::SRC2>(state.debug, iteration, src2);
+ for (int i = 0; i < 2; ++i) {
+ // TODO: Can you restrict to one compare via dest masking?
+
+ auto compare_op = instr.common.compare_op;
+ auto op = (i == 0) ? compare_op.x.Value() : compare_op.y.Value();
+
+ switch (op) {
+ case Instruction::Common::CompareOpType::Equal:
+ state.conditional_code[i] = (src1[i] == src2[i]);
+ break;
+
+ case Instruction::Common::CompareOpType::NotEqual:
+ state.conditional_code[i] = (src1[i] != src2[i]);
+ break;
+
+ case Instruction::Common::CompareOpType::LessThan:
+ state.conditional_code[i] = (src1[i] < src2[i]);
+ break;
+
+ case Instruction::Common::CompareOpType::LessEqual:
+ state.conditional_code[i] = (src1[i] <= src2[i]);
+ break;
+
+ case Instruction::Common::CompareOpType::GreaterThan:
+ state.conditional_code[i] = (src1[i] > src2[i]);
+ break;
+
+ case Instruction::Common::CompareOpType::GreaterEqual:
+ state.conditional_code[i] = (src1[i] >= src2[i]);
+ break;
+
+ default:
+ LOG_ERROR(HW_GPU, "Unknown compare mode %x", static_cast<int>(op));
+ break;
+ }
+ }
+ Record<DebugDataRecord::CMP_RESULT>(state.debug, iteration, state.conditional_code);
+ break;
+
+ case OpCode::Id::EX2:
+ {
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+
+ // EX2 only takes first component exp2 and writes it to all dest components
+ float24 ex2_res = float24::FromFloat32(std::exp2(src1[0].ToFloat32()));
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = ex2_res;
+ }
+
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+ }
+
+ case OpCode::Id::LG2:
+ {
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+
+ // LG2 only takes the first component log2 and writes it to all dest components
+ float24 lg2_res = float24::FromFloat32(std::log2(src1[0].ToFloat32()));
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = lg2_res;
+ }
+
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ break;
+ }
+
+ default:
+ LOG_ERROR(HW_GPU, "Unhandled arithmetic instruction: 0x%02x (%s): 0x%08x",
+ (int)instr.opcode.Value().EffectiveOpCode(), instr.opcode.Value().GetInfo().name, instr.hex);
+ DEBUG_ASSERT(false);
+ break;
+ }
+
+ break;
+ }
+
+ case OpCode::Type::MultiplyAdd:
+ {
+ if ((instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD) ||
+ (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI)) {
+ const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.mad.operand_desc_id];
+
+ bool is_inverted = (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI);
+
+ const float24* src1_ = LookupSourceRegister(instr.mad.GetSrc1(is_inverted));
+ const float24* src2_ = LookupSourceRegister(instr.mad.GetSrc2(is_inverted));
+ const float24* src3_ = LookupSourceRegister(instr.mad.GetSrc3(is_inverted));
+
+ const bool negate_src1 = ((bool)swizzle.negate_src1 != false);
+ const bool negate_src2 = ((bool)swizzle.negate_src2 != false);
+ const bool negate_src3 = ((bool)swizzle.negate_src3 != false);
+
+ float24 src1[4] = {
+ src1_[(int)swizzle.GetSelectorSrc1(0)],
+ src1_[(int)swizzle.GetSelectorSrc1(1)],
+ src1_[(int)swizzle.GetSelectorSrc1(2)],
+ src1_[(int)swizzle.GetSelectorSrc1(3)],
+ };
+ if (negate_src1) {
+ src1[0] = src1[0] * float24::FromFloat32(-1);
+ src1[1] = src1[1] * float24::FromFloat32(-1);
+ src1[2] = src1[2] * float24::FromFloat32(-1);
+ src1[3] = src1[3] * float24::FromFloat32(-1);
+ }
+ float24 src2[4] = {
+ src2_[(int)swizzle.GetSelectorSrc2(0)],
+ src2_[(int)swizzle.GetSelectorSrc2(1)],
+ src2_[(int)swizzle.GetSelectorSrc2(2)],
+ src2_[(int)swizzle.GetSelectorSrc2(3)],
+ };
+ if (negate_src2) {
+ src2[0] = src2[0] * float24::FromFloat32(-1);
+ src2[1] = src2[1] * float24::FromFloat32(-1);
+ src2[2] = src2[2] * float24::FromFloat32(-1);
+ src2[3] = src2[3] * float24::FromFloat32(-1);
+ }
+ float24 src3[4] = {
+ src3_[(int)swizzle.GetSelectorSrc3(0)],
+ src3_[(int)swizzle.GetSelectorSrc3(1)],
+ src3_[(int)swizzle.GetSelectorSrc3(2)],
+ src3_[(int)swizzle.GetSelectorSrc3(3)],
+ };
+ if (negate_src3) {
+ src3[0] = src3[0] * float24::FromFloat32(-1);
+ src3[1] = src3[1] * float24::FromFloat32(-1);
+ src3[2] = src3[2] * float24::FromFloat32(-1);
+ src3[3] = src3[3] * float24::FromFloat32(-1);
+ }
+
+ float24* dest = (instr.mad.dest.Value() < 0x10) ? &state.registers.output[instr.mad.dest.Value().GetIndex()][0]
+ : (instr.mad.dest.Value() < 0x20) ? &state.registers.temporary[instr.mad.dest.Value().GetIndex()][0]
+ : dummy_vec4_float24;
+
+ Record<DebugDataRecord::SRC1>(state.debug, iteration, src1);
+ Record<DebugDataRecord::SRC2>(state.debug, iteration, src2);
+ Record<DebugDataRecord::SRC3>(state.debug, iteration, src3);
+ Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest);
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = src1[i] * src2[i] + src3[i];
+ }
+ Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest);
+ } else {
+ LOG_ERROR(HW_GPU, "Unhandled multiply-add instruction: 0x%02x (%s): 0x%08x",
+ (int)instr.opcode.Value().EffectiveOpCode(), instr.opcode.Value().GetInfo().name, instr.hex);
+ }
+ break;
+ }
+
+ default:
+ {
+ static auto evaluate_condition = [](const UnitState<Debug>& state, bool refx, bool refy, Instruction::FlowControlType flow_control) {
+ bool results[2] = { refx == state.conditional_code[0],
+ refy == state.conditional_code[1] };
+
+ switch (flow_control.op) {
+ case flow_control.Or:
+ return results[0] || results[1];
+
+ case flow_control.And:
+ return results[0] && results[1];
+
+ case flow_control.JustX:
+ return results[0];
+
+ case flow_control.JustY:
+ return results[1];
+ }
+ };
+
+ // Handle each instruction on its own
+ switch (instr.opcode.Value()) {
+ case OpCode::Id::END:
+ exit_loop = true;
+ break;
+
+ case OpCode::Id::JMPC:
+ Record<DebugDataRecord::COND_CMP_IN>(state.debug, iteration, state.conditional_code);
+ if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, instr.flow_control)) {
+ state.program_counter = instr.flow_control.dest_offset - 1;
+ }
+ break;
+
+ case OpCode::Id::JMPU:
+ Record<DebugDataRecord::COND_BOOL_IN>(state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]);
+ if (uniforms.b[instr.flow_control.bool_uniform_id]) {
+ state.program_counter = instr.flow_control.dest_offset - 1;
+ }
+ break;
+
+ case OpCode::Id::CALL:
+ call(state,
+ instr.flow_control.dest_offset,
+ instr.flow_control.num_instructions,
+ state.program_counter + 1, 0, 0);
+ break;
+
+ case OpCode::Id::CALLU:
+ Record<DebugDataRecord::COND_BOOL_IN>(state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]);
+ if (uniforms.b[instr.flow_control.bool_uniform_id]) {
+ call(state,
+ instr.flow_control.dest_offset,
+ instr.flow_control.num_instructions,
+ state.program_counter + 1, 0, 0);
+ }
+ break;
+
+ case OpCode::Id::CALLC:
+ Record<DebugDataRecord::COND_CMP_IN>(state.debug, iteration, state.conditional_code);
+ if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, instr.flow_control)) {
+ call(state,
+ instr.flow_control.dest_offset,
+ instr.flow_control.num_instructions,
+ state.program_counter + 1, 0, 0);
+ }
+ break;
+
+ case OpCode::Id::NOP:
+ break;
+
+ case OpCode::Id::IFU:
+ Record<DebugDataRecord::COND_BOOL_IN>(state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]);
+ if (uniforms.b[instr.flow_control.bool_uniform_id]) {
+ call(state,
+ state.program_counter + 1,
+ instr.flow_control.dest_offset - state.program_counter - 1,
+ instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0);
+ } else {
+ call(state,
+ instr.flow_control.dest_offset,
+ instr.flow_control.num_instructions,
+ instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0);
+ }
+
+ break;
+
+ case OpCode::Id::IFC:
+ {
+ // TODO: Do we need to consider swizzlers here?
+
+ Record<DebugDataRecord::COND_CMP_IN>(state.debug, iteration, state.conditional_code);
+ if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, instr.flow_control)) {
+ call(state,
+ state.program_counter + 1,
+ instr.flow_control.dest_offset - state.program_counter - 1,
+ instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0);
+ } else {
+ call(state,
+ instr.flow_control.dest_offset,
+ instr.flow_control.num_instructions,
+ instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0);
+ }
+
+ break;
+ }
+
+ case OpCode::Id::LOOP:
+ {
+ Math::Vec4<u8> loop_param(uniforms.i[instr.flow_control.int_uniform_id].x,
+ uniforms.i[instr.flow_control.int_uniform_id].y,
+ uniforms.i[instr.flow_control.int_uniform_id].z,
+ uniforms.i[instr.flow_control.int_uniform_id].w);
+ state.address_registers[2] = loop_param.y;
+
+ Record<DebugDataRecord::LOOP_INT_IN>(state.debug, iteration, loop_param);
+ call(state,
+ state.program_counter + 1,
+ instr.flow_control.dest_offset - state.program_counter + 1,
+ instr.flow_control.dest_offset + 1,
+ loop_param.x,
+ loop_param.z);
+ break;
+ }
+
+ default:
+ LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x",
+ (int)instr.opcode.Value().EffectiveOpCode(), instr.opcode.Value().GetInfo().name, instr.hex);
+ break;
+ }
+
+ break;
+ }
+ }
+
+ ++state.program_counter;
+ ++iteration;
+ }
+}
+
+// Explicit instantiation
+template void RunInterpreter(UnitState<false>& state);
+template void RunInterpreter(UnitState<true>& state);
+
+} // namespace
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "citraimport\GPU\video_core/shader/shader.h"
+
+namespace Pica {
+
+namespace Shader {
+
+template<bool Debug>
+void RunInterpreter(UnitState<Debug>& state);
+
+} // namespace
+
+} // namespace
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <smmintrin.h>
+
+#include "citraimport\common/x64/abi.h"
+#include "citraimport\common/x64/cpu_detect.h"
+#include "citraimport\common/x64/emitter.h"
+
+#include "shader.h"
+#include "shader_jit_x64.h"
+
+namespace Pica {
+
+namespace Shader {
+
+using namespace Gen;
+
+typedef void (JitCompiler::*JitFunction)(Instruction instr);
+
+const JitFunction instr_table[64] = {
+ &JitCompiler::Compile_ADD, // add
+ &JitCompiler::Compile_DP3, // dp3
+ &JitCompiler::Compile_DP4, // dp4
+ &JitCompiler::Compile_DPH, // dph
+ nullptr, // unknown
+ &JitCompiler::Compile_EX2, // ex2
+ &JitCompiler::Compile_LG2, // lg2
+ nullptr, // unknown
+ &JitCompiler::Compile_MUL, // mul
+ &JitCompiler::Compile_SGE, // sge
+ &JitCompiler::Compile_SLT, // slt
+ &JitCompiler::Compile_FLR, // flr
+ &JitCompiler::Compile_MAX, // max
+ &JitCompiler::Compile_MIN, // min
+ &JitCompiler::Compile_RCP, // rcp
+ &JitCompiler::Compile_RSQ, // rsq
+ nullptr, // unknown
+ nullptr, // unknown
+ &JitCompiler::Compile_MOVA, // mova
+ &JitCompiler::Compile_MOV, // mov
+ nullptr, // unknown
+ nullptr, // unknown
+ nullptr, // unknown
+ nullptr, // unknown
+ &JitCompiler::Compile_DPH, // dphi
+ nullptr, // unknown
+ &JitCompiler::Compile_SGE, // sgei
+ &JitCompiler::Compile_SLT, // slti
+ nullptr, // unknown
+ nullptr, // unknown
+ nullptr, // unknown
+ nullptr, // unknown
+ nullptr, // unknown
+ &JitCompiler::Compile_NOP, // nop
+ &JitCompiler::Compile_END, // end
+ nullptr, // break
+ &JitCompiler::Compile_CALL, // call
+ &JitCompiler::Compile_CALLC, // callc
+ &JitCompiler::Compile_CALLU, // callu
+ &JitCompiler::Compile_IF, // ifu
+ &JitCompiler::Compile_IF, // ifc
+ &JitCompiler::Compile_LOOP, // loop
+ nullptr, // emit
+ nullptr, // sete
+ &JitCompiler::Compile_JMP, // jmpc
+ &JitCompiler::Compile_JMP, // jmpu
+ &JitCompiler::Compile_CMP, // cmp
+ &JitCompiler::Compile_CMP, // cmp
+ &JitCompiler::Compile_MAD, // madi
+ &JitCompiler::Compile_MAD, // madi
+ &JitCompiler::Compile_MAD, // madi
+ &JitCompiler::Compile_MAD, // madi
+ &JitCompiler::Compile_MAD, // madi
+ &JitCompiler::Compile_MAD, // madi
+ &JitCompiler::Compile_MAD, // madi
+ &JitCompiler::Compile_MAD, // madi
+ &JitCompiler::Compile_MAD, // mad
+ &JitCompiler::Compile_MAD, // mad
+ &JitCompiler::Compile_MAD, // mad
+ &JitCompiler::Compile_MAD, // mad
+ &JitCompiler::Compile_MAD, // mad
+ &JitCompiler::Compile_MAD, // mad
+ &JitCompiler::Compile_MAD, // mad
+ &JitCompiler::Compile_MAD, // mad
+};
+
+// The following is used to alias some commonly used registers. Generally, RAX-RDX and XMM0-XMM3 can
+// be used as scratch registers within a compiler function. The other registers have designated
+// purposes, as documented below:
+
+/// Pointer to the uniform memory
+static const X64Reg UNIFORMS = R9;
+/// The two 32-bit VS address offset registers set by the MOVA instruction
+static const X64Reg ADDROFFS_REG_0 = R10;
+static const X64Reg ADDROFFS_REG_1 = R11;
+/// VS loop count register
+static const X64Reg LOOPCOUNT_REG = R12;
+/// Current VS loop iteration number (we could probably use LOOPCOUNT_REG, but this quicker)
+static const X64Reg LOOPCOUNT = RSI;
+/// Number to increment LOOPCOUNT_REG by on each loop iteration
+static const X64Reg LOOPINC = RDI;
+/// Result of the previous CMP instruction for the X-component comparison
+static const X64Reg COND0 = R13;
+/// Result of the previous CMP instruction for the Y-component comparison
+static const X64Reg COND1 = R14;
+/// Pointer to the UnitState instance for the current VS unit
+static const X64Reg REGISTERS = R15;
+/// SIMD scratch register
+static const X64Reg SCRATCH = XMM0;
+/// Loaded with the first swizzled source register, otherwise can be used as a scratch register
+static const X64Reg SRC1 = XMM1;
+/// Loaded with the second swizzled source register, otherwise can be used as a scratch register
+static const X64Reg SRC2 = XMM2;
+/// Loaded with the third swizzled source register, otherwise can be used as a scratch register
+static const X64Reg SRC3 = XMM3;
+/// Additional scratch register
+static const X64Reg SCRATCH2 = XMM4;
+/// Constant vector of [1.0f, 1.0f, 1.0f, 1.0f], used to efficiently set a vector to one
+static const X64Reg ONE = XMM14;
+/// Constant vector of [-0.f, -0.f, -0.f, -0.f], used to efficiently negate a vector with XOR
+static const X64Reg NEGBIT = XMM15;
+
+// State registers that must not be modified by external functions calls
+// Scratch registers, e.g., SRC1 and SCRATCH, have to be saved on the side if needed
+static const BitSet32 persistent_regs = {
+ UNIFORMS, REGISTERS, // Pointers to register blocks
+ ADDROFFS_REG_0, ADDROFFS_REG_1, LOOPCOUNT_REG, COND0, COND1, // Cached registers
+ ONE+16, NEGBIT+16, // Constants
+};
+
+/// Raw constant for the source register selector that indicates no swizzling is performed
+static const u8 NO_SRC_REG_SWIZZLE = 0x1b;
+/// Raw constant for the destination register enable mask that indicates all components are enabled
+static const u8 NO_DEST_REG_MASK = 0xf;
+
+/**
+ * Loads and swizzles a source register into the specified XMM register.
+ * @param instr VS instruction, used for determining how to load the source register
+ * @param src_num Number indicating which source register to load (1 = src1, 2 = src2, 3 = src3)
+ * @param src_reg SourceRegister object corresponding to the source register to load
+ * @param dest Destination XMM register to store the loaded, swizzled source register
+ */
+void JitCompiler::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, X64Reg dest) {
+ X64Reg src_ptr;
+ size_t src_offset;
+
+ if (src_reg.GetRegisterType() == RegisterType::FloatUniform) {
+ src_ptr = UNIFORMS;
+ src_offset = src_reg.GetIndex() * sizeof(float24) * 4;
+ } else {
+ src_ptr = REGISTERS;
+ src_offset = UnitState<false>::InputOffset(src_reg);
+ }
+
+ int src_offset_disp = (int)src_offset;
+ //ASSERT_MSG(src_offset == src_offset_disp, "Source register offset too large for int type");
+
+ unsigned operand_desc_id;
+ if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD ||
+ instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI) {
+ // The MAD and MADI instructions do not use the address offset registers, so loading the
+ // source is a bit simpler here
+
+ operand_desc_id = instr.mad.operand_desc_id;
+
+ // Load the source
+ MOVAPS(dest, MDisp(src_ptr, src_offset_disp));
+ } else {
+ operand_desc_id = instr.common.operand_desc_id;
+
+ const bool is_inverted = (0 != (instr.opcode.Value().GetInfo().subtype & OpCode::Info::SrcInversed));
+ unsigned offset_src = is_inverted ? 2 : 1;
+
+ if (src_num == offset_src && instr.common.address_register_index != 0) {
+ switch (instr.common.address_register_index) {
+ case 1: // address offset 1
+ MOVAPS(dest, MComplex(src_ptr, ADDROFFS_REG_0, SCALE_1, src_offset_disp));
+ break;
+ case 2: // address offset 2
+ MOVAPS(dest, MComplex(src_ptr, ADDROFFS_REG_1, SCALE_1, src_offset_disp));
+ break;
+ case 3: // address offset 3
+ MOVAPS(dest, MComplex(src_ptr, LOOPCOUNT_REG, SCALE_1, src_offset_disp));
+ break;
+ default:
+ //UNREACHABLE();
+ break;
+ }
+ } else {
+ // Load the source
+ MOVAPS(dest, MDisp(src_ptr, src_offset_disp));
+ }
+ }
+
+ SwizzlePattern swiz = { g_state.vs.swizzle_data[operand_desc_id] };
+
+ // Generate instructions for source register swizzling as needed
+ u8 sel = swiz.GetRawSelector(src_num);
+ if (sel != NO_SRC_REG_SWIZZLE) {
+ // Selector component order needs to be reversed for the SHUFPS instruction
+ sel = ((sel & 0xc0) >> 6) | ((sel & 3) << 6) | ((sel & 0xc) << 2) | ((sel & 0x30) >> 2);
+
+ // Shuffle inputs for swizzle
+ SHUFPS(dest, R(dest), sel);
+ }
+
+ // If the source register should be negated, flip the negative bit using XOR
+ const bool negate[] = { swiz.negate_src1, swiz.negate_src2, swiz.negate_src3 };
+ if (negate[src_num - 1]) {
+ XORPS(dest, R(NEGBIT));
+ }
+}
+
+void JitCompiler::Compile_DestEnable(Instruction instr,X64Reg src) {
+ DestRegister dest;
+ unsigned operand_desc_id;
+ if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD ||
+ instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI) {
+ operand_desc_id = instr.mad.operand_desc_id;
+ dest = instr.mad.dest.Value();
+ } else {
+ operand_desc_id = instr.common.operand_desc_id;
+ dest = instr.common.dest.Value();
+ }
+
+ SwizzlePattern swiz = { g_state.vs.swizzle_data[operand_desc_id] };
+
+ int dest_offset_disp = (int)UnitState<false>::OutputOffset(dest);
+ //ASSERT_MSG(dest_offset_disp == UnitState<false>::OutputOffset(dest), "Destinaton offset too large for int type");
+
+ // If all components are enabled, write the result to the destination register
+ if (swiz.dest_mask == NO_DEST_REG_MASK) {
+ // Store dest back to memory
+ MOVAPS(MDisp(REGISTERS, dest_offset_disp), src);
+
+ } else {
+ // Not all components are enabled, so mask the result when storing to the destination register...
+ MOVAPS(SCRATCH, MDisp(REGISTERS, dest_offset_disp));
+
+ if (Common::GetCPUCaps().sse4_1) {
+ u8 mask = ((swiz.dest_mask & 1) << 3) | ((swiz.dest_mask & 8) >> 3) | ((swiz.dest_mask & 2) << 1) | ((swiz.dest_mask & 4) >> 1);
+ BLENDPS(SCRATCH, R(src), mask);
+ } else {
+ MOVAPS(SCRATCH2, R(src));
+ UNPCKHPS(SCRATCH2, R(SCRATCH)); // Unpack X/Y components of source and destination
+ UNPCKLPS(SCRATCH, R(src)); // Unpack Z/W components of source and destination
+
+ // Compute selector to selectively copy source components to destination for SHUFPS instruction
+ u8 sel = ((swiz.DestComponentEnabled(0) ? 1 : 0) << 0) |
+ ((swiz.DestComponentEnabled(1) ? 3 : 2) << 2) |
+ ((swiz.DestComponentEnabled(2) ? 0 : 1) << 4) |
+ ((swiz.DestComponentEnabled(3) ? 2 : 3) << 6);
+ SHUFPS(SCRATCH, R(SCRATCH2), sel);
+ }
+
+ // Store dest back to memory
+ MOVAPS(MDisp(REGISTERS, dest_offset_disp), SCRATCH);
+ }
+}
+
+void JitCompiler::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch) {
+ MOVAPS(scratch, R(src1));
+ CMPPS(scratch, R(src2), CMP_ORD);
+
+ MULPS(src1, R(src2));
+
+ MOVAPS(src2, R(src1));
+ CMPPS(src2, R(src2), CMP_UNORD);
+
+ XORPS(scratch, R(src2));
+ ANDPS(src1, R(scratch));
+}
+
+void JitCompiler::Compile_EvaluateCondition(Instruction instr) {
+ // Note: NXOR is used below to check for equality
+ switch (instr.flow_control.op) {
+ case Instruction::FlowControlType::Or:
+ MOV(32, R(RAX), R(COND0));
+ MOV(32, R(RBX), R(COND1));
+ XOR(32, R(RAX), Imm32(instr.flow_control.refx.Value() ^ 1));
+ XOR(32, R(RBX), Imm32(instr.flow_control.refy.Value() ^ 1));
+ OR(32, R(RAX), R(RBX));
+ break;
+
+ case Instruction::FlowControlType::And:
+ MOV(32, R(RAX), R(COND0));
+ MOV(32, R(RBX), R(COND1));
+ XOR(32, R(RAX), Imm32(instr.flow_control.refx.Value() ^ 1));
+ XOR(32, R(RBX), Imm32(instr.flow_control.refy.Value() ^ 1));
+ AND(32, R(RAX), R(RBX));
+ break;
+
+ case Instruction::FlowControlType::JustX:
+ MOV(32, R(RAX), R(COND0));
+ XOR(32, R(RAX), Imm32(instr.flow_control.refx.Value() ^ 1));
+ break;
+
+ case Instruction::FlowControlType::JustY:
+ MOV(32, R(RAX), R(COND1));
+ XOR(32, R(RAX), Imm32(instr.flow_control.refy.Value() ^ 1));
+ break;
+ }
+}
+
+void JitCompiler::Compile_UniformCondition(Instruction instr) {
+ int offset = offsetof(decltype(g_state.vs.uniforms), b) + (instr.flow_control.bool_uniform_id * sizeof(bool));
+ CMP(sizeof(bool) * 8, MDisp(UNIFORMS, offset), Imm8(0));
+}
+
+BitSet32 JitCompiler::PersistentCallerSavedRegs() {
+ return persistent_regs & ABI_ALL_CALLER_SAVED;
+}
+
+void JitCompiler::Compile_ADD(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
+ ADDPS(SRC1, R(SRC2));
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_DP3(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
+
+ Compile_SanitizedMul(SRC1, SRC2, SCRATCH);
+
+ MOVAPS(SRC2, R(SRC1));
+ SHUFPS(SRC2, R(SRC2), _MM_SHUFFLE(1, 1, 1, 1));
+
+ MOVAPS(SRC3, R(SRC1));
+ SHUFPS(SRC3, R(SRC3), _MM_SHUFFLE(2, 2, 2, 2));
+
+ SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 0, 0, 0));
+ ADDPS(SRC1, R(SRC2));
+ ADDPS(SRC1, R(SRC3));
+
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_DP4(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
+
+ Compile_SanitizedMul(SRC1, SRC2, SCRATCH);
+
+ MOVAPS(SRC2, R(SRC1));
+ SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(2, 3, 0, 1)); // XYZW -> ZWXY
+ ADDPS(SRC1, R(SRC2));
+
+ MOVAPS(SRC2, R(SRC1));
+ SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 1, 2, 3)); // XYZW -> WZYX
+ ADDPS(SRC1, R(SRC2));
+
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_DPH(Instruction instr) {
+ if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::DPHI) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2);
+ } else {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
+ }
+
+ if (Common::GetCPUCaps().sse4_1) {
+ // Set 4th component to 1.0
+ BLENDPS(SRC1, R(ONE), 0x8); // 0b1000
+ } else {
+ // Set 4th component to 1.0
+ MOVAPS(SCRATCH, R(SRC1));
+ UNPCKHPS(SCRATCH, R(ONE)); // XYZW, 1111 -> Z1__
+ UNPCKLPD(SRC1, R(SCRATCH)); // XYZW, Z1__ -> XYZ1
+ }
+
+ Compile_SanitizedMul(SRC1, SRC2, SCRATCH);
+
+ MOVAPS(SRC2, R(SRC1));
+ SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(2, 3, 0, 1)); // XYZW -> ZWXY
+ ADDPS(SRC1, R(SRC2));
+
+ MOVAPS(SRC2, R(SRC1));
+ SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 1, 2, 3)); // XYZW -> WZYX
+ ADDPS(SRC1, R(SRC2));
+
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_EX2(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ MOVSS(XMM0, R(SRC1));
+
+ ABI_PushRegistersAndAdjustStack(PersistentCallerSavedRegs(), 0);
+ ABI_CallFunction(reinterpret_cast<const void*>(exp2f));
+ ABI_PopRegistersAndAdjustStack(PersistentCallerSavedRegs(), 0);
+
+ SHUFPS(XMM0, R(XMM0), _MM_SHUFFLE(0, 0, 0, 0));
+ MOVAPS(SRC1, R(XMM0));
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_LG2(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ MOVSS(XMM0, R(SRC1));
+
+ ABI_PushRegistersAndAdjustStack(PersistentCallerSavedRegs(), 0);
+ ABI_CallFunction(reinterpret_cast<const void*>(log2f));
+ ABI_PopRegistersAndAdjustStack(PersistentCallerSavedRegs(), 0);
+
+ SHUFPS(XMM0, R(XMM0), _MM_SHUFFLE(0, 0, 0, 0));
+ MOVAPS(SRC1, R(XMM0));
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_MUL(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
+ Compile_SanitizedMul(SRC1, SRC2, SCRATCH);
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_SGE(Instruction instr) {
+ if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::SGEI) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2);
+ } else {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
+ }
+
+ CMPPS(SRC2, R(SRC1), CMP_LE);
+ ANDPS(SRC2, R(ONE));
+
+ Compile_DestEnable(instr, SRC2);
+}
+
+void JitCompiler::Compile_SLT(Instruction instr) {
+ if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::SLTI) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2);
+ } else {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
+ }
+
+ CMPPS(SRC1, R(SRC2), CMP_LT);
+ ANDPS(SRC1, R(ONE));
+
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_FLR(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+
+ if (Common::GetCPUCaps().sse4_1) {
+ ROUNDFLOORPS(SRC1, R(SRC1));
+ } else {
+ CVTPS2DQ(SRC1, R(SRC1));
+ CVTDQ2PS(SRC1, R(SRC1));
+ }
+
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_MAX(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
+ // SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned.
+ MAXPS(SRC1, R(SRC2));
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_MIN(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
+ // SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned.
+ MINPS(SRC1, R(SRC2));
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_MOVA(Instruction instr) {
+ SwizzlePattern swiz = { g_state.vs.swizzle_data[instr.common.operand_desc_id] };
+
+ if (!swiz.DestComponentEnabled(0) && !swiz.DestComponentEnabled(1)) {
+ return; // NoOp
+ }
+
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+
+ // Convert floats to integers using truncation (only care about X and Y components)
+ CVTTPS2DQ(SRC1, R(SRC1));
+
+ // Get result
+ MOVQ_xmm(R(RAX), SRC1);
+
+ // Handle destination enable
+ if (swiz.DestComponentEnabled(0) && swiz.DestComponentEnabled(1)) {
+ // Move and sign-extend low 32 bits
+ MOVSX(64, 32, ADDROFFS_REG_0, R(RAX));
+
+ // Move and sign-extend high 32 bits
+ SHR(64, R(RAX), Imm8(32));
+ MOVSX(64, 32, ADDROFFS_REG_1, R(RAX));
+
+ // Multiply by 16 to be used as an offset later
+ SHL(64, R(ADDROFFS_REG_0), Imm8(4));
+ SHL(64, R(ADDROFFS_REG_1), Imm8(4));
+ } else {
+ if (swiz.DestComponentEnabled(0)) {
+ // Move and sign-extend low 32 bits
+ MOVSX(64, 32, ADDROFFS_REG_0, R(RAX));
+
+ // Multiply by 16 to be used as an offset later
+ SHL(64, R(ADDROFFS_REG_0), Imm8(4));
+ } else if (swiz.DestComponentEnabled(1)) {
+ // Move and sign-extend high 32 bits
+ SHR(64, R(RAX), Imm8(32));
+ MOVSX(64, 32, ADDROFFS_REG_1, R(RAX));
+
+ // Multiply by 16 to be used as an offset later
+ SHL(64, R(ADDROFFS_REG_1), Imm8(4));
+ }
+ }
+}
+
+void JitCompiler::Compile_MOV(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_RCP(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+
+ // TODO(bunnei): RCPSS is a pretty rough approximation, this might cause problems if Pica
+ // performs this operation more accurately. This should be checked on hardware.
+ RCPSS(SRC1, R(SRC1));
+ SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 0, 0, 0)); // XYWZ -> XXXX
+
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_RSQ(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+
+ // TODO(bunnei): RSQRTSS is a pretty rough approximation, this might cause problems if Pica
+ // performs this operation more accurately. This should be checked on hardware.
+ RSQRTSS(SRC1, R(SRC1));
+ SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 0, 0, 0)); // XYWZ -> XXXX
+
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_NOP(Instruction instr) {
+}
+
+void JitCompiler::Compile_END(Instruction instr) {
+ ABI_PopRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
+ RET();
+}
+
+void JitCompiler::Compile_CALL(Instruction instr) {
+ unsigned offset = instr.flow_control.dest_offset;
+ while (offset < (instr.flow_control.dest_offset + instr.flow_control.num_instructions)) {
+ Compile_NextInstr(&offset);
+ }
+}
+
+void JitCompiler::Compile_CALLC(Instruction instr) {
+ Compile_EvaluateCondition(instr);
+ FixupBranch b = J_CC(CC_Z, true);
+ Compile_CALL(instr);
+ SetJumpTarget(b);
+}
+
+void JitCompiler::Compile_CALLU(Instruction instr) {
+ Compile_UniformCondition(instr);
+ FixupBranch b = J_CC(CC_Z, true);
+ Compile_CALL(instr);
+ SetJumpTarget(b);
+}
+
+void JitCompiler::Compile_CMP(Instruction instr) {
+ using Op = Instruction::Common::CompareOpType::Op;
+ Op op_x = instr.common.compare_op.x;
+ Op op_y = instr.common.compare_op.y;
+
+ Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
+ Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
+
+ // SSE doesn't have greater-than (GT) or greater-equal (GE) comparison operators. You need to
+ // emulate them by swapping the lhs and rhs and using LT and LE. NLT and NLE can't be used here
+ // because they don't match when used with NaNs.
+ static const u8 cmp[] = { CMP_EQ, CMP_NEQ, CMP_LT, CMP_LE, CMP_LT, CMP_LE };
+
+ bool invert_op_x = (op_x == Op::GreaterThan || op_x == Op::GreaterEqual);
+ Gen::X64Reg lhs_x = invert_op_x ? SRC2 : SRC1;
+ Gen::X64Reg rhs_x = invert_op_x ? SRC1 : SRC2;
+
+ if (op_x == op_y) {
+ // Compare X-component and Y-component together
+ CMPPS(lhs_x, R(rhs_x), cmp[op_x]);
+ MOVQ_xmm(R(COND0), lhs_x);
+
+ MOV(64, R(COND1), R(COND0));
+ } else {
+ bool invert_op_y = (op_y == Op::GreaterThan || op_y == Op::GreaterEqual);
+ Gen::X64Reg lhs_y = invert_op_y ? SRC2 : SRC1;
+ Gen::X64Reg rhs_y = invert_op_y ? SRC1 : SRC2;
+
+ // Compare X-component
+ MOVAPS(SCRATCH, R(lhs_x));
+ CMPSS(SCRATCH, R(rhs_x), cmp[op_x]);
+
+ // Compare Y-component
+ CMPPS(lhs_y, R(rhs_y), cmp[op_y]);
+
+ MOVQ_xmm(R(COND0), SCRATCH);
+ MOVQ_xmm(R(COND1), lhs_y);
+ }
+
+ SHR(32, R(COND0), Imm8(31));
+ SHR(64, R(COND1), Imm8(63));
+}
+
+void JitCompiler::Compile_MAD(Instruction instr) {
+ Compile_SwizzleSrc(instr, 1, instr.mad.src1, SRC1);
+
+ if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI) {
+ Compile_SwizzleSrc(instr, 2, instr.mad.src2i, SRC2);
+ Compile_SwizzleSrc(instr, 3, instr.mad.src3i, SRC3);
+ } else {
+ Compile_SwizzleSrc(instr, 2, instr.mad.src2, SRC2);
+ Compile_SwizzleSrc(instr, 3, instr.mad.src3, SRC3);
+ }
+
+ Compile_SanitizedMul(SRC1, SRC2, SCRATCH);
+ ADDPS(SRC1, R(SRC3));
+
+ Compile_DestEnable(instr, SRC1);
+}
+
+void JitCompiler::Compile_IF(Instruction instr) {
+ //ASSERT_MSG(instr.flow_control.dest_offset > *offset_ptr, "Backwards if-statements not supported");
+
+ // Evaluate the "IF" condition
+ if (instr.opcode.Value() == OpCode::Id::IFU) {
+ Compile_UniformCondition(instr);
+ } else if (instr.opcode.Value() == OpCode::Id::IFC) {
+ Compile_EvaluateCondition(instr);
+ }
+ FixupBranch b = J_CC(CC_Z, true);
+
+ // Compile the code that corresponds to the condition evaluating as true
+ Compile_Block(instr.flow_control.dest_offset - 1);
+
+ // If there isn't an "ELSE" condition, we are done here
+ if (instr.flow_control.num_instructions == 0) {
+ SetJumpTarget(b);
+ return;
+ }
+
+ FixupBranch b2 = J(true);
+
+ SetJumpTarget(b);
+
+ // This code corresponds to the "ELSE" condition
+ // Comple the code that corresponds to the condition evaluating as false
+ Compile_Block(instr.flow_control.dest_offset + instr.flow_control.num_instructions - 1);
+
+ SetJumpTarget(b2);
+}
+
+void JitCompiler::Compile_LOOP(Instruction instr) {
+ //ASSERT_MSG(instr.flow_control.dest_offset > *offset_ptr, "Backwards loops not supported");
+ //ASSERT_MSG(!looping, "Nested loops not supported");
+
+ looping = true;
+
+ int offset = offsetof(decltype(g_state.vs.uniforms), i) + (instr.flow_control.int_uniform_id * sizeof(Math::Vec4<u8>));
+ MOV(32, R(LOOPCOUNT), MDisp(UNIFORMS, offset));
+ MOV(32, R(LOOPCOUNT_REG), R(LOOPCOUNT));
+ SHR(32, R(LOOPCOUNT_REG), Imm8(8));
+ AND(32, R(LOOPCOUNT_REG), Imm32(0xff)); // Y-component is the start
+ MOV(32, R(LOOPINC), R(LOOPCOUNT));
+ SHR(32, R(LOOPINC), Imm8(16));
+ MOVZX(32, 8, LOOPINC, R(LOOPINC)); // Z-component is the incrementer
+ MOVZX(32, 8, LOOPCOUNT, R(LOOPCOUNT)); // X-component is iteration count
+ ADD(32, R(LOOPCOUNT), Imm8(1)); // Iteration count is X-component + 1
+
+ auto loop_start = GetCodePtr();
+
+ Compile_Block(instr.flow_control.dest_offset);
+
+ ADD(32, R(LOOPCOUNT_REG), R(LOOPINC)); // Increment LOOPCOUNT_REG by Z-component
+ SUB(32, R(LOOPCOUNT), Imm8(1)); // Increment loop count by 1
+ J_CC(CC_NZ, loop_start); // Loop if not equal
+
+ looping = false;
+}
+
+void JitCompiler::Compile_JMP(Instruction instr) {
+ //ASSERT_MSG(instr.flow_control.dest_offset > *offset_ptr, "Backwards jumps not supported");
+
+ if (instr.opcode.Value() == OpCode::Id::JMPC)
+ Compile_EvaluateCondition(instr);
+ else if (instr.opcode.Value() == OpCode::Id::JMPU)
+ Compile_UniformCondition(instr);
+ else
+ {
+ //UNREACHABLE();
+ }
+
+ FixupBranch b = J_CC(CC_NZ, true);
+
+ Compile_Block(instr.flow_control.dest_offset);
+
+ SetJumpTarget(b);
+}
+
+void JitCompiler::Compile_Block(unsigned stop) {
+ // Save current offset pointer
+ unsigned* prev_offset_ptr = offset_ptr;
+ unsigned offset = *prev_offset_ptr;
+
+ while (offset <= stop)
+ Compile_NextInstr(&offset);
+
+ // Restore current offset pointer
+ offset_ptr = prev_offset_ptr;
+ *offset_ptr = offset;
+}
+
+void JitCompiler::Compile_NextInstr(unsigned* offset) {
+ offset_ptr = offset;
+
+ Instruction instr = *(Instruction*)&g_state.vs.program_code[(*offset_ptr)++];
+ OpCode::Id opcode = instr.opcode.Value();
+ auto instr_func = instr_table[static_cast<unsigned>(opcode)];
+
+ if (instr_func) {
+ // JIT the instruction!
+ ((*this).*instr_func)(instr);
+ } else {
+ // Unhandled instruction
+ LOG_CRITICAL(HW_GPU, "Unhandled instruction: 0x%02x (0x%08x)",
+ instr.opcode.Value().EffectiveOpCode(), instr.hex);
+ }
+}
+
+CompiledShader* JitCompiler::Compile() {
+ const u8* start = GetCodePtr();
+ unsigned offset = g_state.regs.vs.main_offset;
+
+ // The stack pointer is 8 modulo 16 at the entry of a procedure
+ ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
+
+ MOV(PTRBITS, R(REGISTERS), R(ABI_PARAM1));
+ MOV(PTRBITS, R(UNIFORMS), ImmPtr(&g_state.vs.uniforms));
+
+ // Zero address/loop registers
+ XOR(64, R(ADDROFFS_REG_0), R(ADDROFFS_REG_0));
+ XOR(64, R(ADDROFFS_REG_1), R(ADDROFFS_REG_1));
+ XOR(64, R(LOOPCOUNT_REG), R(LOOPCOUNT_REG));
+
+ // Used to set a register to one
+ static const __m128 one = { 1.f, 1.f, 1.f, 1.f };
+ MOV(PTRBITS, R(RAX), ImmPtr(&one));
+ MOVAPS(ONE, MatR(RAX));
+
+ // Used to negate registers
+ static const __m128 neg = { -0.f, -0.f, -0.f, -0.f };
+ MOV(PTRBITS, R(RAX), ImmPtr(&neg));
+ MOVAPS(NEGBIT, MatR(RAX));
+
+ looping = false;
+
+ while (offset < g_state.vs.program_code.size()) {
+ Compile_NextInstr(&offset);
+ }
+
+ return (CompiledShader*)start;
+}
+
+JitCompiler::JitCompiler() {
+ AllocCodeSpace(1024 * 1024 * 4);
+}
+
+void JitCompiler::Clear() {
+ ClearCodeSpace();
+}
+
+} // namespace Shader
+
+} // namespace Pica
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <citraimport\nihstro/shader_bytecode.h>
+
+#include "citraimport\common/x64/emitter.h"
+
+#include "citraimport\GPU\video_core/pica.h"
+#include "citraimport\GPU\video_core/shader/shader.h"
+
+using nihstro::Instruction;
+using nihstro::OpCode;
+using nihstro::SwizzlePattern;
+
+namespace Pica {
+
+namespace Shader {
+
+using CompiledShader = void(void* registers);
+
+/**
+ * This class implements the shader JIT compiler. It recompiles a Pica shader program into x86_64
+ * code that can be executed on the host machine directly.
+ */
+class JitCompiler : public Gen::XCodeBlock {
+public:
+ JitCompiler();
+
+ CompiledShader* Compile();
+
+ void Clear();
+
+ void Compile_ADD(Instruction instr);
+ void Compile_DP3(Instruction instr);
+ void Compile_DP4(Instruction instr);
+ void Compile_DPH(Instruction instr);
+ void Compile_EX2(Instruction instr);
+ void Compile_LG2(Instruction instr);
+ void Compile_MUL(Instruction instr);
+ void Compile_SGE(Instruction instr);
+ void Compile_SLT(Instruction instr);
+ void Compile_FLR(Instruction instr);
+ void Compile_MAX(Instruction instr);
+ void Compile_MIN(Instruction instr);
+ void Compile_RCP(Instruction instr);
+ void Compile_RSQ(Instruction instr);
+ void Compile_MOVA(Instruction instr);
+ void Compile_MOV(Instruction instr);
+ void Compile_NOP(Instruction instr);
+ void Compile_END(Instruction instr);
+ void Compile_CALL(Instruction instr);
+ void Compile_CALLC(Instruction instr);
+ void Compile_CALLU(Instruction instr);
+ void Compile_IF(Instruction instr);
+ void Compile_LOOP(Instruction instr);
+ void Compile_JMP(Instruction instr);
+ void Compile_CMP(Instruction instr);
+ void Compile_MAD(Instruction instr);
+
+private:
+ void Compile_Block(unsigned stop);
+ void Compile_NextInstr(unsigned* offset);
+
+ void Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, Gen::X64Reg dest);
+ void Compile_DestEnable(Instruction instr, Gen::X64Reg dest);
+
+ /**
+ * Compiles a `MUL src1, src2` operation, properly handling the PICA semantics when multiplying
+ * zero by inf. Clobbers `src2` and `scratch`.
+ */
+ void Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch);
+
+ void Compile_EvaluateCondition(Instruction instr);
+ void Compile_UniformCondition(Instruction instr);
+
+ BitSet32 PersistentCallerSavedRegs();
+
+ /// Pointer to the variable that stores the current Pica code offset. Used to handle nested code blocks.
+ unsigned* offset_ptr = nullptr;
+
+ /// Set to true if currently in a loop, used to check for the existence of nested loops
+ bool looping = false;
+};
+
+} // Shader
+
+} // Pica
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstdio>
+#include <cstring>
+
+#include "citraimport\GPU\video_core/utils.h"
+
+namespace VideoCore {
+
+/**
+ * Dumps a texture to TGA
+ * @param filename String filename to dump texture to
+ * @param width Width of texture in pixels
+ * @param height Height of texture in pixels
+ * @param raw_data Raw RGBA8 texture data to dump
+ * @todo This should be moved to some general purpose/common code
+ */
+void DumpTGA(std::string filename, short width, short height, u8* raw_data) {
+ TGAHeader hdr = {0, 0, 2, 0, 0, 0, 0, width, height, 24, 0};
+ FILE* fout = fopen(filename.c_str(), "wb");
+
+ fwrite(&hdr, sizeof(TGAHeader), 1, fout);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ putc(raw_data[(3 * (y * width)) + (3 * x) + 0], fout); // b
+ putc(raw_data[(3 * (y * width)) + (3 * x) + 1], fout); // g
+ putc(raw_data[(3 * (y * width)) + (3 * x) + 2], fout); // r
+ }
+ }
+
+ fclose(fout);
+}
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+
+#include "citraimport\common/common_types.h"
+
+namespace VideoCore {
+
+/// Structure for the TGA texture format (for dumping)
+struct TGAHeader {
+ char idlength;
+ char colormaptype;
+ char datatypecode;
+ short int colormaporigin;
+ short int colormaplength;
+ short int x_origin;
+ short int y_origin;
+ short width;
+ short height;
+ char bitsperpixel;
+ char imagedescriptor;
+};
+
+/**
+ * Dumps a texture to TGA
+ * @param filename String filename to dump texture to
+ * @param width Width of texture in pixels
+ * @param height Height of texture in pixels
+ * @param raw_data Raw RGBA8 texture data to dump
+ * @todo This should be moved to some general purpose/common code
+ */
+void DumpTGA(std::string filename, short width, short height, u8* raw_data);
+
+/**
+ * Interleave the lower 3 bits of each coordinate to get the intra-block offsets, which are
+ * arranged in a Z-order curve. More details on the bit manipulation at:
+ * https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
+ */
+static inline u32 MortonInterleave(u32 x, u32 y) {
+ u32 i = (x & 7) | ((y & 7) << 8); // ---- -210
+ i = (i ^ (i << 2)) & 0x1313; // ---2 --10
+ i = (i ^ (i << 1)) & 0x1515; // ---2 -1-0
+ i = (i | (i >> 7)) & 0x3F;
+ return i;
+}
+
+/**
+ * Calculates the offset of the position of the pixel in Morton order
+ */
+static inline u32 GetMortonOffset(u32 x, u32 y, u32 bytes_per_pixel) {
+ // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each
+ // of which is composed of four 2x2 subtiles each of which is composed of four texels.
+ // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g.
+ // texels are laid out in a 2x2 subtile like this:
+ // 2 3
+ // 0 1
+ //
+ // The full 8x8 tile has the texels arranged like this:
+ //
+ // 42 43 46 47 58 59 62 63
+ // 40 41 44 45 56 57 60 61
+ // 34 35 38 39 50 51 54 55
+ // 32 33 36 37 48 49 52 53
+ // 10 11 14 15 26 27 30 31
+ // 08 09 12 13 24 25 28 29
+ // 02 03 06 07 18 19 22 23
+ // 00 01 04 05 16 17 20 21
+ //
+ // This pattern is what's called Z-order curve, or Morton order.
+
+ const unsigned int block_height = 8;
+ const unsigned int coarse_x = x & ~7;
+
+ u32 i = VideoCore::MortonInterleave(x, y);
+
+ const unsigned int offset = coarse_x * block_height;
+
+ return (i + offset) * bytes_per_pixel;
+}
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+
+#include "citraimport\GPU\video_core/pica.h"
+#include "citraimport\GPU\video_core/renderer_base.h"
+#include "citraimport\GPU\video_core/video_core.h"
+#include "citraimport\GPU\video_core/renderer_opengl/renderer_opengl.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Video Core namespace
+
+namespace VideoCore {
+
+EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window
+RendererBase* g_renderer = nullptr; ///< Renderer plugin
+
+std::atomic<bool> g_hw_renderer_enabled;
+std::atomic<bool> g_shader_jit_enabled;
+
+/// Initialize the video core
+void Init(EmuWindow* emu_window) {
+ Pica::Init();
+
+ g_emu_window = emu_window;
+ g_renderer = new RendererOpenGL();
+ g_renderer->SetWindow(g_emu_window);
+ g_renderer->Init();
+
+ LOG_DEBUG(Render, "initialized OK");
+}
+
+/// Shutdown the video core
+void Shutdown() {
+ Pica::Shutdown();
+
+ delete g_renderer;
+
+ LOG_DEBUG(Render, "shutdown OK");
+}
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+
+class EmuWindow;
+class RendererBase;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Video Core namespace
+
+namespace VideoCore {
+
+// 3DS Video Constants
+// -------------------
+
+// NOTE: The LCDs actually rotate the image 90 degrees when displaying. Because of that the
+// framebuffers in video memory are stored in column-major order and rendered sideways, causing
+// the widths and heights of the framebuffers read by the LCD to be switched compared to the
+// heights and widths of the screens listed here.
+static const int kScreenTopWidth = 400; ///< 3DS top screen width
+static const int kScreenTopHeight = 240; ///< 3DS top screen height
+static const int kScreenBottomWidth = 320; ///< 3DS bottom screen width
+static const int kScreenBottomHeight = 240; ///< 3DS bottom screen height
+
+// Video core renderer
+// ---------------------
+
+extern RendererBase* g_renderer; ///< Renderer plugin
+extern EmuWindow* g_emu_window; ///< Emu window
+
+// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from qt ui)
+extern std::atomic<bool> g_hw_renderer_enabled;
+extern std::atomic<bool> g_shader_jit_enabled;
+
+/// Start the video core
+void Start();
+
+/// Initialize the video core
+void Init(EmuWindow* emu_window);
+
+/// Shutdown the video core
+void Shutdown();
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <tuple>
+#include <utility>
+
+#include "citraimport\math_util.h"
+#include "common.h"
+
+/*#include "common/common_types.h"
+#include "common/math_util.h"
+
+#include "core/hle/service/hid/hid.h"*/
+
+namespace KeyMap {
+struct HostDeviceKey;
+}
+
+namespace VideoCore {
+
+ // 3DS Video Constants
+ // -------------------
+
+ // NOTE: The LCDs actually rotate the image 90 degrees when displaying. Because of that the
+ // framebuffers in video memory are stored in column-major order and rendered sideways, causing
+ // the widths and heights of the framebuffers read by the LCD to be switched compared to the
+ // heights and widths of the screens listed here.
+ static const int kScreenTopWidth = 400; ///< 3DS top screen width
+ static const int kScreenTopHeight = 240; ///< 3DS top screen height
+ static const int kScreenBottomWidth = 320; ///< 3DS bottom screen width
+ static const int kScreenBottomHeight = 240; ///< 3DS bottom screen height
+
+} // namespace
+
+
+
+/**
+ * Abstraction class used to provide an interface between emulation code and the frontend
+ * (e.g. SDL, QGLWidget, GLFW, etc...).
+ *
+ * Design notes on the interaction between EmuWindow and the emulation core:
+ * - Generally, decisions on anything visible to the user should be left up to the GUI.
+ * For example, the emulation core should not try to dictate some window title or size.
+ * This stuff is not the core's business and only causes problems with regards to thread-safety
+ * anyway.
+ * - Under certain circumstances, it may be desirable for the core to politely request the GUI
+ * to set e.g. a minimum window size. However, the GUI should always be free to ignore any
+ * such hints.
+ * - EmuWindow may expose some of its state as read-only to the emulation core, however care
+ * should be taken to make sure the provided information is self-consistent. This requires
+ * some sort of synchronization (most of this is still a TODO).
+ * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
+ * re-read the upper points again and think about it if you don't see this.
+ */
+class EmuWindow
+{
+public:
+ /// Data structure to store emuwindow configuration
+ struct WindowConfig {
+ bool fullscreen;
+ int res_width;
+ int res_height;
+ std::pair<unsigned,unsigned> min_client_area_size;
+ };
+
+ /// Describes the layout of the window framebuffer (size and top/bottom screen positions)
+ struct FramebufferLayout {
+
+ /**
+ * Factory method for constructing a default FramebufferLayout
+ * @param width Window framebuffer width in pixels
+ * @param height Window framebuffer height in pixels
+ * @return Newly created FramebufferLayout object with default screen regions initialized
+ */
+ static FramebufferLayout DefaultScreenLayout(unsigned width, unsigned height);
+
+ unsigned width;
+ unsigned height;
+ MathUtil::Rectangle<unsigned> top_screen;
+ MathUtil::Rectangle<unsigned> bottom_screen;
+ };
+
+ /// Swap buffers to display the next frame
+ virtual void SwapBuffers() = 0;
+
+ /// Polls window events
+ virtual void PollEvents() = 0;
+
+ /// Makes the graphics context current for the caller thread
+ virtual void MakeCurrent() = 0;
+
+ /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
+ virtual void DoneCurrent() = 0;
+
+ virtual void ReloadSetKeymaps() = 0;
+
+ /// Signals a key press action to the HID module
+ void KeyPressed(int key);
+
+ /// Signals a key release action to the HID module
+ void KeyReleased(int key);
+
+ /**
+ * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
+ * @param framebuffer_x Framebuffer x-coordinate that was pressed
+ * @param framebuffer_y Framebuffer y-coordinate that was pressed
+ */
+ void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y);
+
+ /// Signal that a touch released event has occurred (e.g. mouse click released)
+ void TouchReleased();
+
+ /**
+ * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
+ * @param framebuffer_x Framebuffer x-coordinate
+ * @param framebuffer_y Framebuffer y-coordinate
+ */
+ void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
+
+ /**
+ * Gets the current pad state (which buttons are pressed and the circle pad direction).
+ * @note This should be called by the core emu thread to get a state set by the window thread.
+ * @todo Fix this function to be thread-safe.
+ * @return PadState object indicating the current pad state
+ */
+ /*const Service::HID::PadState GetPadState() const {
+ return pad_state;
+ }*/
+
+ /**
+ * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
+ * @note This should be called by the core emu thread to get a state set by the window thread.
+ * @todo Fix this function to be thread-safe.
+ * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
+ * `pressed` is true if the touch screen is currently being pressed
+ */
+ const std::tuple<u16, u16, bool> GetTouchState() const {
+ return std::make_tuple(touch_x, touch_y, touch_pressed);
+ }
+
+ /**
+ * Returns currently active configuration.
+ * @note Accesses to the returned object need not be consistent because it may be modified in another thread
+ */
+ const WindowConfig& GetActiveConfig() const {
+ return active_config;
+ }
+
+ /**
+ * Requests the internal configuration to be replaced by the specified argument at some point in the future.
+ * @note This method is thread-safe, because it delays configuration changes to the GUI event loop. Hence there is no guarantee on when the requested configuration will be active.
+ */
+ void SetConfig(const WindowConfig& val) {
+ config = val;
+ }
+
+ /**
+ * Gets the framebuffer layout (width, height, and screen regions)
+ * @note This method is thread-safe
+ */
+ const FramebufferLayout& GetFramebufferLayout() const {
+ return framebuffer_layout;
+ }
+
+protected:
+ EmuWindow() {
+ // TODO: Find a better place to set this.
+ config.min_client_area_size = std::make_pair(400u, 480u);
+ active_config = config;
+ //pad_state.hex = 0;
+ touch_x = 0;
+ touch_y = 0;
+ touch_pressed = false;
+ }
+ virtual ~EmuWindow() {}
+
+ /**
+ * Processes any pending configuration changes from the last SetConfig call.
+ * This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration
+ * field changed.
+ * @note Implementations will usually want to call this from the GUI thread.
+ * @todo Actually call this in existing implementations.
+ */
+ void ProcessConfigurationChanges() {
+ // TODO: For proper thread safety, we should eventually implement a proper
+ // multiple-writer/single-reader queue...
+
+ if (config.min_client_area_size != active_config.min_client_area_size) {
+ OnMinimalClientAreaChangeRequest(config.min_client_area_size);
+ config.min_client_area_size = active_config.min_client_area_size;
+ }
+ }
+
+ /**
+ * Update framebuffer layout with the given parameter.
+ * @note EmuWindow implementations will usually use this in window resize event handlers.
+ */
+ void NotifyFramebufferLayoutChanged(const FramebufferLayout& layout) {
+ framebuffer_layout = layout;
+ }
+
+ /**
+ * Update internal client area size with the given parameter.
+ * @note EmuWindow implementations will usually use this in window resize event handlers.
+ */
+ void NotifyClientAreaSizeChanged(const std::pair<unsigned,unsigned>& size) {
+ client_area_width = size.first;
+ client_area_height = size.second;
+ }
+
+private:
+ /**
+ * Handler called when the minimal client area was requested to be changed via SetConfig.
+ * For the request to be honored, EmuWindow implementations will usually reimplement this function.
+ */
+ virtual void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
+ // By default, ignore this request and do nothing.
+ }
+
+ FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
+
+ unsigned client_area_width; ///< Current client width, should be set by window impl.
+ unsigned client_area_height; ///< Current client height, should be set by window impl.
+
+ WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges)
+ WindowConfig active_config; ///< Internal active configuration
+
+ bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false
+
+ u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
+ u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
+
+ /**
+ * Clip the provided coordinates to be inside the touchscreen area.
+ */
+ std::tuple<unsigned,unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y);
+
+ //Service::HID::PadState pad_state;
+};
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cstdlib>
+#include <string>
+
+// Let’s use our own GL header, instead of one from GLFW.
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include "emu_window_glfw.h"
+
+extern "C" void citraFireInterrupt(int id);
+extern "C" int citraPressedkey;
+extern "C" bool citraSettingSkipGSP;
+
+EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) {
+ return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win));
+}
+
+void EmuWindow_GLFW::OnMouseButtonEvent(GLFWwindow* win, int button, int action, int mods) {
+ if (button == GLFW_MOUSE_BUTTON_LEFT) {
+ auto emu_window = GetEmuWindow(win);
+ auto layout = emu_window->GetFramebufferLayout();
+ double x, y;
+ glfwGetCursorPos(win, &x, &y);
+
+ /*if (action == GLFW_PRESS) //hid todo
+ emu_window->TouchPressed(static_cast<unsigned>(x), static_cast<unsigned>(y));
+ else if (action == GLFW_RELEASE)
+ emu_window->TouchReleased();*/
+ }
+}
+
+void EmuWindow_GLFW::OnCursorPosEvent(GLFWwindow* win, double x, double y) {
+ //GetEmuWindow(win)->TouchMoved(static_cast<unsigned>(std::max(x, 0.0)), static_cast<unsigned>(std::max(y, 0.0))); //hid todo
+}
+
+static u32 toHidBit(int val)
+{
+ switch (val)
+ {
+ case GLFW_KEY_A:
+ return 0x1;
+ case GLFW_KEY_B:
+ return 0x2;
+ case GLFW_KEY_Q: //Select
+ return 0x4;
+ case GLFW_KEY_W: //Start
+ return 0x8;
+ case GLFW_KEY_RIGHT:
+ return 0x10;
+ case GLFW_KEY_LEFT:
+ return 0x20;
+ case GLFW_KEY_UP:
+ return 0x40;
+ case GLFW_KEY_DOWN:
+ return 0x80;
+ case GLFW_KEY_E: //R
+ return 0x100;
+ case GLFW_KEY_R: //L
+ return 0x200;
+ case GLFW_KEY_F: //X
+ return 0x400;
+ case GLFW_KEY_T: //Y
+ return 0x800;
+ default:
+ return 0;
+ }
+}
+
+/// Called by GLFW when a key event occurs
+void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) {
+ auto emu_window = GetEmuWindow(win);
+ int keyboard_id = emu_window->keyboard_id;
+
+ if (action == GLFW_PRESS) { //todo hid
+ //citraFireInterrupt(0x6A);
+ citraPressedkey |= toHidBit(key);
+ if (key == GLFW_KEY_Z)
+ {
+ if (citraSettingSkipGSP)
+ {
+ citraSettingSkipGSP = false;
+ printf("frameskip off\n");
+ }
+ else
+ {
+ citraSettingSkipGSP = true;
+ printf("frameskip on\n");
+ }
+ }
+ } else if (action == GLFW_RELEASE) {
+ //citraFireInterrupt(0x6A);
+ citraPressedkey &= toHidBit(key);
+ }
+}
+
+/// Whether the window is still open, and a close request hasn't yet been sent
+const bool EmuWindow_GLFW::IsOpen() {
+ return glfwWindowShouldClose(m_render_window) == 0;
+}
+
+void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) {
+ GetEmuWindow(win)->NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
+}
+
+void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) {
+ // NOTE: GLFW provides no proper way to set a minimal window size.
+ // Hence, we just ignore the corresponding EmuWindow hint.
+ OnFramebufferResizeEvent(win, width, height);
+}
+
+extern bool novideo;
+/// EmuWindow_GLFW constructor
+EmuWindow_GLFW::EmuWindow_GLFW() {
+ //keyboard_id = KeyMap::NewDeviceId();
+
+ ReloadSetKeymaps();
+
+ glfwSetErrorCallback([](int error, const char *desc){
+ LOG( "GLFW 0x%08x: %s", error, desc);
+ });
+
+ // Initialize the window
+ if(!glfwInit()) {
+ LOG("Failed to initialize GLFW! Exiting...");
+ return;
+ }
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ // GLFW on OSX requires these window hints to be set to create a 3.2+ GL context.
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+ std::string window_title = "XDS";
+ m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
+ (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
+ window_title.c_str(), nullptr, nullptr);
+
+ if (m_render_window == nullptr) {
+ LOG("Failed to create GLFW window! Exiting...");
+ return;
+ }
+
+ glfwSetWindowUserPointer(m_render_window, this);
+
+ // Notify base interface about window state
+ int width, height;
+ glfwGetFramebufferSize(m_render_window, &width, &height);
+ OnFramebufferResizeEvent(m_render_window, width, height);
+
+ glfwGetWindowSize(m_render_window, &width, &height);
+ OnClientAreaResizeEvent(m_render_window, width, height);
+
+ // Setup callbacks
+ glfwSetKeyCallback(m_render_window, OnKeyEvent);
+ glfwSetMouseButtonCallback(m_render_window, OnMouseButtonEvent);
+ glfwSetCursorPosCallback(m_render_window, OnCursorPosEvent);
+ glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent);
+ glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent);
+
+ DoneCurrent();
+ novideo = false;
+}
+
+/// EmuWindow_GLFW destructor
+EmuWindow_GLFW::~EmuWindow_GLFW() {
+ glfwTerminate();
+}
+
+/// Swap buffers to display the next frame
+void EmuWindow_GLFW::SwapBuffers() {
+ glfwSwapBuffers(m_render_window);
+}
+
+/// Polls window events
+void EmuWindow_GLFW::PollEvents() {
+ glfwPollEvents();
+}
+
+/// Makes the GLFW OpenGL context current for the caller thread
+void EmuWindow_GLFW::MakeCurrent() {
+ glfwMakeContextCurrent(m_render_window);
+}
+
+/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
+void EmuWindow_GLFW::DoneCurrent() {
+ glfwMakeContextCurrent(nullptr);
+}
+
+void EmuWindow_GLFW::ReloadSetKeymaps() {
+ /*for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
+ KeyMap::SetKeyMapping({Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, Service::HID::pad_mapping[i]);
+ }*/
+}
+
+void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
+ std::pair<int,int> current_size;
+ glfwGetWindowSize(m_render_window, ¤t_size.first, ¤t_size.second);
+
+ int new_width = std::max(current_size.first, (int)minimal_size.first);
+ int new_height = std::max(current_size.second, (int)minimal_size.second);
+
+ if (current_size != std::make_pair(new_width, new_height))
+ glfwSetWindowSize(m_render_window, new_width, new_height);
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <utility>
+
+#include "emu_window.h"
+
+struct GLFWwindow;
+
+class EmuWindow_GLFW : public EmuWindow {
+public:
+ EmuWindow_GLFW();
+ ~EmuWindow_GLFW();
+
+ /// Swap buffers to display the next frame
+ void SwapBuffers() override;
+
+ /// Polls window events
+ void PollEvents() override;
+
+ /// Makes the graphics context current for the caller thread
+ void MakeCurrent() override;
+
+ /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
+ void DoneCurrent() override;
+
+ static void OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods);
+
+ static void OnMouseButtonEvent(GLFWwindow* window, int button, int action, int mods);
+
+ static void OnCursorPosEvent(GLFWwindow* window, double x, double y);
+
+ /// Whether the window is still open, and a close request hasn't yet been sent
+ const bool IsOpen();
+
+ static void OnClientAreaResizeEvent(GLFWwindow* win, int width, int height);
+
+ static void OnFramebufferResizeEvent(GLFWwindow* win, int width, int height);
+
+ void ReloadSetKeymaps() override;
+
+private:
+ void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override;
+
+ static EmuWindow_GLFW* GetEmuWindow(GLFWwindow* win);
+
+ GLFWwindow* m_render_window; ///< Internal GLFW render window
+
+ /// Device id of keyboard for use with KeyMap
+ int keyboard_id;
+};
--- /dev/null
+# Generate cpp with Git revision from template
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY)
+
+set(SRCS
+ break_points.cpp
+ emu_window.cpp
+ file_util.cpp
+ hash.cpp
+ key_map.cpp
+ logging/filter.cpp
+ logging/text_formatter.cpp
+ logging/backend.cpp
+ memory_util.cpp
+ microprofile.cpp
+ misc.cpp
+ profiler.cpp
+ scm_rev.cpp
+ string_util.cpp
+ symbols.cpp
+ thread.cpp
+ timer.cpp
+ )
+
+set(HEADERS
+ assert.h
+ bit_field.h
+ bit_set.h
+ break_points.h
+ chunk_file.h
+ code_block.h
+ color.h
+ common_funcs.h
+ common_paths.h
+ common_types.h
+ emu_window.h
+ file_util.h
+ hash.h
+ key_map.h
+ linear_disk_cache.h
+ logging/text_formatter.h
+ logging/filter.h
+ logging/log.h
+ logging/backend.h
+ make_unique.h
+ math_util.h
+ memory_util.h
+ microprofile.h
+ microprofileui.h
+ platform.h
+ profiler.h
+ profiler_reporting.h
+ scm_rev.h
+ scope_exit.h
+ string_util.h
+ swap.h
+ symbols.h
+ synchronized_wrapper.h
+ thread.h
+ thread_queue_list.h
+ timer.h
+ vector_math.h
+ )
+
+if(ARCHITECTURE_x86_64)
+ set(SRCS ${SRCS}
+ x64/abi.cpp
+ x64/cpu_detect.cpp
+ x64/emitter.cpp)
+
+ set(HEADERS ${HEADERS}
+ x64/abi.h
+ x64/cpu_detect.h
+ x64/emitter.h)
+endif()
+
+create_directory_groups(${SRCS} ${HEADERS})
+
+add_library(common STATIC ${SRCS} ${HEADERS})
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstdlib>
+
+#include "citraimport\common/common_funcs.h"
+#include "citraimport\common/logging/log.h"
+
+// For asserts we'd like to keep all the junk executed when an assert happens away from the
+// important code in the function. One way of doing this is to put all the relevant code inside a
+// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to
+// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper
+// template that calls the lambda. This seems to generate an extra instruction at the call-site
+// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good
+// enough for our purposes.
+template <typename Fn>
+#if defined(_MSC_VER)
+ __declspec(noinline, noreturn)
+#elif defined(__GNUC__)
+ __attribute__((noinline, noreturn, cold))
+#endif
+static void assert_noinline_call(const Fn& fn) {
+ fn();
+ Crash();
+ exit(1); // Keeps GCC's mouth shut about this actually returning
+}
+
+#define ASSERT(_a_) \
+ do if (!(_a_)) { assert_noinline_call([] { \
+ LOG_CRITICAL(Debug, "Assertion Failed!"); \
+ }); } while (0)
+
+#define ASSERT_MSG(_a_, ...) \
+ do if (!(_a_)) { assert_noinline_call([&] { \
+ LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
+ }); } while (0)
+
+#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
+
+#ifdef _DEBUG
+#define DEBUG_ASSERT(_a_) ASSERT(_a_)
+#define DEBUG_ASSERT_MSG(_a_, ...) ASSERT_MSG(_a_, __VA_ARGS__)
+#else // not debug
+#define DEBUG_ASSERT(_a_)
+#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
+#endif
+
+#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!")
--- /dev/null
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+#pragma once
+
+#include <cstddef>
+#include <limits>
+#include <type_traits>
+
+#include "citraimport\common/common_funcs.h"
+
+/*
+ * Abstract bitfield class
+ *
+ * Allows endianness-independent access to individual bitfields within some raw
+ * integer value. The assembly generated by this class is identical to the
+ * usage of raw bitfields, so it's a perfectly fine replacement.
+ *
+ * For BitField<X,Y,Z>, X is the distance of the bitfield to the LSB of the
+ * raw value, Y is the length in bits of the bitfield. Z is an integer type
+ * which determines the sign of the bitfield. Z must have the same size as the
+ * raw integer.
+ *
+ *
+ * General usage:
+ *
+ * Create a new union with the raw integer value as a member.
+ * Then for each bitfield you want to expose, add a BitField member
+ * in the union. The template parameters are the bit offset and the number
+ * of desired bits.
+ *
+ * Changes in the bitfield members will then get reflected in the raw integer
+ * value and vice-versa.
+ *
+ *
+ * Sample usage:
+ *
+ * union SomeRegister
+ * {
+ * u32 hex;
+ *
+ * BitField<0,7,u32> first_seven_bits; // unsigned
+ * BitField<7,8,u32> next_eight_bits; // unsigned
+ * BitField<3,15,s32> some_signed_fields; // signed
+ * };
+ *
+ * This is equivalent to the little-endian specific code:
+ *
+ * union SomeRegister
+ * {
+ * u32 hex;
+ *
+ * struct
+ * {
+ * u32 first_seven_bits : 7;
+ * u32 next_eight_bits : 8;
+ * };
+ * struct
+ * {
+ * u32 : 3; // padding
+ * s32 some_signed_fields : 15;
+ * };
+ * };
+ *
+ *
+ * Caveats:
+ *
+ * 1)
+ * BitField provides automatic casting from and to the storage type where
+ * appropriate. However, when using non-typesafe functions like printf, an
+ * explicit cast must be performed on the BitField object to make sure it gets
+ * passed correctly, e.g.:
+ * printf("Value: %d", (s32)some_register.some_signed_fields);
+ *
+ * 2)
+ * Not really a caveat, but potentially irritating: This class is used in some
+ * packed structures that do not guarantee proper alignment. Therefore we have
+ * to use #pragma pack here not to pack the members of the class, but instead
+ * to break GCC's assumption that the members of the class are aligned on
+ * sizeof(StorageType).
+ * TODO(neobrain): Confirm that this is a proper fix and not just masking
+ * symptoms.
+ */
+#pragma pack(1)
+template<std::size_t position, std::size_t bits, typename T>
+struct BitField
+{
+private:
+ // This constructor might be considered ambiguous:
+ // Would it initialize the storage or just the bitfield?
+ // Hence, delete it. Use the assignment operator to set bitfield values!
+ BitField(T val) = delete;
+
+public:
+ // Force default constructor to be created
+ // so that we can use this within unions
+ BitField() = default;
+
+#ifndef _WIN32
+ // We explicitly delete the copy assigment operator here, because the
+ // default copy assignment would copy the full storage value, rather than
+ // just the bits relevant to this particular bit field.
+ // Ideally, we would just implement the copy assignment to copy only the
+ // relevant bits, but this requires compiler support for unrestricted
+ // unions.
+ // MSVC 2013 has no support for this, hence we disable this code on
+ // Windows (so that the default copy assignment operator will be used).
+ // For any C++11 conformant compiler we delete the operator to make sure
+ // we never use this inappropriate operator to begin with.
+ // TODO: Implement this operator properly once all target compilers
+ // support unrestricted unions.
+ BitField& operator=(const BitField&) = delete;
+#endif
+
+ FORCE_INLINE BitField& operator=(T val)
+ {
+ Assign(val);
+ return *this;
+ }
+
+ FORCE_INLINE operator T() const
+ {
+ return Value();
+ }
+
+ FORCE_INLINE void Assign(const T& value) {
+ storage = (storage & ~GetMask()) | (((StorageType)value << position) & GetMask());
+ }
+
+ FORCE_INLINE T Value() const
+ {
+ if (std::numeric_limits<T>::is_signed)
+ {
+ std::size_t shift = 8 * sizeof(T)-bits;
+ return (T)((storage << (shift - position)) >> shift);
+ }
+ else
+ {
+ return (T)((storage & GetMask()) >> position);
+ }
+ }
+
+ // TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
+ FORCE_INLINE bool ToBool() const
+ {
+ return Value() != 0;
+ }
+
+private:
+ // StorageType is T for non-enum types and the underlying type of T if
+ // T is an enumeration. Note that T is wrapped within an enable_if in the
+ // former case to workaround compile errors which arise when using
+ // std::underlying_type<T>::type directly.
+ typedef typename std::conditional < std::is_enum<T>::value,
+ std::underlying_type<T>,
+ std::enable_if < true, T >> ::type::type StorageType;
+
+ // Unsigned version of StorageType
+ typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
+
+ FORCE_INLINE StorageType GetMask() const
+ {
+ return (((StorageTypeU)~0) >> (8 * sizeof(T)-bits)) << position;
+ }
+
+ StorageType storage;
+
+ static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
+
+ // And, you know, just in case people specify something stupid like bits=position=0x80000000
+ static_assert(position < 8 * sizeof(T), "Invalid position");
+ static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
+ static_assert(bits > 0, "Invalid number of bits");
+ static_assert(std::is_standard_layout<T>::value, "Invalid base type");
+};
+#pragma pack()
--- /dev/null
+// This file is under the public domain.
+
+#pragma once
+
+#include <cstddef>
+#ifdef _WIN32
+#include <intrin.h>
+#endif
+#include <initializer_list>
+#include <type_traits>
+#include "citraimport\common/common_types.h"
+
+// namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly
+namespace Common {
+
+// Helper functions:
+
+#ifdef _WIN32
+template <typename T>
+static inline int CountSetBits(T v)
+{
+ // from https://graphics.stanford.edu/~seander/bithacks.html
+ // GCC has this built in, but MSVC's intrinsic will only emit the actual
+ // POPCNT instruction, which we're not depending on
+ v = v - ((v >> 1) & (T)~(T)0/3);
+ v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3);
+ v = (v + (v >> 4)) & (T)~(T)0/255*15;
+ return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8;
+}
+static inline int LeastSignificantSetBit(u8 val)
+{
+ unsigned long index;
+ _BitScanForward(&index, val);
+ return (int)index;
+}
+static inline int LeastSignificantSetBit(u16 val)
+{
+ unsigned long index;
+ _BitScanForward(&index, val);
+ return (int)index;
+}
+static inline int LeastSignificantSetBit(u32 val)
+{
+ unsigned long index;
+ _BitScanForward(&index, val);
+ return (int)index;
+}
+/*static inline int LeastSignificantSetBit(u64 val)
+{
+ unsigned long index;
+ _BitScanForward64(&index, val);
+ return (int)index;
+}*/
+#else
+static inline int CountSetBits(u8 val) { return __builtin_popcount(val); }
+static inline int CountSetBits(u16 val) { return __builtin_popcount(val); }
+static inline int CountSetBits(u32 val) { return __builtin_popcount(val); }
+static inline int CountSetBits(u64 val) { return __builtin_popcountll(val); }
+static inline int LeastSignificantSetBit(u8 val) { return __builtin_ctz(val); }
+static inline int LeastSignificantSetBit(u16 val) { return __builtin_ctz(val); }
+static inline int LeastSignificantSetBit(u32 val) { return __builtin_ctz(val); }
+static inline int LeastSignificantSetBit(u64 val) { return __builtin_ctzll(val); }
+#endif
+
+// Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
+// using the set bits of an integer to represent a set of integers. Like that
+// class, it acts like an array of bools:
+// BitSet32 bs;
+// bs[1] = true;
+// but also like the underlying integer ([0] = least significant bit):
+// BitSet32 bs2 = ...;
+// bs = (bs ^ bs2) & BitSet32(0xffff);
+// The following additional functionality is provided:
+// - Construction using an initializer list.
+// BitSet bs { 1, 2, 4, 8 };
+// - Efficiently iterating through the set bits:
+// for (int i : bs)
+// [i is the *index* of a set bit]
+// (This uses the appropriate CPU instruction to find the next set bit in one
+// operation.)
+// - Counting set bits using .Count() - see comment on that method.
+
+// TODO: use constexpr when MSVC gets out of the Dark Ages
+
+template <typename IntTy>
+class BitSet
+{
+ static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
+public:
+ // A reference to a particular bit, returned from operator[].
+ class Ref
+ {
+ public:
+ Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
+ Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
+ operator bool() const { return (m_bs->m_val & m_mask) != 0; }
+ bool operator=(bool set)
+ {
+ m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
+ return set;
+ }
+ private:
+ BitSet* m_bs;
+ IntTy m_mask;
+ };
+
+ // A STL-like iterator is required to be able to use range-based for loops.
+ class Iterator
+ {
+ public:
+ Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {}
+ Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {}
+ Iterator& operator=(Iterator other) { new (this) Iterator(other); return *this; }
+ int operator*() { return m_bit; }
+ Iterator& operator++()
+ {
+ if (m_val == 0)
+ {
+ m_bit = -1;
+ }
+ else
+ {
+ int bit = LeastSignificantSetBit(m_val);
+ m_val &= ~(1 << bit);
+ m_bit = bit;
+ }
+ return *this;
+ }
+ Iterator operator++(int _)
+ {
+ Iterator other(*this);
+ ++*this;
+ return other;
+ }
+ bool operator==(Iterator other) const { return m_bit == other.m_bit; }
+ bool operator!=(Iterator other) const { return m_bit != other.m_bit; }
+ private:
+ IntTy m_val;
+ int m_bit;
+ };
+
+ BitSet() : m_val(0) {}
+ explicit BitSet(IntTy val) : m_val(val) {}
+ BitSet(std::initializer_list<int> init)
+ {
+ m_val = 0;
+ for (int bit : init)
+ m_val |= (IntTy)1 << bit;
+ }
+
+ static BitSet AllTrue(size_t count)
+ {
+ return BitSet(count == sizeof(IntTy)*8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
+ }
+
+ Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); }
+ const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(this))[bit]; }
+ bool operator==(BitSet other) const { return m_val == other.m_val; }
+ bool operator!=(BitSet other) const { return m_val != other.m_val; }
+ bool operator<(BitSet other) const { return m_val < other.m_val; }
+ bool operator>(BitSet other) const { return m_val > other.m_val; }
+ BitSet operator|(BitSet other) const { return BitSet(m_val | other.m_val); }
+ BitSet operator&(BitSet other) const { return BitSet(m_val & other.m_val); }
+ BitSet operator^(BitSet other) const { return BitSet(m_val ^ other.m_val); }
+ BitSet operator~() const { return BitSet(~m_val); }
+ BitSet& operator|=(BitSet other) { return *this = *this | other; }
+ BitSet& operator&=(BitSet other) { return *this = *this & other; }
+ BitSet& operator^=(BitSet other) { return *this = *this ^ other; }
+ operator u32() = delete;
+ operator bool() { return m_val != 0; }
+
+ // Warning: Even though on modern CPUs this is a single fast instruction,
+ // Dolphin's official builds do not currently assume POPCNT support on x86,
+ // so slower explicit bit twiddling is generated. Still should generally
+ // be faster than a loop.
+ unsigned int Count() const { return CountSetBits(m_val); }
+
+ Iterator begin() const { Iterator it(m_val, 0); return ++it; }
+ Iterator end() const { return Iterator(m_val, -1); }
+
+ IntTy m_val;
+};
+
+} // Common
+
+typedef Common::BitSet<u8> BitSet8;
+typedef Common::BitSet<u16> BitSet16;
+typedef Common::BitSet<u32> BitSet32;
+typedef Common::BitSet<u64> BitSet64;
\ No newline at end of file
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "citraimport\common/break_points.h"
+#include "citraimport\common/logging/log.h"
+
+#include <sstream>
+#include <algorithm>
+
+bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const
+{
+ auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; };
+ auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
+ return it != m_BreakPoints.end();
+}
+
+bool BreakPoints::IsTempBreakPoint(u32 iAddress) const
+{
+ auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress && bp.bTemporary; };
+ auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
+ return it != m_BreakPoints.end();
+}
+
+BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const
+{
+ TBreakPointsStr bps;
+ for (auto breakpoint : m_BreakPoints)
+ {
+ if (!breakpoint.bTemporary)
+ {
+ std::stringstream bp;
+ bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : "");
+ bps.push_back(bp.str());
+ }
+ }
+
+ return bps;
+}
+
+void BreakPoints::AddFromStrings(const TBreakPointsStr& bps)
+{
+ for (auto bps_item : bps)
+ {
+ TBreakPoint bp;
+ std::stringstream bpstr;
+ bpstr << std::hex << bps_item;
+ bpstr >> bp.iAddress;
+ bp.bOn = bps_item.find("n") != bps_item.npos;
+ bp.bTemporary = false;
+ Add(bp);
+ }
+}
+
+void BreakPoints::Add(const TBreakPoint& bp)
+{
+ if (!IsAddressBreakPoint(bp.iAddress))
+ {
+ m_BreakPoints.push_back(bp);
+ //if (jit)
+ // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
+ }
+}
+
+void BreakPoints::Add(u32 em_address, bool temp)
+{
+ if (!IsAddressBreakPoint(em_address)) // only add new addresses
+ {
+ TBreakPoint pt; // breakpoint settings
+ pt.bOn = true;
+ pt.bTemporary = temp;
+ pt.iAddress = em_address;
+
+ m_BreakPoints.push_back(pt);
+
+ //if (jit)
+ // jit->GetBlockCache()->InvalidateICache(em_address, 4);
+ }
+}
+
+void BreakPoints::Remove(u32 em_address)
+{
+ auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; };
+ auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
+ if (it != m_BreakPoints.end())
+ m_BreakPoints.erase(it);
+}
+
+void BreakPoints::Clear()
+{
+ //if (jit)
+ //{
+ // std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(),
+ // [](const TBreakPoint& bp)
+ // {
+ // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
+ // }
+ // );
+ //}
+
+ m_BreakPoints.clear();
+}
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+#include <string>
+
+#include "citraimport\common/common_types.h"
+
+class DebugInterface;
+
+struct TBreakPoint
+{
+ u32 iAddress;
+ bool bOn;
+ bool bTemporary;
+};
+
+// Code breakpoints.
+class BreakPoints
+{
+public:
+ typedef std::vector<TBreakPoint> TBreakPoints;
+ typedef std::vector<std::string> TBreakPointsStr;
+
+ const TBreakPoints& GetBreakPoints() { return m_BreakPoints; }
+
+ TBreakPointsStr GetStrings() const;
+ void AddFromStrings(const TBreakPointsStr& bps);
+
+ // is address breakpoint
+ bool IsAddressBreakPoint(u32 iAddress) const;
+ bool IsTempBreakPoint(u32 iAddress) const;
+
+ // Add BreakPoint
+ void Add(u32 em_address, bool temp=false);
+ void Add(const TBreakPoint& bp);
+
+ // Remove Breakpoint
+ void Remove(u32 iAddress);
+ void Clear();
+
+ void DeleteByAddress(u32 Address);
+
+private:
+ TBreakPoints m_BreakPoints;
+ u32 m_iBreakOnCount;
+};
--- /dev/null
+// Copyright (C) 2003 Dolphin Project.
+
+// 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, version 2.0 or later versions.
+
+// 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 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official SVN repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#pragma once
+
+// Extremely simple serialization framework.
+
+// (mis)-features:
+// + Super fast
+// + Very simple
+// + Same code is used for serialization and deserializaition (in most cases)
+// - Zero backwards/forwards compatibility
+// - Serialization code for anything complex has to be manually written.
+
+#include <cstring>
+#include <deque>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/logging/log.h"
+
+template <class T>
+struct LinkedListItem : public T
+{
+ LinkedListItem<T> *next;
+};
+
+class PointerWrap;
+
+class PointerWrapSection
+{
+public:
+ PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) {
+ }
+ ~PointerWrapSection();
+
+ bool operator == (const int &v) const { return ver_ == v; }
+ bool operator != (const int &v) const { return ver_ != v; }
+ bool operator <= (const int &v) const { return ver_ <= v; }
+ bool operator >= (const int &v) const { return ver_ >= v; }
+ bool operator < (const int &v) const { return ver_ < v; }
+ bool operator > (const int &v) const { return ver_ > v; }
+
+ operator bool() const {
+ return ver_ > 0;
+ }
+
+private:
+ PointerWrap &p_;
+ int ver_;
+ const char *title_;
+};
+
+// Wrapper class
+class PointerWrap
+{
+ // This makes it a compile error if you forget to define DoState() on non-POD.
+ // Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason...
+#ifdef _MSC_VER
+ template<typename T, bool isPOD = std::is_pod<T>::value, bool isPointer = std::is_pointer<T>::value>
+#else
+ template<typename T, bool isPOD = __is_pod(T), bool isPointer = std::is_pointer<T>::value>
+#endif
+ struct DoHelper
+ {
+ static void DoArray(PointerWrap *p, T *x, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ p->Do(x[i]);
+ }
+
+ static void Do(PointerWrap *p, T &x)
+ {
+ p->DoClass(x);
+ }
+ };
+
+ template<typename T>
+ struct DoHelper<T, true, false>
+ {
+ static void DoArray(PointerWrap *p, T *x, int count)
+ {
+ p->DoVoid((void *)x, sizeof(T) * count);
+ }
+
+ static void Do(PointerWrap *p, T &x)
+ {
+ p->DoVoid((void *)&x, sizeof(x));
+ }
+ };
+
+public:
+ enum Mode {
+ MODE_READ = 1, // load
+ MODE_WRITE, // save
+ MODE_MEASURE, // calculate size
+ MODE_VERIFY, // compare
+ };
+
+ enum Error {
+ ERROR_NONE = 0,
+ ERROR_WARNING = 1,
+ ERROR_FAILURE = 2,
+ };
+
+ u8 **ptr;
+ Mode mode;
+ Error error;
+
+public:
+ PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), mode(mode_), error(ERROR_NONE) {}
+ PointerWrap(unsigned char **ptr_, int mode_) : ptr((u8**)ptr_), mode((Mode)mode_), error(ERROR_NONE) {}
+
+ PointerWrapSection Section(const char *title, int ver) {
+ return Section(title, ver, ver);
+ }
+
+ // The returned object can be compared against the version that was loaded.
+ // This can be used to support versions as old as minVer.
+ // Version = 0 means the section was not found.
+ PointerWrapSection Section(const char *title, int minVer, int ver) {
+ char marker[16] = {0};
+ int foundVersion = ver;
+
+ strncpy(marker, title, sizeof(marker));
+ if (!ExpectVoid(marker, sizeof(marker)))
+ {
+ // Might be before we added name markers for safety.
+ if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion)))
+ DoMarker(title);
+ // Wasn't found, but maybe we can still load the state.
+ else
+ foundVersion = 0;
+ }
+ else
+ Do(foundVersion);
+
+ if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) {
+ LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, title);
+ SetError(ERROR_FAILURE);
+ return PointerWrapSection(*this, -1, title);
+ }
+ return PointerWrapSection(*this, foundVersion, title);
+ }
+
+ void SetMode(Mode mode_) {mode = mode_;}
+ Mode GetMode() const {return mode;}
+ u8 **GetPPtr() {return ptr;}
+ void SetError(Error error_)
+ {
+ if (error < error_)
+ error = error_;
+ if (error > ERROR_WARNING)
+ mode = PointerWrap::MODE_MEASURE;
+ }
+
+ bool ExpectVoid(void *data, int size)
+ {
+ switch (mode) {
+ case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break;
+ case MODE_WRITE: memcpy(*ptr, data, size); break;
+ case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything
+ case MODE_VERIFY:
+ for (int i = 0; i < size; i++) {
+ DEBUG_ASSERT_MSG(((u8*)data)[i] == (*ptr)[i],
+ "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
+ ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i],
+ (*ptr)[i], (*ptr)[i], &(*ptr)[i]);
+ }
+ break;
+ default: break; // throw an error?
+ }
+ (*ptr) += size;
+ return true;
+ }
+
+ void DoVoid(void *data, int size)
+ {
+ switch (mode) {
+ case MODE_READ: memcpy(data, *ptr, size); break;
+ case MODE_WRITE: memcpy(*ptr, data, size); break;
+ case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything
+ case MODE_VERIFY:
+ for (int i = 0; i < size; i++) {
+ DEBUG_ASSERT_MSG(((u8*)data)[i] == (*ptr)[i],
+ "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
+ ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i],
+ (*ptr)[i], (*ptr)[i], &(*ptr)[i]);
+ }
+ break;
+ default: break; // throw an error?
+ }
+ (*ptr) += size;
+ }
+
+ template<class K, class T>
+ void Do(std::map<K, T *> &x)
+ {
+ if (mode == MODE_READ)
+ {
+ for (auto it = x.begin(), end = x.end(); it != end; ++it)
+ {
+ if (it->second != nullptr)
+ delete it->second;
+ }
+ }
+ T *dv = nullptr;
+ DoMap(x, dv);
+ }
+
+ template<class K, class T>
+ void Do(std::map<K, T> &x)
+ {
+ T dv = T();
+ DoMap(x, dv);
+ }
+
+ template<class K, class T>
+ void DoMap(std::map<K, T> &x, T &default_val)
+ {
+ unsigned int number = (unsigned int)x.size();
+ Do(number);
+ switch (mode) {
+ case MODE_READ:
+ {
+ x.clear();
+ while (number > 0)
+ {
+ K first = K();
+ Do(first);
+ T second = default_val;
+ Do(second);
+ x[first] = second;
+ --number;
+ }
+ }
+ break;
+ case MODE_WRITE:
+ case MODE_MEASURE:
+ case MODE_VERIFY:
+ {
+ typename std::map<K, T>::iterator itr = x.begin();
+ while (number > 0)
+ {
+ K first = itr->first;
+ Do(first);
+ Do(itr->second);
+ --number;
+ ++itr;
+ }
+ }
+ break;
+ }
+ }
+
+ template<class K, class T>
+ void Do(std::multimap<K, T *> &x)
+ {
+ if (mode == MODE_READ)
+ {
+ for (auto it = x.begin(), end = x.end(); it != end; ++it)
+ {
+ if (it->second != nullptr)
+ delete it->second;
+ }
+ }
+ T *dv = nullptr;
+ DoMultimap(x, dv);
+ }
+
+ template<class K, class T>
+ void Do(std::multimap<K, T> &x)
+ {
+ T dv = T();
+ DoMultimap(x, dv);
+ }
+
+ template<class K, class T>
+ void DoMultimap(std::multimap<K, T> &x, T &default_val)
+ {
+ unsigned int number = (unsigned int)x.size();
+ Do(number);
+ switch (mode) {
+ case MODE_READ:
+ {
+ x.clear();
+ while (number > 0)
+ {
+ K first = K();
+ Do(first);
+ T second = default_val;
+ Do(second);
+ x.insert(std::make_pair(first, second));
+ --number;
+ }
+ }
+ break;
+ case MODE_WRITE:
+ case MODE_MEASURE:
+ case MODE_VERIFY:
+ {
+ typename std::multimap<K, T>::iterator itr = x.begin();
+ while (number > 0)
+ {
+ Do(itr->first);
+ Do(itr->second);
+ --number;
+ ++itr;
+ }
+ }
+ break;
+ }
+ }
+
+ // Store vectors.
+ template<class T>
+ void Do(std::vector<T *> &x)
+ {
+ T *dv = nullptr;
+ DoVector(x, dv);
+ }
+
+ template<class T>
+ void Do(std::vector<T> &x)
+ {
+ T dv = T();
+ DoVector(x, dv);
+ }
+
+
+ template<class T>
+ void DoPOD(std::vector<T> &x)
+ {
+ T dv = T();
+ DoVectorPOD(x, dv);
+ }
+
+ template<class T>
+ void Do(std::vector<T> &x, T &default_val)
+ {
+ DoVector(x, default_val);
+ }
+
+ template<class T>
+ void DoVector(std::vector<T> &x, T &default_val)
+ {
+ u32 vec_size = (u32)x.size();
+ Do(vec_size);
+ x.resize(vec_size, default_val);
+ if (vec_size > 0)
+ DoArray(&x[0], vec_size);
+ }
+
+ template<class T>
+ void DoVectorPOD(std::vector<T> &x, T &default_val)
+ {
+ u32 vec_size = (u32)x.size();
+ Do(vec_size);
+ x.resize(vec_size, default_val);
+ if (vec_size > 0)
+ DoArray(&x[0], vec_size);
+ }
+
+ // Store deques.
+ template<class T>
+ void Do(std::deque<T *> &x)
+ {
+ T *dv = nullptr;
+ DoDeque(x, dv);
+ }
+
+ template<class T>
+ void Do(std::deque<T> &x)
+ {
+ T dv = T();
+ DoDeque(x, dv);
+ }
+
+ template<class T>
+ void DoDeque(std::deque<T> &x, T &default_val)
+ {
+ u32 deq_size = (u32)x.size();
+ Do(deq_size);
+ x.resize(deq_size, default_val);
+ u32 i;
+ for(i = 0; i < deq_size; i++)
+ Do(x[i]);
+ }
+
+ // Store STL lists.
+ template<class T>
+ void Do(std::list<T *> &x)
+ {
+ T *dv = nullptr;
+ Do(x, dv);
+ }
+
+ template<class T>
+ void Do(std::list<T> &x)
+ {
+ T dv = T();
+ DoList(x, dv);
+ }
+
+ template<class T>
+ void Do(std::list<T> &x, T &default_val)
+ {
+ DoList(x, default_val);
+ }
+
+ template<class T>
+ void DoList(std::list<T> &x, T &default_val)
+ {
+ u32 list_size = (u32)x.size();
+ Do(list_size);
+ x.resize(list_size, default_val);
+
+ typename std::list<T>::iterator itr, end;
+ for (itr = x.begin(), end = x.end(); itr != end; ++itr)
+ Do(*itr);
+ }
+
+
+ // Store STL sets.
+ template <class T>
+ void Do(std::set<T *> &x)
+ {
+ if (mode == MODE_READ)
+ {
+ for (auto it = x.begin(), end = x.end(); it != end; ++it)
+ {
+ if (*it != nullptr)
+ delete *it;
+ }
+ }
+ DoSet(x);
+ }
+
+ template <class T>
+ void Do(std::set<T> &x)
+ {
+ DoSet(x);
+ }
+
+ template <class T>
+ void DoSet(std::set<T> &x)
+ {
+ unsigned int number = (unsigned int)x.size();
+ Do(number);
+
+ switch (mode)
+ {
+ case MODE_READ:
+ {
+ x.clear();
+ while (number-- > 0)
+ {
+ T it = T();
+ Do(it);
+ x.insert(it);
+ }
+ }
+ break;
+ case MODE_WRITE:
+ case MODE_MEASURE:
+ case MODE_VERIFY:
+ {
+ typename std::set<T>::iterator itr = x.begin();
+ while (number-- > 0)
+ Do(*itr++);
+ }
+ break;
+
+ default:
+ LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode);
+ }
+ }
+
+ // Store strings.
+ void Do(std::string &x)
+ {
+ int stringLen = (int)x.length() + 1;
+ Do(stringLen);
+
+ switch (mode) {
+ case MODE_READ: x = (char*)*ptr; break;
+ case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
+ case MODE_MEASURE: break;
+ case MODE_VERIFY:
+ DEBUG_ASSERT_MSG((x == (char*)*ptr),
+ "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n",
+ x.c_str(), (char*)*ptr, ptr);
+ break;
+ }
+ (*ptr) += stringLen;
+ }
+
+ void Do(std::wstring &x)
+ {
+ int stringLen = sizeof(wchar_t)*((int)x.length() + 1);
+ Do(stringLen);
+
+ switch (mode) {
+ case MODE_READ: x = (wchar_t*)*ptr; break;
+ case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
+ case MODE_MEASURE: break;
+ case MODE_VERIFY:
+ DEBUG_ASSERT_MSG((x == (wchar_t*)*ptr),
+ "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n",
+ x.c_str(), (wchar_t*)*ptr, ptr);
+ break;
+ }
+ (*ptr) += stringLen;
+ }
+
+ template<class T>
+ void DoClass(T &x) {
+ x.DoState(*this);
+ }
+
+ template<class T>
+ void DoClass(T *&x) {
+ if (mode == MODE_READ)
+ {
+ if (x != nullptr)
+ delete x;
+ x = new T();
+ }
+ x->DoState(*this);
+ }
+
+ template<class T>
+ void DoArray(T *x, int count) {
+ DoHelper<T>::DoArray(this, x, count);
+ }
+
+ template<class T>
+ void Do(T &x) {
+ DoHelper<T>::Do(this, x);
+ }
+
+ template<class T>
+ void DoPOD(T &x) {
+ DoHelper<T>::Do(this, x);
+ }
+
+ template<class T>
+ void DoPointer(T* &x, T*const base) {
+ // pointers can be more than 2^31 apart, but you're using this function wrong if you need that much range
+ s32 offset = x - base;
+ Do(offset);
+ if (mode == MODE_READ)
+ x = base + offset;
+ }
+
+ template<class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), void (*TDo)(PointerWrap&, T*)>
+ void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = nullptr)
+ {
+ LinkedListItem<T>* list_cur = list_start;
+ LinkedListItem<T>* prev = nullptr;
+
+ while (true)
+ {
+ u8 shouldExist = (list_cur ? 1 : 0);
+ Do(shouldExist);
+ if (shouldExist == 1)
+ {
+ LinkedListItem<T>* cur = list_cur ? list_cur : TNew();
+ TDo(*this, (T*)cur);
+ if (!list_cur)
+ {
+ if (mode == MODE_READ)
+ {
+ cur->next = nullptr;
+ list_cur = cur;
+ if (prev)
+ prev->next = cur;
+ else
+ list_start = cur;
+ }
+ else
+ {
+ TFree(cur);
+ continue;
+ }
+ }
+ }
+ else
+ {
+ if (mode == MODE_READ)
+ {
+ if (prev)
+ prev->next = nullptr;
+ if (list_end)
+ *list_end = prev;
+ if (list_cur)
+ {
+ if (list_start == list_cur)
+ list_start = nullptr;
+ do
+ {
+ LinkedListItem<T>* next = list_cur->next;
+ TFree(list_cur);
+ list_cur = next;
+ }
+ while (list_cur);
+ }
+ }
+ break;
+ }
+ prev = list_cur;
+ list_cur = list_cur->next;
+ }
+ }
+
+ void DoMarker(const char* prevName, u32 arbitraryNumber=0x42)
+ {
+ u32 cookie = arbitraryNumber;
+ Do(cookie);
+ if(mode == PointerWrap::MODE_READ && cookie != arbitraryNumber)
+ {
+ LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber);
+ SetError(ERROR_FAILURE);
+ }
+ }
+};
+
+inline PointerWrapSection::~PointerWrapSection() {
+ if (ver_ > 0) {
+ p_.DoMarker(title_);
+ }
+}
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#if defined(_MSC_VER)
+#include <stdlib.h>
+#endif
+
+#include "common_funcs.h"
+#include "common_types.h"
+#include "hash.h"
+
+namespace Common {
+
+// MurmurHash3 was written by Austin Appleby, and is placed in the public
+// domain. The author hereby disclaims copyright to this source code.
+
+// Block read - if your platform needs to do endian-swapping or can only handle aligned reads, do
+// the conversion here
+static FORCE_INLINE u64 getblock64(const u64* p, int i) {
+ return p[i];
+}
+
+// Finalization mix - force all bits of a hash block to avalanche
+static FORCE_INLINE u64 fmix64(u64 k) {
+ k ^= k >> 33;
+ k *= 0xff51afd7ed558ccdllu;
+ k ^= k >> 33;
+ k *= 0xc4ceb9fe1a85ec53llu;
+ k ^= k >> 33;
+
+ return k;
+}
+
+// This is the 128-bit variant of the MurmurHash3 hash function that is targetted for 64-bit
+// platforms (MurmurHash3_x64_128). It was taken from:
+// https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
+void MurmurHash3_128(const void* key, int len, u32 seed, void* out) {
+ const u8 * data = (const u8*)key;
+ const int nblocks = len / 16;
+
+ u64 h1 = seed;
+ u64 h2 = seed;
+
+ const u64 c1 = 0x87c37b91114253d5llu;
+ const u64 c2 = 0x4cf5ad432745937fllu;
+
+ // Body
+
+ const u64 * blocks = (const u64 *)(data);
+
+ for (int i = 0; i < nblocks; i++) {
+ u64 k1 = getblock64(blocks,i*2+0);
+ u64 k2 = getblock64(blocks,i*2+1);
+
+ k1 *= c1; k1 = _rotl64(k1,31); k1 *= c2; h1 ^= k1;
+
+ h1 = _rotl64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;
+
+ k2 *= c2; k2 = _rotl64(k2,33); k2 *= c1; h2 ^= k2;
+
+ h2 = _rotl64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
+ }
+
+ // Tail
+
+ const u8 * tail = (const u8*)(data + nblocks*16);
+
+ u64 k1 = 0;
+ u64 k2 = 0;
+
+ switch (len & 15) {
+ case 15: k2 ^= ((u64)tail[14]) << 48;
+ case 14: k2 ^= ((u64)tail[13]) << 40;
+ case 13: k2 ^= ((u64)tail[12]) << 32;
+ case 12: k2 ^= ((u64)tail[11]) << 24;
+ case 11: k2 ^= ((u64)tail[10]) << 16;
+ case 10: k2 ^= ((u64)tail[ 9]) << 8;
+ case 9: k2 ^= ((u64)tail[ 8]) << 0;
+ k2 *= c2; k2 = _rotl64(k2,33); k2 *= c1; h2 ^= k2;
+
+ case 8: k1 ^= ((u64)tail[ 7]) << 56;
+ case 7: k1 ^= ((u64)tail[ 6]) << 48;
+ case 6: k1 ^= ((u64)tail[ 5]) << 40;
+ case 5: k1 ^= ((u64)tail[ 4]) << 32;
+ case 4: k1 ^= ((u64)tail[ 3]) << 24;
+ case 3: k1 ^= ((u64)tail[ 2]) << 16;
+ case 2: k1 ^= ((u64)tail[ 1]) << 8;
+ case 1: k1 ^= ((u64)tail[ 0]) << 0;
+ k1 *= c1; k1 = _rotl64(k1,31); k1 *= c2; h1 ^= k1;
+ };
+
+ // Finalization
+
+ h1 ^= len; h2 ^= len;
+
+ h1 += h2;
+ h2 += h1;
+
+ h1 = fmix64(h1);
+ h2 = fmix64(h2);
+
+ h1 += h2;
+ h2 += h1;
+
+ ((u64*)out)[0] = h1;
+ ((u64*)out)[1] = h2;
+}
+
+} // namespace Common
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "key_map.h"
+#include <map>
+
+namespace KeyMap {
+
+static std::map<HostDeviceKey, Service::HID::PadState> key_map;
+static int next_device_id = 0;
+
+int NewDeviceId() {
+ return next_device_id++;
+}
+
+void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState) {
+ key_map[key].hex = padState.hex;
+}
+
+Service::HID::PadState GetPadKey(HostDeviceKey key) {
+ return key_map[key];
+}
+
+}
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <time.h>
+
+#ifdef _WIN32
+#include <Windows.h>
+#include <mmsystem.h>
+#include <sys/timeb.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include "citraimport\common\common_types.h"
+#include "citraimport\common/string_util.h"
+#include "citraimport\common/timer.h"
+
+/*namespace Common
+{
+
+u32 Timer::GetTimeMs()
+{
+#ifdef _WIN32
+ return timeGetTime();
+#else
+ struct timeval t;
+ (void)gettimeofday(&t, nullptr);
+ return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000));
+#endif
+}
+
+// --------------------------------------------
+// Initiate, Start, Stop, and Update the time
+// --------------------------------------------
+
+// Set initial values for the class
+Timer::Timer()
+ : m_LastTime(0), m_StartTime(0), m_Running(false)
+{
+ Update();
+}
+
+// Write the starting time
+void Timer::Start()
+{
+ m_StartTime = GetTimeMs();
+ m_Running = true;
+}
+
+// Stop the timer
+void Timer::Stop()
+{
+ // Write the final time
+ m_LastTime = GetTimeMs();
+ m_Running = false;
+}
+
+// Update the last time variable
+void Timer::Update()
+{
+ m_LastTime = GetTimeMs();
+ //TODO(ector) - QPF
+}
+
+// -------------------------------------
+// Get time difference and elapsed time
+// -------------------------------------
+
+// Get the number of milliseconds since the last Update()
+u64 Timer::GetTimeDifference()
+{
+ return GetTimeMs() - m_LastTime;
+}
+
+// Add the time difference since the last Update() to the starting time.
+// This is used to compensate for a paused game.
+void Timer::AddTimeDifference()
+{
+ m_StartTime += GetTimeDifference();
+}
+
+// Get the time elapsed since the Start()
+u64 Timer::GetTimeElapsed()
+{
+ // If we have not started yet, return 1 (because then I don't
+ // have to change the FPS calculation in CoreRerecording.cpp .
+ if (m_StartTime == 0) return 1;
+
+ // Return the final timer time if the timer is stopped
+ if (!m_Running) return (m_LastTime - m_StartTime);
+
+ return (GetTimeMs() - m_StartTime);
+}
+
+// Get the formatted time elapsed since the Start()
+std::string Timer::GetTimeElapsedFormatted() const
+{
+ // If we have not started yet, return zero
+ if (m_StartTime == 0)
+ return "00:00:00:000";
+
+ // The number of milliseconds since the start.
+ // Use a different value if the timer is stopped.
+ u64 Milliseconds;
+ if (m_Running)
+ Milliseconds = GetTimeMs() - m_StartTime;
+ else
+ Milliseconds = m_LastTime - m_StartTime;
+ // Seconds
+ u32 Seconds = (u32)(Milliseconds / 1000);
+ // Minutes
+ u32 Minutes = Seconds / 60;
+ // Hours
+ u32 Hours = Minutes / 60;
+
+ std::string TmpStr = StringFromFormat("%02i:%02i:%02i:%03i",
+ Hours, Minutes % 60, Seconds % 60, Milliseconds % 1000);
+ return TmpStr;
+}
+
+// Get current time
+void Timer::IncreaseResolution()
+{
+#ifdef _WIN32
+ timeBeginPeriod(1);
+#endif
+}
+
+void Timer::RestoreResolution()
+{
+#ifdef _WIN32
+ timeEndPeriod(1);
+#endif
+}
+
+// Get the number of seconds since January 1 1970
+u64 Timer::GetTimeSinceJan1970()
+{
+ time_t ltime;
+ time(<ime);
+ return((u64)ltime);
+}
+
+u64 Timer::GetLocalTimeSinceJan1970()
+{
+ time_t sysTime, tzDiff, tzDST;
+ struct tm * gmTime;
+
+ time(&sysTime);
+
+ // Account for DST where needed
+ gmTime = localtime(&sysTime);
+ if(gmTime->tm_isdst == 1)
+ tzDST = 3600;
+ else
+ tzDST = 0;
+
+ // Lazy way to get local time in sec
+ gmTime = gmtime(&sysTime);
+ tzDiff = sysTime - mktime(gmTime);
+
+ return (u64)(sysTime + tzDiff + tzDST);
+}
+
+// Return the current time formatted as Minutes:Seconds:Milliseconds
+// in the form 00:00:000.
+std::string Timer::GetTimeFormatted()
+{
+ time_t sysTime;
+ struct tm * gmTime;
+ char tmp[13];
+
+ time(&sysTime);
+ gmTime = localtime(&sysTime);
+
+ strftime(tmp, 6, "%M:%S", gmTime);
+
+ // Now tack on the milliseconds
+#ifdef _WIN32
+ struct timeb tp;
+ (void)::ftime(&tp);
+ return StringFromFormat("%s:%03i", tmp, tp.millitm);
+#else
+ struct timeval t;
+ (void)gettimeofday(&t, nullptr);
+ return StringFromFormat("%s:%03d", tmp, (int)(t.tv_usec / 1000));
+#endif
+}
+
+// Returns a timestamp with decimals for precise time comparisons
+// ----------------
+double Timer::GetDoubleTime()
+{
+#ifdef _WIN32
+ struct timeb tp;
+ (void)::ftime(&tp);
+#else
+ struct timeval t;
+ (void)gettimeofday(&t, nullptr);
+#endif
+ // Get continuous timestamp
+ u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970();
+
+ // Remove a few years. We only really want enough seconds to make
+ // sure that we are detecting actual actions, perhaps 60 seconds is
+ // enough really, but I leave a year of seconds anyway, in case the
+ // user's clock is incorrect or something like that.
+ TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60);
+
+ // Make a smaller integer that fits in the double
+ u32 Seconds = (u32)TmpSeconds;
+#ifdef _WIN32
+ double ms = tp.millitm / 1000.0 / 1000.0;
+#else
+ double ms = t.tv_usec / 1000000.0;
+#endif
+ double TmpTime = Seconds + ms;
+
+ return TmpTime;
+}
+
+} // Namespace Common
+*/
\ No newline at end of file
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common_types.h"
+#include "memory_util.h"
+
+// Everything that needs to generate code should inherit from this.
+// You get memory management for free, plus, you can use all emitter functions without
+// having to prefix them with gen-> or something similar.
+// Example implementation:
+// class JIT : public CodeBlock<ARMXEmitter> {}
+template<class T> class CodeBlock : public T, NonCopyable
+{
+private:
+ // A privately used function to set the executable RAM space to something invalid.
+ // For debugging usefulness it should be used to set the RAM to a host specific breakpoint instruction
+ virtual void PoisonMemory() = 0;
+
+protected:
+ u8 *region;
+ size_t region_size;
+
+public:
+ CodeBlock() : region(nullptr), region_size(0) {}
+ virtual ~CodeBlock() { if (region) FreeCodeSpace(); }
+
+ // Call this before you generate any code.
+ void AllocCodeSpace(int size)
+ {
+ region_size = size;
+ region = (u8*)AllocateExecutableMemory(region_size);
+ T::SetCodePtr(region);
+ }
+
+ // Always clear code space with breakpoints, so that if someone accidentally executes
+ // uninitialized, it just breaks into the debugger.
+ void ClearCodeSpace()
+ {
+ PoisonMemory();
+ ResetCodePtr();
+ }
+
+ // Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
+ void FreeCodeSpace()
+ {
+#ifdef __SYMBIAN32__
+ ResetExecutableMemory(region);
+#else
+ FreeMemoryPages(region, region_size);
+#endif
+ region = nullptr;
+ region_size = 0;
+ }
+
+ bool IsInSpace(const u8 *ptr)
+ {
+ return (ptr >= region) && (ptr < (region + region_size));
+ }
+
+ // Cannot currently be undone. Will write protect the entire code region.
+ // Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
+ void WriteProtect()
+ {
+ WriteProtectMemory(region, region_size, true);
+ }
+
+ void ResetCodePtr()
+ {
+ T::SetCodePtr(region);
+ }
+
+ size_t GetSpaceLeft() const
+ {
+ return region_size - (T::GetCodePtr() - region);
+ }
+
+ u8 *GetBasePtr() {
+ return region;
+ }
+
+ size_t GetOffset(const u8 *ptr) const {
+ return ptr - region;
+ }
+};
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "citraimport\common/common_types.h"
+#include "citraimport\common/swap.h"
+#include "citraimport\common/vector_math.h"
+
+namespace Color {
+
+/// Convert a 1-bit color component to 8 bit
+inline u8 Convert1To8(u8 value) {
+ return value * 255;
+}
+
+/// Convert a 4-bit color component to 8 bit
+inline u8 Convert4To8(u8 value) {
+ return (value << 4) | value;
+}
+
+/// Convert a 5-bit color component to 8 bit
+inline u8 Convert5To8(u8 value) {
+ return (value << 3) | (value >> 2);
+}
+
+/// Convert a 6-bit color component to 8 bit
+inline u8 Convert6To8(u8 value) {
+ return (value << 2) | (value >> 4);
+}
+
+/// Convert a 8-bit color component to 1 bit
+inline u8 Convert8To1(u8 value) {
+ return value >> 7;
+}
+
+/// Convert a 8-bit color component to 4 bit
+inline u8 Convert8To4(u8 value) {
+ return value >> 4;
+}
+
+/// Convert a 8-bit color component to 5 bit
+inline u8 Convert8To5(u8 value) {
+ return value >> 3;
+}
+
+/// Convert a 8-bit color component to 6 bit
+inline u8 Convert8To6(u8 value) {
+ return value >> 2;
+}
+
+/**
+ * Decode a color stored in RGBA8 format
+ * @param bytes Pointer to encoded source color
+ * @return Result color decoded as Math::Vec4<u8>
+ */
+inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
+ return { bytes[3], bytes[2], bytes[1], bytes[0] };
+}
+
+/**
+ * Decode a color stored in RGB8 format
+ * @param bytes Pointer to encoded source color
+ * @return Result color decoded as Math::Vec4<u8>
+ */
+inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
+ return { bytes[2], bytes[1], bytes[0], 255 };
+}
+
+/**
+ * Decode a color stored in RG8 (aka HILO8) format
+ * @param bytes Pointer to encoded source color
+ * @return Result color decoded as Math::Vec4<u8>
+ */
+inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
+ return { bytes[1], bytes[0], 0, 255 };
+}
+
+/**
+ * Decode a color stored in RGB565 format
+ * @param bytes Pointer to encoded source color
+ * @return Result color decoded as Math::Vec4<u8>
+ */
+inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
+ const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
+ return { Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
+ Convert5To8(pixel & 0x1F), 255 };
+}
+
+/**
+ * Decode a color stored in RGB5A1 format
+ * @param bytes Pointer to encoded source color
+ * @return Result color decoded as Math::Vec4<u8>
+ */
+inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
+ const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
+ return { Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
+ Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1) };
+}
+
+/**
+ * Decode a color stored in RGBA4 format
+ * @param bytes Pointer to encoded source color
+ * @return Result color decoded as Math::Vec4<u8>
+ */
+inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
+ const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
+ return { Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
+ Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF) };
+}
+
+/**
+ * Decode a depth value stored in D16 format
+ * @param bytes Pointer to encoded source value
+ * @return Depth value as an u32
+ */
+inline u32 DecodeD16(const u8* bytes) {
+ return *reinterpret_cast<const u16_le*>(bytes);
+}
+
+/**
+ * Decode a depth value stored in D24 format
+ * @param bytes Pointer to encoded source value
+ * @return Depth value as an u32
+ */
+inline u32 DecodeD24(const u8* bytes) {
+ return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
+}
+
+/**
+ * Decode a depth value and a stencil value stored in D24S8 format
+ * @param bytes Pointer to encoded source values
+ * @return Resulting values stored as a Math::Vec2
+ */
+inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
+ return { static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3] };
+}
+
+/**
+ * Encode a color as RGBA8 format
+ * @param color Source color to encode
+ * @param bytes Destination pointer to store encoded color
+ */
+inline void EncodeRGBA8(const Math::Vec4<u8>& color, u8* bytes) {
+ bytes[3] = color.r();
+ bytes[2] = color.g();
+ bytes[1] = color.b();
+ bytes[0] = color.a();
+}
+
+/**
+ * Encode a color as RGB8 format
+ * @param color Source color to encode
+ * @param bytes Destination pointer to store encoded color
+ */
+inline void EncodeRGB8(const Math::Vec4<u8>& color, u8* bytes) {
+ bytes[2] = color.r();
+ bytes[1] = color.g();
+ bytes[0] = color.b();
+}
+
+/**
+ * Encode a color as RG8 (aka HILO8) format
+ * @param color Source color to encode
+ * @param bytes Destination pointer to store encoded color
+ */
+inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) {
+ bytes[1] = color.r();
+ bytes[0] = color.g();
+}
+/**
+ * Encode a color as RGB565 format
+ * @param color Source color to encode
+ * @param bytes Destination pointer to store encoded color
+ */
+inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
+ *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
+ (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
+}
+
+/**
+ * Encode a color as RGB5A1 format
+ * @param color Source color to encode
+ * @param bytes Destination pointer to store encoded color
+ */
+inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
+ *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
+ (Convert8To5(color.g()) << 6) | (Convert8To5(color.b()) << 1) | Convert8To1(color.a());
+}
+
+/**
+ * Encode a color as RGBA4 format
+ * @param color Source color to encode
+ * @param bytes Destination pointer to store encoded color
+ */
+inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
+ *reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) |
+ (Convert8To4(color.g()) << 8) | (Convert8To4(color.b()) << 4) | Convert8To4(color.a());
+}
+
+/**
+ * Encode a 16 bit depth value as D16 format
+ * @param value 16 bit source depth value to encode
+ * @param bytes Pointer where to store the encoded value
+ */
+inline void EncodeD16(u32 value, u8* bytes) {
+ *reinterpret_cast<u16_le*>(bytes) = value & 0xFFFF;
+}
+
+/**
+ * Encode a 24 bit depth value as D24 format
+ * @param value 24 bit source depth value to encode
+ * @param bytes Pointer where to store the encoded value
+ */
+inline void EncodeD24(u32 value, u8* bytes) {
+ bytes[0] = value & 0xFF;
+ bytes[1] = (value >> 8) & 0xFF;
+ bytes[2] = (value >> 16) & 0xFF;
+}
+
+/**
+ * Encode a 24 bit depth and 8 bit stencil values as D24S8 format
+ * @param depth 24 bit source depth value to encode
+ * @param stencil 8 bit source stencil value to encode
+ * @param bytes Pointer where to store the encoded value
+ */
+inline void EncodeD24S8(u32 depth, u8 stencil, u8* bytes) {
+ bytes[0] = depth & 0xFF;
+ bytes[1] = (depth >> 8) & 0xFF;
+ bytes[2] = (depth >> 16) & 0xFF;
+ bytes[3] = stencil;
+}
+
+/**
+ * Encode a 24 bit depth value as D24X8 format (32 bits per pixel with 8 bits unused)
+ * @param depth 24 bit source depth value to encode
+ * @param bytes Pointer where to store the encoded value
+ * @note unused bits will not be modified
+ */
+inline void EncodeD24X8(u32 depth, u8* bytes) {
+ bytes[0] = depth & 0xFF;
+ bytes[1] = (depth >> 8) & 0xFF;
+ bytes[2] = (depth >> 16) & 0xFF;
+}
+
+/**
+ * Encode an 8 bit stencil value as X24S8 format (32 bits per pixel with 24 bits unused)
+ * @param stencil 8 bit source stencil value to encode
+ * @param bytes Pointer where to store the encoded value
+ * @note unused bits will not be modified
+ */
+inline void EncodeX24S8(u8 stencil, u8* bytes) {
+ bytes[3] = stencil;
+}
+
+} // namespace
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common_types.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+/// Textually concatenates two tokens. The double-expansion is required by the C preprocessor.
+#define CONCAT2(x, y) DO_CONCAT2(x, y)
+#define DO_CONCAT2(x, y) x ## y
+
+// helper macro to properly align structure members.
+// Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121",
+// depending on the current source line to make sure variable names are unique.
+#define INSERT_PADDING_BYTES(num_bytes) u8 CONCAT2(pad, __LINE__)[(num_bytes)]
+#define INSERT_PADDING_WORDS(num_words) u32 CONCAT2(pad, __LINE__)[(num_words)]
+
+#ifdef _WIN32
+ // Alignment
+ #define FORCE_INLINE __forceinline
+ #define MEMORY_ALIGNED16(x) __declspec(align(16)) x
+ #define MEMORY_ALIGNED32(x) __declspec(align(32)) x
+ #define MEMORY_ALIGNED64(x) __declspec(align(64)) x
+ #define MEMORY_ALIGNED128(x) __declspec(align(128)) x
+#else
+ #define FORCE_INLINE inline __attribute__((always_inline))
+ #define MEMORY_ALIGNED16(x) __attribute__((aligned(16))) x
+ #define MEMORY_ALIGNED32(x) __attribute__((aligned(32))) x
+ #define MEMORY_ALIGNED64(x) __attribute__((aligned(64))) x
+ #define MEMORY_ALIGNED128(x) __attribute__((aligned(128))) x
+#endif
+
+#ifndef _MSC_VER
+
+#ifdef ARCHITECTURE_x86_64
+#define Crash() __asm__ __volatile__("int $3")
+#elif defined(_M_ARM)
+#define Crash() __asm__ __volatile__("trap")
+#else
+#define Crash() exit(1)
+#endif
+
+// GCC 4.8 defines all the rotate functions now
+// Small issue with GCC's lrotl/lrotr intrinsics is they are still 32bit while we require 64bit
+#ifdef _rotl
+#define rotl _rotl
+#else
+inline u32 rotl(u32 x, int shift) {
+ shift &= 31;
+ if (!shift) return x;
+ return (x << shift) | (x >> (32 - shift));
+}
+#endif
+
+#ifdef _rotr
+#define rotr _rotr
+#else
+inline u32 rotr(u32 x, int shift) {
+ shift &= 31;
+ if (!shift) return x;
+ return (x >> shift) | (x << (32 - shift));
+}
+#endif
+
+inline u64 _rotl64(u64 x, unsigned int shift){
+ unsigned int n = shift % 64;
+ return (x << n) | (x >> (64 - n));
+}
+
+inline u64 _rotr64(u64 x, unsigned int shift){
+ unsigned int n = shift % 64;
+ return (x >> n) | (x << (64 - n));
+}
+
+#else // _MSC_VER
+ #if (_MSC_VER < 1900)
+ // Function Cross-Compatibility
+ #define snprintf _snprintf
+ #endif
+
+ // Locale Cross-Compatibility
+ #define locale_t _locale_t
+
+ extern "C" {
+ __declspec(dllimport) void __stdcall DebugBreak(void);
+ }
+ #define Crash() {DebugBreak();}
+#endif // _MSC_VER ndef
+
+// Generic function to get last error message.
+// Call directly after the command or use the error num.
+// This function might change the error code.
+// Defined in Misc.cpp.
+const char* GetLastErrorMsg();
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+// Directory separators, do we need this?
+#define DIR_SEP "/"
+#define DIR_SEP_CHR '/'
+
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif
+
+// The user data dir
+#define ROOT_DIR "."
+#define USERDATA_DIR "user"
+#ifdef USER_DIR
+ #define EMU_DATA_DIR USER_DIR
+#else
+ #ifdef _WIN32
+ #define EMU_DATA_DIR "Citra Emulator"
+ #else
+ #define EMU_DATA_DIR "citra-emu"
+ #endif
+#endif
+
+// Dirs in both User and Sys
+#define EUR_DIR "EUR"
+#define USA_DIR "USA"
+#define JAP_DIR "JAP"
+
+// Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
+#define CONFIG_DIR "config"
+#define GAMECONFIG_DIR "game_config"
+#define MAPS_DIR "maps"
+#define CACHE_DIR "cache"
+#define SDMC_DIR "sdmc"
+#define NAND_DIR "nand"
+#define SYSDATA_DIR "sysdata"
+#define SHADERCACHE_DIR "shader_cache"
+#define STATESAVES_DIR "state_saves"
+#define SCREENSHOTS_DIR "screenShots"
+#define DUMP_DIR "dump"
+#define DUMP_TEXTURES_DIR "textures"
+#define DUMP_FRAMES_DIR "frames"
+#define DUMP_AUDIO_DIR "audio"
+#define LOGS_DIR "logs"
+#define SHADERS_DIR "shaders"
+#define SYSCONF_DIR "sysconf"
+
+// Filenames
+// Files in the directory returned by GetUserPath(D_CONFIG_IDX)
+#define EMU_CONFIG "emu.ini"
+#define DEBUGGER_CONFIG "debugger.ini"
+#define LOGGER_CONFIG "logger.ini"
+
+// Sys files
+#define SHARED_FONT "shared_font.bin"
+
+// Files in the directory returned by GetUserPath(D_LOGS_IDX)
+#define MAIN_LOG "emu.log"
+
+// Files in the directory returned by GetUserPath(D_SYSCONF_IDX)
+#define SYSCONF "SYSCONF"
--- /dev/null
+/**
+ * Copyright (C) 2005-2012 Gekko Emulator
+ *
+ * @file common_types.h
+ * @author ShizZy <shizzy247@gmail.com>
+ * @date 2012-02-11
+ * @brief Common types used throughout the project
+ *
+ * @section LICENSE
+ * 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 of
+ * the License, 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 at
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * Official project repository can be found at:
+ * http://code.google.com/p/gekko-gc-emu/
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#ifdef _MSC_VER
+#ifndef __func__
+#define __func__ __FUNCTION__
+#endif
+#endif
+
+typedef std::uint8_t u8; ///< 8-bit unsigned byte
+typedef std::uint16_t u16; ///< 16-bit unsigned short
+typedef std::uint32_t u32; ///< 32-bit unsigned word
+typedef std::uint64_t u64; ///< 64-bit unsigned int
+
+typedef std::int8_t s8; ///< 8-bit signed byte
+typedef std::int16_t s16; ///< 16-bit signed short
+typedef std::int32_t s32; ///< 32-bit signed word
+typedef std::int64_t s64; ///< 64-bit signed int
+
+typedef float f32; ///< 32-bit floating point
+typedef double f64; ///< 64-bit floating point
+
+// TODO: It would be nice to eventually replace these with strong types that prevent accidental
+// conversion between each other.
+typedef u32 VAddr; ///< Represents a pointer in the userspace virtual address space.
+typedef u32 PAddr; ///< Represents a pointer in the ARM11 physical address space.
+
+// An inheritable class to disallow the copy constructor and operator= functions
+class NonCopyable {
+protected:
+ NonCopyable() = default;
+ ~NonCopyable() = default;
+
+ NonCopyable(NonCopyable&) = delete;
+ NonCopyable& operator=(NonCopyable&) = delete;
+};
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cmath>
+
+#include "common/assert.h"
+#include "common/key_map.h"
+
+#include "emu_window.h"
+#include "video_core/video_core.h"
+
+void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) {
+ pad_state.hex |= KeyMap::GetPadKey(key).hex;
+}
+
+void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) {
+ pad_state.hex &= ~KeyMap::GetPadKey(key).hex;
+}
+
+/**
+ * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
+ * @param layout FramebufferLayout object describing the framebuffer size and screen positions
+ * @param framebuffer_x Framebuffer x-coordinate to check
+ * @param framebuffer_y Framebuffer y-coordinate to check
+ * @return True if the coordinates are within the touchpad, otherwise false
+ */
+static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsigned framebuffer_x,
+ unsigned framebuffer_y) {
+ return (framebuffer_y >= layout.bottom_screen.top &&
+ framebuffer_y < layout.bottom_screen.bottom &&
+ framebuffer_x >= layout.bottom_screen.left &&
+ framebuffer_x < layout.bottom_screen.right);
+}
+
+std::tuple<unsigned,unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) {
+
+ new_x = std::max(new_x, framebuffer_layout.bottom_screen.left);
+ new_x = std::min(new_x, framebuffer_layout.bottom_screen.right-1);
+
+ new_y = std::max(new_y, framebuffer_layout.bottom_screen.top);
+ new_y = std::min(new_y, framebuffer_layout.bottom_screen.bottom-1);
+
+ return std::make_tuple(new_x, new_y);
+}
+
+void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
+ if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
+ return;
+
+ touch_x = VideoCore::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) /
+ (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
+ touch_y = VideoCore::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) /
+ (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
+
+ touch_pressed = true;
+ pad_state.touch = 1;
+}
+
+void EmuWindow::TouchReleased() {
+ touch_pressed = false;
+ touch_x = 0;
+ touch_y = 0;
+ pad_state.touch = 0;
+}
+
+void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
+ if (!touch_pressed)
+ return;
+
+ if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
+ std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
+
+ TouchPressed(framebuffer_x, framebuffer_y);
+}
+
+EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) {
+
+ ASSERT(width > 0);
+ ASSERT(height > 0);
+
+ EmuWindow::FramebufferLayout res = { width, height, {}, {} };
+
+ float window_aspect_ratio = static_cast<float>(height) / width;
+ float emulation_aspect_ratio = static_cast<float>(VideoCore::kScreenTopHeight * 2) /
+ VideoCore::kScreenTopWidth;
+
+ if (window_aspect_ratio > emulation_aspect_ratio) {
+ // Window is narrower than the emulation content => apply borders to the top and bottom
+ int viewport_height = static_cast<int>(std::round(emulation_aspect_ratio * width));
+
+ res.top_screen.left = 0;
+ res.top_screen.right = res.top_screen.left + width;
+ res.top_screen.top = (height - viewport_height) / 2;
+ res.top_screen.bottom = res.top_screen.top + viewport_height / 2;
+
+ int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) /
+ VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left));
+ int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
+
+ res.bottom_screen.left = bottom_border;
+ res.bottom_screen.right = res.bottom_screen.left + bottom_width;
+ res.bottom_screen.top = res.top_screen.bottom;
+ res.bottom_screen.bottom = res.bottom_screen.top + viewport_height / 2;
+ } else {
+ // Otherwise, apply borders to the left and right sides of the window.
+ int viewport_width = static_cast<int>(std::round(height / emulation_aspect_ratio));
+
+ res.top_screen.left = (width - viewport_width) / 2;
+ res.top_screen.right = res.top_screen.left + viewport_width;
+ res.top_screen.top = 0;
+ res.top_screen.bottom = res.top_screen.top + height / 2;
+
+ int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) /
+ VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left));
+ int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
+
+ res.bottom_screen.left = res.top_screen.left + bottom_border;
+ res.bottom_screen.right = res.bottom_screen.left + bottom_width;
+ res.bottom_screen.top = res.top_screen.bottom;
+ res.bottom_screen.bottom = res.bottom_screen.top + height / 2;
+ }
+
+ return res;
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <tuple>
+#include <utility>
+
+#include "citraimport\common/common_types.h"
+#include "citraimport\common/math_util.h"
+
+namespace KeyMap {
+struct HostDeviceKey;
+}
+
+/**
+ * Abstraction class used to provide an interface between emulation code and the frontend
+ * (e.g. SDL, QGLWidget, GLFW, etc...).
+ *
+ * Design notes on the interaction between EmuWindow and the emulation core:
+ * - Generally, decisions on anything visible to the user should be left up to the GUI.
+ * For example, the emulation core should not try to dictate some window title or size.
+ * This stuff is not the core's business and only causes problems with regards to thread-safety
+ * anyway.
+ * - Under certain circumstances, it may be desirable for the core to politely request the GUI
+ * to set e.g. a minimum window size. However, the GUI should always be free to ignore any
+ * such hints.
+ * - EmuWindow may expose some of its state as read-only to the emulation core, however care
+ * should be taken to make sure the provided information is self-consistent. This requires
+ * some sort of synchronization (most of this is still a TODO).
+ * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
+ * re-read the upper points again and think about it if you don't see this.
+ */
+class EmuWindow
+{
+public:
+ /// Data structure to store emuwindow configuration
+ struct WindowConfig {
+ bool fullscreen;
+ int res_width;
+ int res_height;
+ std::pair<unsigned,unsigned> min_client_area_size;
+ };
+
+ /// Describes the layout of the window framebuffer (size and top/bottom screen positions)
+ struct FramebufferLayout {
+
+ /**
+ * Factory method for constructing a default FramebufferLayout
+ * @param width Window framebuffer width in pixels
+ * @param height Window framebuffer height in pixels
+ * @return Newly created FramebufferLayout object with default screen regions initialized
+ */
+ static FramebufferLayout DefaultScreenLayout(unsigned width, unsigned height);
+
+ unsigned width;
+ unsigned height;
+ MathUtil::Rectangle<unsigned> top_screen;
+ MathUtil::Rectangle<unsigned> bottom_screen;
+ };
+
+ /// Swap buffers to display the next frame
+ virtual void SwapBuffers() = 0;
+
+ /// Polls window events
+ virtual void PollEvents() = 0;
+
+ /// Makes the graphics context current for the caller thread
+ virtual void MakeCurrent() = 0;
+
+ /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
+ virtual void DoneCurrent() = 0;
+
+ virtual void ReloadSetKeymaps() = 0;
+
+ /// Signals a key press action to the HID module
+ void KeyPressed(KeyMap::HostDeviceKey key);
+
+ /// Signals a key release action to the HID module
+ void KeyReleased(KeyMap::HostDeviceKey key);
+
+ /**
+ * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
+ * @param framebuffer_x Framebuffer x-coordinate that was pressed
+ * @param framebuffer_y Framebuffer y-coordinate that was pressed
+ */
+ void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y);
+
+ /// Signal that a touch released event has occurred (e.g. mouse click released)
+ void TouchReleased();
+
+ /**
+ * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
+ * @param framebuffer_x Framebuffer x-coordinate
+ * @param framebuffer_y Framebuffer y-coordinate
+ */
+ void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
+
+ /**
+ * Gets the current pad state (which buttons are pressed and the circle pad direction).
+ * @note This should be called by the core emu thread to get a state set by the window thread.
+ * @todo Fix this function to be thread-safe.
+ * @return PadState object indicating the current pad state
+ */
+ /*const Service::HID::PadState GetPadState() const {
+ return pad_state;
+ }*/ //hid todo
+
+ /**
+ * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
+ * @note This should be called by the core emu thread to get a state set by the window thread.
+ * @todo Fix this function to be thread-safe.
+ * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
+ * `pressed` is true if the touch screen is currently being pressed
+ */
+ const std::tuple<u16, u16, bool> GetTouchState() const {
+ return std::make_tuple(touch_x, touch_y, touch_pressed);
+ }
+
+ /**
+ * Returns currently active configuration.
+ * @note Accesses to the returned object need not be consistent because it may be modified in another thread
+ */
+ const WindowConfig& GetActiveConfig() const {
+ return active_config;
+ }
+
+ /**
+ * Requests the internal configuration to be replaced by the specified argument at some point in the future.
+ * @note This method is thread-safe, because it delays configuration changes to the GUI event loop. Hence there is no guarantee on when the requested configuration will be active.
+ */
+ void SetConfig(const WindowConfig& val) {
+ config = val;
+ }
+
+ /**
+ * Gets the framebuffer layout (width, height, and screen regions)
+ * @note This method is thread-safe
+ */
+ const FramebufferLayout& GetFramebufferLayout() const {
+ return framebuffer_layout;
+ }
+
+protected:
+ EmuWindow() {
+ // TODO: Find a better place to set this.
+ config.min_client_area_size = std::make_pair(400u, 480u);
+ active_config = config;
+ //pad_state.hex = 0;
+ touch_x = 0;
+ touch_y = 0;
+ touch_pressed = false;
+ }
+ virtual ~EmuWindow() {}
+
+ /**
+ * Processes any pending configuration changes from the last SetConfig call.
+ * This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration
+ * field changed.
+ * @note Implementations will usually want to call this from the GUI thread.
+ * @todo Actually call this in existing implementations.
+ */
+ void ProcessConfigurationChanges() {
+ // TODO: For proper thread safety, we should eventually implement a proper
+ // multiple-writer/single-reader queue...
+
+ if (config.min_client_area_size != active_config.min_client_area_size) {
+ OnMinimalClientAreaChangeRequest(config.min_client_area_size);
+ config.min_client_area_size = active_config.min_client_area_size;
+ }
+ }
+
+ /**
+ * Update framebuffer layout with the given parameter.
+ * @note EmuWindow implementations will usually use this in window resize event handlers.
+ */
+ void NotifyFramebufferLayoutChanged(const FramebufferLayout& layout) {
+ framebuffer_layout = layout;
+ }
+
+ /**
+ * Update internal client area size with the given parameter.
+ * @note EmuWindow implementations will usually use this in window resize event handlers.
+ */
+ void NotifyClientAreaSizeChanged(const std::pair<unsigned,unsigned>& size) {
+ client_area_width = size.first;
+ client_area_height = size.second;
+ }
+
+private:
+ /**
+ * Handler called when the minimal client area was requested to be changed via SetConfig.
+ * For the request to be honored, EmuWindow implementations will usually reimplement this function.
+ */
+ virtual void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
+ // By default, ignore this request and do nothing.
+ }
+
+ FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
+
+ unsigned client_area_width; ///< Current client width, should be set by window impl.
+ unsigned client_area_height; ///< Current client height, should be set by window impl.
+
+ WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges)
+ WindowConfig active_config; ///< Internal active configuration
+
+ bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false
+
+ u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
+ u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
+
+ /**
+ * Clip the provided coordinates to be inside the touchscreen area.
+ */
+ std::tuple<unsigned,unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y);
+
+ //Service::HID::PadState pad_state; //hid todo
+};
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "citraimport\common/assert.h"
+#include "citraimport\common/common_funcs.h"
+#include "citraimport\common/common_paths.h"
+#include "citraimport\common/file_util.h"
+#include "citraimport\common/logging/log.h"
+
+#ifdef _WIN32
+ #include <windows.h>
+ #include <shlobj.h> // for SHGetFolderPath
+ #include <shellapi.h>
+ #include <commdlg.h> // for GetSaveFileName
+ #include <io.h>
+ #include <direct.h> // getcwd
+ #include <tchar.h>
+
+ #include "citraimport\common/string_util.h"
+
+ // 64 bit offsets for windows
+ #define fseeko _fseeki64
+ #define ftello _ftelli64
+ #define atoll _atoi64
+ #define stat64 _stat64
+ #define fstat64 _fstat64
+ #define fileno _fileno
+#else
+ #ifdef __APPLE__
+ #include <sys/param.h>
+ #endif
+ #include <cctype>
+ #include <cerrno>
+ #include <cstdlib>
+ #include <cstring>
+ #include <dirent.h>
+ #include <pwd.h>
+ #include <unistd.h>
+#endif
+
+#if defined(__APPLE__)
+ #include <CoreFoundation/CFString.h>
+ #include <CoreFoundation/CFURL.h>
+ #include <CoreFoundation/CFBundle.h>
+#endif
+
+#include <algorithm>
+#include <sys/stat.h>
+
+#ifndef S_ISDIR
+ #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
+#endif
+
+#ifdef BSD4_4
+ #define stat64 stat
+ #define fstat64 fstat
+#endif
+
+// This namespace has various generic functions related to files and paths.
+// The code still needs a ton of cleanup.
+// REMEMBER: strdup considered harmful!
+namespace FileUtil
+{
+
+// Remove any ending forward slashes from directory paths
+// Modifies argument.
+static void StripTailDirSlashes(std::string &fname)
+{
+ if (fname.length() > 1)
+ {
+ size_t i = fname.length() - 1;
+ while (fname[i] == DIR_SEP_CHR)
+ fname[i--] = '\0';
+ }
+ return;
+}
+
+// Returns true if file filename exists
+bool Exists(const std::string &filename)
+{
+ struct stat64 file_info;
+
+ std::string copy(filename);
+ StripTailDirSlashes(copy);
+
+#ifdef _WIN32
+ int result = _tstat64(Common::UTF8ToTStr(copy).c_str(), &file_info);
+#else
+ int result = stat64(copy.c_str(), &file_info);
+#endif
+
+ return (result == 0);
+}
+
+// Returns true if filename is a directory
+bool IsDirectory(const std::string &filename)
+{
+ struct stat64 file_info;
+
+ std::string copy(filename);
+ StripTailDirSlashes(copy);
+
+#ifdef _WIN32
+ int result = _tstat64(Common::UTF8ToTStr(copy).c_str(), &file_info);
+#else
+ int result = stat64(copy.c_str(), &file_info);
+#endif
+
+ if (result < 0) {
+ LOG_WARNING(Common_Filesystem, "stat failed on %s: %s",
+ filename.c_str(), GetLastErrorMsg());
+ return false;
+ }
+
+ return S_ISDIR(file_info.st_mode);
+}
+
+// Deletes a given filename, return true on success
+// Doesn't supports deleting a directory
+bool Delete(const std::string &filename)
+{
+ LOG_INFO(Common_Filesystem, "file %s", filename.c_str());
+
+ // Return true because we care about the file no
+ // being there, not the actual delete.
+ if (!Exists(filename))
+ {
+ LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str());
+ return true;
+ }
+
+ // We can't delete a directory
+ if (IsDirectory(filename))
+ {
+ LOG_ERROR(Common_Filesystem, "Failed: %s is a directory", filename.c_str());
+ return false;
+ }
+
+#ifdef _WIN32
+ if (!DeleteFile(Common::UTF8ToTStr(filename).c_str()))
+ {
+ LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s",
+ filename.c_str(), GetLastErrorMsg());
+ return false;
+ }
+#else
+ if (unlink(filename.c_str()) == -1) {
+ LOG_ERROR(Common_Filesystem, "unlink failed on %s: %s",
+ filename.c_str(), GetLastErrorMsg());
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+// Returns true if successful, or path already exists.
+bool CreateDir(const std::string &path)
+{
+ LOG_TRACE(Common_Filesystem, "directory %s", path.c_str());
+#ifdef _WIN32
+ if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr))
+ return true;
+ DWORD error = GetLastError();
+ if (error == ERROR_ALREADY_EXISTS)
+ {
+ LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists", path.c_str());
+ return true;
+ }
+ LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error);
+ return false;
+#else
+ if (mkdir(path.c_str(), 0755) == 0)
+ return true;
+
+ int err = errno;
+
+ if (err == EEXIST)
+ {
+ LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str());
+ return true;
+ }
+
+ LOG_ERROR(Common_Filesystem, "mkdir failed on %s: %s", path.c_str(), strerror(err));
+ return false;
+#endif
+}
+
+// Creates the full path of fullPath returns true on success
+bool CreateFullPath(const std::string &fullPath)
+{
+ int panicCounter = 100;
+ LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str());
+
+ if (FileUtil::Exists(fullPath))
+ {
+ LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str());
+ return true;
+ }
+
+ size_t position = 0;
+ while (true)
+ {
+ // Find next sub path
+ position = fullPath.find(DIR_SEP_CHR, position);
+
+ // we're done, yay!
+ if (position == fullPath.npos)
+ return true;
+
+ // Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
+ std::string const subPath(fullPath.substr(0, position + 1));
+ if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) {
+ LOG_ERROR(Common, "CreateFullPath: directory creation failed");
+ return false;
+ }
+
+ // A safety check
+ panicCounter--;
+ if (panicCounter <= 0)
+ {
+ LOG_ERROR(Common, "CreateFullPath: directory structure is too deep");
+ return false;
+ }
+ position++;
+ }
+}
+
+
+// Deletes a directory filename, returns true on success
+bool DeleteDir(const std::string &filename)
+{
+ LOG_INFO(Common_Filesystem, "directory %s", filename.c_str());
+
+ // check if a directory
+ if (!FileUtil::IsDirectory(filename))
+ {
+ LOG_ERROR(Common_Filesystem, "Not a directory %s", filename.c_str());
+ return false;
+ }
+
+#ifdef _WIN32
+ if (::RemoveDirectory(Common::UTF8ToTStr(filename).c_str()))
+ return true;
+#else
+ if (rmdir(filename.c_str()) == 0)
+ return true;
+#endif
+ LOG_ERROR(Common_Filesystem, "failed %s: %s", filename.c_str(), GetLastErrorMsg());
+
+ return false;
+}
+
+// renames file srcFilename to destFilename, returns true on success
+bool Rename(const std::string &srcFilename, const std::string &destFilename)
+{
+ LOG_TRACE(Common_Filesystem, "%s --> %s",
+ srcFilename.c_str(), destFilename.c_str());
+ if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
+ return true;
+ LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
+ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
+ return false;
+}
+
+// copies file srcFilename to destFilename, returns true on success
+bool Copy(const std::string &srcFilename, const std::string &destFilename)
+{
+ LOG_TRACE(Common_Filesystem, "%s --> %s",
+ srcFilename.c_str(), destFilename.c_str());
+#ifdef _WIN32
+ if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE))
+ return true;
+
+ LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
+ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
+ return false;
+#else
+
+ // buffer size
+#define BSIZE 1024
+
+ char buffer[BSIZE];
+
+ // Open input file
+ FILE *input = fopen(srcFilename.c_str(), "rb");
+ if (!input)
+ {
+ LOG_ERROR(Common_Filesystem, "opening input failed %s --> %s: %s",
+ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
+ return false;
+ }
+
+ // open output file
+ FILE *output = fopen(destFilename.c_str(), "wb");
+ if (!output)
+ {
+ fclose(input);
+ LOG_ERROR(Common_Filesystem, "opening output failed %s --> %s: %s",
+ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
+ return false;
+ }
+
+ // copy loop
+ while (!feof(input))
+ {
+ // read input
+ int rnum = fread(buffer, sizeof(char), BSIZE, input);
+ if (rnum != BSIZE)
+ {
+ if (ferror(input) != 0)
+ {
+ LOG_ERROR(Common_Filesystem,
+ "failed reading from source, %s --> %s: %s",
+ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
+ goto bail;
+ }
+ }
+
+ // write output
+ int wnum = fwrite(buffer, sizeof(char), rnum, output);
+ if (wnum != rnum)
+ {
+ LOG_ERROR(Common_Filesystem,
+ "failed writing to output, %s --> %s: %s",
+ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
+ goto bail;
+ }
+ }
+ // close files
+ fclose(input);
+ fclose(output);
+ return true;
+bail:
+ if (input)
+ fclose(input);
+ if (output)
+ fclose(output);
+ return false;
+#endif
+}
+
+// Returns the size of filename (64bit)
+u64 GetSize(const std::string &filename)
+{
+ if (!Exists(filename))
+ {
+ LOG_ERROR(Common_Filesystem, "failed %s: No such file", filename.c_str());
+ return 0;
+ }
+
+ if (IsDirectory(filename))
+ {
+ LOG_ERROR(Common_Filesystem, "failed %s: is a directory", filename.c_str());
+ return 0;
+ }
+
+ struct stat64 buf;
+#ifdef _WIN32
+ if (_tstat64(Common::UTF8ToTStr(filename).c_str(), &buf) == 0)
+#else
+ if (stat64(filename.c_str(), &buf) == 0)
+#endif
+ {
+ LOG_TRACE(Common_Filesystem, "%s: %lld",
+ filename.c_str(), (long long)buf.st_size);
+ return buf.st_size;
+ }
+
+ LOG_ERROR(Common_Filesystem, "Stat failed %s: %s",
+ filename.c_str(), GetLastErrorMsg());
+ return 0;
+}
+
+// Overloaded GetSize, accepts file descriptor
+u64 GetSize(const int fd)
+{
+ struct stat64 buf;
+ if (fstat64(fd, &buf) != 0) {
+ LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s",
+ fd, GetLastErrorMsg());
+ return 0;
+ }
+ return buf.st_size;
+}
+
+// Overloaded GetSize, accepts FILE*
+u64 GetSize(FILE *f)
+{
+ // can't use off_t here because it can be 32-bit
+ u64 pos = ftello(f);
+ if (fseeko(f, 0, SEEK_END) != 0) {
+ LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s",
+ f, GetLastErrorMsg());
+ return 0;
+ }
+ u64 size = ftello(f);
+ if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) {
+ LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s",
+ f, GetLastErrorMsg());
+ return 0;
+ }
+ return size;
+}
+
+// creates an empty file filename, returns true on success
+bool CreateEmptyFile(const std::string &filename)
+{
+ LOG_TRACE(Common_Filesystem, "%s", filename.c_str());
+
+ if (!FileUtil::IOFile(filename, "wb"))
+ {
+ LOG_ERROR(Common_Filesystem, "failed %s: %s",
+ filename.c_str(), GetLastErrorMsg());
+ return false;
+ }
+
+ return true;
+}
+
+
+int ScanDirectoryTreeAndCallback(const std::string &directory, std::function<int(const std::string&, const std::string&)> callback)
+{
+ LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str());
+ // How many files + directories we found
+ int found_entries = 0;
+#ifdef _WIN32
+ // Find the first file in the directory.
+ WIN32_FIND_DATA ffd;
+
+ HANDLE handle_find = FindFirstFile(Common::UTF8ToTStr(directory + "\\*").c_str(), &ffd);
+ if (handle_find == INVALID_HANDLE_VALUE) {
+ FindClose(handle_find);
+ return found_entries;
+ }
+ // windows loop
+ do {
+ const std::string virtual_name(Common::TStrToUTF8(ffd.cFileName));
+#else
+ struct dirent dirent, *result = nullptr;
+
+ DIR *dirp = opendir(directory.c_str());
+ if (!dirp)
+ return 0;
+
+ // non windows loop
+ while (!readdir_r(dirp, &dirent, &result) && result) {
+ const std::string virtual_name(result->d_name);
+#endif
+ // check for "." and ".."
+ if (((virtual_name[0] == '.') && (virtual_name[1] == '\0')) ||
+ ((virtual_name[0] == '.') && (virtual_name[1] == '.') &&
+ (virtual_name[2] == '\0')))
+ continue;
+
+ int ret = callback(directory, virtual_name);
+ if (ret < 0) {
+ if (ret != -1)
+ found_entries = ret;
+ break;
+ }
+ found_entries += ret;
+
+#ifdef _WIN32
+ } while (FindNextFile(handle_find, &ffd) != 0);
+ FindClose(handle_find);
+#else
+ }
+ closedir(dirp);
+#endif
+ // Return number of entries found.
+ return found_entries;
+}
+
+int ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry)
+{
+ const auto callback = [&parent_entry](const std::string& directory,
+ const std::string& virtual_name) -> int {
+ FSTEntry entry;
+ int found_entries = 0;
+ entry.virtualName = virtual_name;
+ entry.physicalName = directory + DIR_SEP + virtual_name;
+
+ if (IsDirectory(entry.physicalName)) {
+ entry.isDirectory = true;
+ // is a directory, lets go inside
+ entry.size = ScanDirectoryTree(entry.physicalName, entry);
+ found_entries += (int)entry.size;
+ } else { // is a file
+ entry.isDirectory = false;
+ entry.size = GetSize(entry.physicalName);
+ }
+ ++found_entries;
+ // Push into the tree
+ parent_entry.children.push_back(entry);
+ return found_entries;
+ };
+
+ return ScanDirectoryTreeAndCallback(directory, callback);
+}
+
+
+bool DeleteDirRecursively(const std::string &directory)
+{
+ const static auto callback = [](const std::string& directory,
+ const std::string& virtual_name) -> int {
+ std::string new_path = directory + DIR_SEP_CHR + virtual_name;
+ if (IsDirectory(new_path)) {
+ if (!DeleteDirRecursively(new_path)) {
+ return -2;
+ }
+ } else if (!Delete(new_path)) {
+ return -2;
+ }
+ return 0;
+ };
+
+ if (ScanDirectoryTreeAndCallback(directory, callback) == -2) {
+ return false;
+ }
+ FileUtil::DeleteDir(directory);
+
+ return true;
+}
+
+// Create directory and copy contents (does not overwrite existing files)
+void CopyDir(const std::string &source_path, const std::string &dest_path)
+{
+#ifndef _WIN32
+ if (source_path == dest_path) return;
+ if (!FileUtil::Exists(source_path)) return;
+ if (!FileUtil::Exists(dest_path)) FileUtil::CreateFullPath(dest_path);
+
+ struct dirent dirent, *result = nullptr;
+ DIR *dirp = opendir(source_path.c_str());
+ if (!dirp) return;
+
+ while (!readdir_r(dirp, &dirent, &result) && result)
+ {
+ const std::string virtualName(result->d_name);
+ // check for "." and ".."
+ if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
+ ((virtualName[0] == '.') && (virtualName[1] == '.') &&
+ (virtualName[2] == '\0')))
+ continue;
+
+ std::string source, dest;
+ source = source_path + virtualName;
+ dest = dest_path + virtualName;
+ if (IsDirectory(source))
+ {
+ source += '/';
+ dest += '/';
+ if (!FileUtil::Exists(dest)) FileUtil::CreateFullPath(dest);
+ CopyDir(source, dest);
+ }
+ else if (!FileUtil::Exists(dest)) FileUtil::Copy(source, dest);
+ }
+ closedir(dirp);
+#endif
+}
+
+// Returns the current directory
+std::string GetCurrentDir()
+{
+ char *dir;
+ // Get the current working directory (getcwd uses malloc)
+ if (!(dir = getcwd(nullptr, 0))) {
+
+ LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s",
+ GetLastErrorMsg());
+ return nullptr;
+ }
+ std::string strDir = dir;
+ free(dir);
+ return strDir;
+}
+
+// Sets the current directory to the given directory
+bool SetCurrentDir(const std::string &directory)
+{
+ return chdir(directory.c_str()) == 0;
+}
+
+#if defined(__APPLE__)
+std::string GetBundleDirectory()
+{
+ CFURLRef BundleRef;
+ char AppBundlePath[MAXPATHLEN];
+ // Get the main bundle for the app
+ BundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+ CFStringRef BundlePath = CFURLCopyFileSystemPath(BundleRef, kCFURLPOSIXPathStyle);
+ CFStringGetFileSystemRepresentation(BundlePath, AppBundlePath, sizeof(AppBundlePath));
+ CFRelease(BundleRef);
+ CFRelease(BundlePath);
+
+ return AppBundlePath;
+}
+#endif
+
+#ifdef _WIN32
+std::string& GetExeDirectory()
+{
+ static std::string exe_path;
+ if (exe_path.empty())
+ {
+ TCHAR tchar_exe_path[2048];
+ GetModuleFileName(nullptr, tchar_exe_path, 2048);
+ exe_path = Common::TStrToUTF8(tchar_exe_path);
+ exe_path = exe_path.substr(0, exe_path.find_last_of('\\'));
+ }
+ return exe_path;
+}
+#else
+/**
+ * @return The user’s home directory on POSIX systems
+ */
+static const std::string& GetHomeDirectory() {
+ static std::string home_path;
+ if (home_path.empty()) {
+ const char* envvar = getenv("HOME");
+ if (envvar) {
+ home_path = envvar;
+ } else {
+ auto pw = getpwuid(getuid());
+ ASSERT_MSG(pw, "$HOME isn’t defined, and the current user can’t be found in /etc/passwd.");
+ home_path = pw->pw_dir;
+ }
+ }
+ return home_path;
+}
+
+/**
+ * Follows the XDG Base Directory Specification to get a directory path
+ * @param envvar The XDG environment variable to get the value from
+ * @return The directory path
+ * @sa http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+ */
+static const std::string GetUserDirectory(const std::string& envvar) {
+ const char* directory = getenv(envvar.c_str());
+
+ std::string user_dir;
+ if (directory) {
+ user_dir = directory;
+ } else {
+ std::string subdirectory;
+ if (envvar == "XDG_DATA_HOME")
+ subdirectory = DIR_SEP ".local" DIR_SEP "share";
+ else if (envvar == "XDG_CONFIG_HOME")
+ subdirectory = DIR_SEP ".config";
+ else if (envvar == "XDG_CACHE_HOME")
+ subdirectory = DIR_SEP ".cache";
+ else
+ ASSERT_MSG(false, "Unknown XDG variable %s.", envvar.c_str());
+ user_dir = GetHomeDirectory() + subdirectory;
+ }
+
+ ASSERT_MSG(!user_dir.empty(), "User directory %s musn’t be empty.", envvar.c_str());
+ ASSERT_MSG(user_dir[0] == '/', "User directory %s must be absolute.", envvar.c_str());
+
+ return user_dir;
+}
+#endif
+
+std::string GetSysDirectory()
+{
+ std::string sysDir;
+
+#if defined (__APPLE__)
+ sysDir = GetBundleDirectory();
+ sysDir += DIR_SEP;
+ sysDir += SYSDATA_DIR;
+#else
+ sysDir = SYSDATA_DIR;
+#endif
+ sysDir += DIR_SEP;
+
+ LOG_DEBUG(Common_Filesystem, "Setting to %s:", sysDir.c_str());
+ return sysDir;
+}
+
+// Returns a string with a Citra data dir or file in the user's home
+// directory. To be used in "multi-user" mode (that is, installed).
+const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath)
+{
+ static std::string paths[NUM_PATH_INDICES];
+
+ // Set up all paths and files on the first run
+ if (paths[D_USER_IDX].empty())
+ {
+#ifdef _WIN32
+ paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
+ paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
+ paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
+#else
+ if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
+ paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
+ paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
+ paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
+ } else {
+ std::string data_dir = GetUserDirectory("XDG_DATA_HOME");
+ std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME");
+ std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME");
+
+ paths[D_USER_IDX] = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
+ paths[D_CONFIG_IDX] = config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
+ paths[D_CACHE_IDX] = cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
+ }
+#endif
+
+ paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP;
+ paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
+ paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
+ paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
+ paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
+ paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
+ paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
+ paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
+ paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
+ paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP;
+ paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
+ paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
+ paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
+ paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP;
+ paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
+ paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
+ paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
+ }
+
+ if (!newPath.empty())
+ {
+ if (!FileUtil::IsDirectory(newPath))
+ {
+ LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str());
+ return paths[DirIDX];
+ }
+ else
+ {
+ paths[DirIDX] = newPath;
+ }
+
+ switch (DirIDX)
+ {
+ case D_ROOT_IDX:
+ paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
+ paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR + DIR_SEP;
+ paths[F_SYSCONF_IDX] = paths[D_SYSCONF_IDX] + SYSCONF;
+ break;
+
+ case D_USER_IDX:
+ paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
+ paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
+ paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP;
+ paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
+ paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
+ paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
+ paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
+ paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
+ paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
+ paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
+ paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
+ paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP;
+ paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
+ paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
+ paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
+ paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP;
+ paths[D_SYSCONF_IDX] = paths[D_USER_IDX] + SYSCONF_DIR DIR_SEP;
+ paths[F_EMUCONFIG_IDX] = paths[D_CONFIG_IDX] + EMU_CONFIG;
+ paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
+ paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
+ paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
+ break;
+
+ case D_CONFIG_IDX:
+ paths[F_EMUCONFIG_IDX] = paths[D_CONFIG_IDX] + EMU_CONFIG;
+ paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
+ paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
+ break;
+
+ case D_DUMP_IDX:
+ paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
+ paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
+ paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
+ break;
+
+ case D_LOGS_IDX:
+ paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
+ }
+ }
+
+ return paths[DirIDX];
+}
+
+size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename)
+{
+ return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
+}
+
+size_t ReadFileToString(bool text_file, const char *filename, std::string &str)
+{
+ FileUtil::IOFile file(filename, text_file ? "r" : "rb");
+ auto const f = file.GetHandle();
+
+ if (!f)
+ return false;
+
+ str.resize(static_cast<u32>(GetSize(f)));
+ return file.ReadArray(&str[0], str.size());
+}
+
+/**
+ * Splits the filename into 8.3 format
+ * Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename
+ * @param filename The normal filename to use
+ * @param short_name A 9-char array in which the short name will be written
+ * @param extension A 4-char array in which the extension will be written
+ */
+void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
+ std::array<char, 4>& extension) {
+ const std::string forbidden_characters = ".\"/\\[]:;=, ";
+
+ // On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces.
+ short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}};
+ extension = {{' ', ' ', ' ', '\0'}};
+
+ std::string::size_type point = filename.rfind('.');
+ if (point == filename.size() - 1)
+ point = filename.rfind('.', point);
+
+ // Get short name.
+ int j = 0;
+ for (char letter : filename.substr(0, point)) {
+ if (forbidden_characters.find(letter, 0) != std::string::npos)
+ continue;
+ if (j == 8) {
+ // TODO(Link Mauve): also do that for filenames containing a space.
+ // TODO(Link Mauve): handle multiple files having the same short name.
+ short_name[6] = '~';
+ short_name[7] = '1';
+ break;
+ }
+ short_name[j++] = toupper(letter);
+ }
+
+ // Get extension.
+ if (point != std::string::npos) {
+ j = 0;
+ for (char letter : filename.substr(point + 1, 3))
+ extension[j++] = toupper(letter);
+ }
+}
+
+IOFile::IOFile()
+ : m_file(nullptr), m_good(true)
+{}
+
+IOFile::IOFile(std::FILE* file)
+ : m_file(file), m_good(true)
+{}
+
+IOFile::IOFile(const std::string& filename, const char openmode[])
+ : m_file(nullptr), m_good(true)
+{
+ Open(filename, openmode);
+}
+
+IOFile::~IOFile()
+{
+ Close();
+}
+
+IOFile::IOFile(IOFile&& other)
+ : m_file(nullptr), m_good(true)
+{
+ Swap(other);
+}
+
+IOFile& IOFile::operator=(IOFile&& other)
+{
+ Swap(other);
+ return *this;
+}
+
+void IOFile::Swap(IOFile& other)
+{
+ std::swap(m_file, other.m_file);
+ std::swap(m_good, other.m_good);
+}
+
+bool IOFile::Open(const std::string& filename, const char openmode[])
+{
+ Close();
+#ifdef _WIN32
+ _tfopen_s(&m_file, Common::UTF8ToTStr(filename).c_str(), Common::UTF8ToTStr(openmode).c_str());
+#else
+ m_file = fopen(filename.c_str(), openmode);
+#endif
+
+ m_good = IsOpen();
+ return m_good;
+}
+
+bool IOFile::Close()
+{
+ if (!IsOpen() || 0 != std::fclose(m_file))
+ m_good = false;
+
+ m_file = nullptr;
+ return m_good;
+}
+
+std::FILE* IOFile::ReleaseHandle()
+{
+ std::FILE* const ret = m_file;
+ m_file = nullptr;
+ return ret;
+}
+
+void IOFile::SetHandle(std::FILE* file)
+{
+ Close();
+ Clear();
+ m_file = file;
+}
+
+u64 IOFile::GetSize()
+{
+ if (IsOpen())
+ return FileUtil::GetSize(m_file);
+ else
+ return 0;
+}
+
+bool IOFile::Seek(s64 off, int origin)
+{
+ if (!IsOpen() || 0 != fseeko(m_file, off, origin))
+ m_good = false;
+
+ return m_good;
+}
+
+u64 IOFile::Tell()
+{
+ if (IsOpen())
+ return ftello(m_file);
+ else
+ return -1;
+}
+
+bool IOFile::Flush()
+{
+ if (!IsOpen() || 0 != std::fflush(m_file))
+ m_good = false;
+
+ return m_good;
+}
+
+bool IOFile::Resize(u64 size)
+{
+ if (!IsOpen() || 0 !=
+#ifdef _WIN32
+ // ector: _chsize sucks, not 64-bit safe
+ // F|RES: changed to _chsize_s. i think it is 64-bit safe
+ _chsize_s(_fileno(m_file), size)
+#else
+ // TODO: handle 64bit and growing
+ ftruncate(fileno(m_file), size)
+#endif
+ )
+ m_good = false;
+
+ return m_good;
+}
+
+} // namespace
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <fstream>
+#include <functional>
+#include <cstddef>
+#include <cstdio>
+#include <string>
+#include <vector>
+
+#include "citraimport\common/common_types.h"
+
+// User directory indices for GetUserPath
+enum {
+ D_USER_IDX,
+ D_ROOT_IDX,
+ D_CONFIG_IDX,
+ D_GAMECONFIG_IDX,
+ D_MAPS_IDX,
+ D_CACHE_IDX,
+ D_SHADERCACHE_IDX,
+ D_SHADERS_IDX,
+ D_STATESAVES_IDX,
+ D_SCREENSHOTS_IDX,
+ D_SDMC_IDX,
+ D_NAND_IDX,
+ D_SYSDATA_IDX,
+ D_HIRESTEXTURES_IDX,
+ D_DUMP_IDX,
+ D_DUMPFRAMES_IDX,
+ D_DUMPAUDIO_IDX,
+ D_DUMPTEXTURES_IDX,
+ D_DUMPDSP_IDX,
+ D_LOGS_IDX,
+ D_SYSCONF_IDX,
+ F_EMUCONFIG_IDX,
+ F_DEBUGGERCONFIG_IDX,
+ F_LOGGERCONFIG_IDX,
+ F_MAINLOG_IDX,
+ F_RAMDUMP_IDX,
+ F_ARAMDUMP_IDX,
+ F_SYSCONF_IDX,
+ NUM_PATH_INDICES
+};
+
+namespace FileUtil
+{
+
+// FileSystem tree node/
+struct FSTEntry
+{
+ bool isDirectory;
+ u64 size; // file length or number of entries from children
+ std::string physicalName; // name on disk
+ std::string virtualName; // name in FST names table
+ std::vector<FSTEntry> children;
+};
+
+// Returns true if file filename exists
+bool Exists(const std::string &filename);
+
+// Returns true if filename is a directory
+bool IsDirectory(const std::string &filename);
+
+// Returns the size of filename (64bit)
+u64 GetSize(const std::string &filename);
+
+// Overloaded GetSize, accepts file descriptor
+u64 GetSize(const int fd);
+
+// Overloaded GetSize, accepts FILE*
+u64 GetSize(FILE *f);
+
+// Returns true if successful, or path already exists.
+bool CreateDir(const std::string &filename);
+
+// Creates the full path of fullPath returns true on success
+bool CreateFullPath(const std::string &fullPath);
+
+// Deletes a given filename, return true on success
+// Doesn't supports deleting a directory
+bool Delete(const std::string &filename);
+
+// Deletes a directory filename, returns true on success
+bool DeleteDir(const std::string &filename);
+
+// renames file srcFilename to destFilename, returns true on success
+bool Rename(const std::string &srcFilename, const std::string &destFilename);
+
+// copies file srcFilename to destFilename, returns true on success
+bool Copy(const std::string &srcFilename, const std::string &destFilename);
+
+// creates an empty file filename, returns true on success
+bool CreateEmptyFile(const std::string &filename);
+
+/**
+ * Scans the directory tree, calling the callback for each file/directory found.
+ * The callback must return the number of files and directories which the provided path contains.
+ * If the callback's return value is -1, the callback loop is broken immediately.
+ * If the callback's return value is otherwise negative, the callback loop is broken immediately
+ * and the callback's return value is returned from this function (to allow for error handling).
+ * @param directory the parent directory to start scanning from
+ * @param callback The callback which will be called for each file/directory. It is called
+ * with the arguments (const std::string& directory, const std::string& virtual_name).
+ * The `directory `parameter is the path to the directory which contains the file/directory.
+ * The `virtual_name` parameter is the incomplete file path, without any directory info.
+ * @return the total number of files/directories found
+ */
+int ScanDirectoryTreeAndCallback(const std::string &directory, std::function<int(const std::string&, const std::string&)> callback);
+
+/**
+ * Scans the directory tree, storing the results.
+ * @param directory the parent directory to start scanning from
+ * @param parent_entry FSTEntry where the filesystem tree results will be stored.
+ * @return the total number of files/directories found
+ */
+int ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry);
+
+// deletes the given directory and anything under it. Returns true on success.
+bool DeleteDirRecursively(const std::string &directory);
+
+// Returns the current directory
+std::string GetCurrentDir();
+
+// Create directory and copy contents (does not overwrite existing files)
+void CopyDir(const std::string &source_path, const std::string &dest_path);
+
+// Set the current directory to given directory
+bool SetCurrentDir(const std::string &directory);
+
+// Returns a pointer to a string with a Citra data dir in the user's home
+// directory. To be used in "multi-user" mode (that is, installed).
+const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath="");
+
+// Returns the path to where the sys file are
+std::string GetSysDirectory();
+
+#ifdef __APPLE__
+std::string GetBundleDirectory();
+#endif
+
+#ifdef _WIN32
+std::string &GetExeDirectory();
+#endif
+
+size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename);
+size_t ReadFileToString(bool text_file, const char *filename, std::string &str);
+
+/**
+ * Splits the filename into 8.3 format
+ * Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename
+ * @param filename The normal filename to use
+ * @param short_name A 9-char array in which the short name will be written
+ * @param extension A 4-char array in which the extension will be written
+ */
+void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
+ std::array<char, 4>& extension);
+
+// simple wrapper for cstdlib file functions to
+// hopefully will make error checking easier
+// and make forgetting an fclose() harder
+class IOFile : public NonCopyable
+{
+public:
+ IOFile();
+ IOFile(std::FILE* file);
+ IOFile(const std::string& filename, const char openmode[]);
+
+ ~IOFile();
+
+ IOFile(IOFile&& other);
+ IOFile& operator=(IOFile&& other);
+
+ void Swap(IOFile& other);
+
+ bool Open(const std::string& filename, const char openmode[]);
+ bool Close();
+
+ template <typename T>
+ size_t ReadArray(T* data, size_t length)
+ {
+ if (!IsOpen()) {
+ m_good = false;
+ return -1;
+ }
+
+ size_t items_read = std::fread(data, sizeof(T), length, m_file);
+ if (items_read != length)
+ m_good = false;
+
+ return items_read;
+ }
+
+ template <typename T>
+ size_t WriteArray(const T* data, size_t length)
+ {
+ static_assert(std::is_standard_layout<T>::value, "Given array does not consist of standard layout objects");
+ // TODO: gcc 4.8 does not support is_trivially_copyable, but we really should check for it here.
+ //static_assert(std::is_trivially_copyable<T>::value, "Given array does not consist of trivially copyable objects");
+
+ if (!IsOpen()) {
+ m_good = false;
+ return -1;
+ }
+
+ size_t items_written = std::fwrite(data, sizeof(T), length, m_file);
+ if (items_written != length)
+ m_good = false;
+
+ return items_written;
+ }
+
+ size_t ReadBytes(void* data, size_t length)
+ {
+ return ReadArray(reinterpret_cast<char*>(data), length);
+ }
+
+ size_t WriteBytes(const void* data, size_t length)
+ {
+ return WriteArray(reinterpret_cast<const char*>(data), length);
+ }
+
+ template<typename T>
+ size_t WriteObject(const T& object) {
+ static_assert(!std::is_pointer<T>::value, "Given object is a pointer");
+ return WriteArray(&object, 1);
+ }
+
+ bool IsOpen() { return nullptr != m_file; }
+
+ // m_good is set to false when a read, write or other function fails
+ bool IsGood() { return m_good; }
+ operator void*() { return m_good ? m_file : nullptr; }
+
+ std::FILE* ReleaseHandle();
+
+ std::FILE* GetHandle() { return m_file; }
+
+ void SetHandle(std::FILE* file);
+
+ bool Seek(s64 off, int origin);
+ u64 Tell();
+ u64 GetSize();
+ bool Resize(u64 size);
+ bool Flush();
+
+ // clear error state
+ void Clear() { m_good = true; std::clearerr(m_file); }
+
+ std::FILE* m_file;
+ bool m_good;
+private:
+ IOFile(IOFile&);
+ IOFile& operator=(IOFile& other);
+};
+
+} // namespace
+
+// To deal with Windows being dumb at unicode:
+template <typename T>
+void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode)
+{
+#ifdef _MSC_VER
+ fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode);
+#else
+ fstream.open(filename.c_str(), openmode);
+#endif
+}
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "citraimport\common/common_types.h"
+
+namespace Common {
+
+void MurmurHash3_128(const void* key, int len, u32 seed, void* out);
+
+/**
+ * Computes a 64-bit hash over the specified block of data
+ * @param data Block of data to compute hash over
+ * @param len Length of data (in bytes) to compute hash over
+ * @returns 64-bit hash value that was computed over the data block
+ */
+static inline u64 ComputeHash64(const void* data, int len) {
+ u64 res[2];
+ MurmurHash3_128(data, len, 0, res);
+ return res[0];
+}
+
+} // namespace Common
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/hid/hid.h"
+
+namespace KeyMap {
+
+/**
+ * Represents a key for a specific host device.
+ */
+struct HostDeviceKey {
+ int key_code;
+ int device_id; ///< Uniquely identifies a host device
+
+ bool operator < (const HostDeviceKey &other) const {
+ if (device_id == other.device_id) {
+ return key_code < other.key_code;
+ }
+ return device_id < other.device_id;
+ }
+
+ bool operator == (const HostDeviceKey &other) const {
+ return device_id == other.device_id && key_code == other.key_code;
+ }
+};
+
+/**
+ * Generates a new device id, which uniquely identifies a host device within KeyMap.
+ */
+int NewDeviceId();
+
+/**
+ * Maps a device-specific key to a PadState.
+ */
+void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState);
+
+/**
+ * Gets the PadState that's mapped to the provided device-specific key.
+ */
+Service::HID::PadState GetPadKey(HostDeviceKey key);
+
+}
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include <fstream>
+
+// defined in Version.cpp
+extern const char *scm_rev_git_str;
+
+// On disk format:
+//header{
+// u32 'DCAC';
+// u32 version; // svn_rev
+// u16 sizeof(key_type);
+// u16 sizeof(value_type);
+//}
+
+//key_value_pair{
+// u32 value_size;
+// key_type key;
+// value_type[value_size] value;
+//}
+
+template <typename K, typename V>
+class LinearDiskCacheReader
+{
+public:
+ virtual void Read(const K &key, const V *value, u32 value_size) = 0;
+};
+
+// Dead simple unsorted key-value store with append functionality.
+// No random read functionality, all reading is done in OpenAndRead.
+// Keys and values can contain any characters, including \0.
+//
+// Suitable for caching generated shader bytecode between executions.
+// Not tuned for extreme performance but should be reasonably fast.
+// Does not support keys or values larger than 2GB, which should be reasonable.
+// Keys must have non-zero length; values can have zero length.
+
+// K and V are some POD type
+// K : the key type
+// V : value array type
+template <typename K, typename V>
+class LinearDiskCache
+{
+public:
+ // return number of read entries
+ u32 OpenAndRead(const char *filename, LinearDiskCacheReader<K, V> &reader)
+ {
+ using std::ios_base;
+
+ // close any currently opened file
+ Close();
+ m_num_entries = 0;
+
+ // try opening for reading/writing
+ OpenFStream(m_file, filename, ios_base::in | ios_base::out | ios_base::binary);
+
+ m_file.seekg(0, std::ios::end);
+ std::fstream::pos_type end_pos = m_file.tellg();
+ m_file.seekg(0, std::ios::beg);
+ std::fstream::pos_type start_pos = m_file.tellg();
+ std::streamoff file_size = end_pos - start_pos;
+
+ if (m_file.is_open() && ValidateHeader())
+ {
+ // good header, read some key/value pairs
+ K key;
+
+ V *value = nullptr;
+ u32 value_size;
+ u32 entry_number;
+
+ std::fstream::pos_type last_pos = m_file.tellg();
+
+ while (Read(&value_size))
+ {
+ std::streamoff next_extent = (last_pos - start_pos) + sizeof(value_size) + value_size;
+ if (next_extent > file_size)
+ break;
+
+ delete[] value;
+ value = new V[value_size];
+
+ // read key/value and pass to reader
+ if (Read(&key) &&
+ Read(value, value_size) &&
+ Read(&entry_number) &&
+ entry_number == m_num_entries+1)
+ {
+ reader.Read(key, value, value_size);
+ }
+ else
+ {
+ break;
+ }
+
+ m_num_entries++;
+ last_pos = m_file.tellg();
+ }
+ m_file.seekp(last_pos);
+ m_file.clear();
+
+ delete[] value;
+ return m_num_entries;
+ }
+
+ // failed to open file for reading or bad header
+ // close and recreate file
+ Close();
+ m_file.open(filename, ios_base::out | ios_base::trunc | ios_base::binary);
+ WriteHeader();
+ return 0;
+ }
+
+ void Sync()
+ {
+ m_file.flush();
+ }
+
+ void Close()
+ {
+ if (m_file.is_open())
+ m_file.close();
+ // clear any error flags
+ m_file.clear();
+ }
+
+ // Appends a key-value pair to the store.
+ void Append(const K &key, const V *value, u32 value_size)
+ {
+ // TODO: Should do a check that we don't already have "key"? (I think each caller does that already.)
+ Write(&value_size);
+ Write(&key);
+ Write(value, value_size);
+ m_num_entries++;
+ Write(&m_num_entries);
+ }
+
+private:
+ void WriteHeader()
+ {
+ Write(&m_header);
+ }
+
+ bool ValidateHeader()
+ {
+ char file_header[sizeof(Header)];
+
+ return (Read(file_header, sizeof(Header))
+ && !memcmp((const char*)&m_header, file_header, sizeof(Header)));
+ }
+
+ template <typename D>
+ bool Write(const D *data, u32 count = 1)
+ {
+ return m_file.write((const char*)data, count * sizeof(D)).good();
+ }
+
+ template <typename D>
+ bool Read(const D *data, u32 count = 1)
+ {
+ return m_file.read((char*)data, count * sizeof(D)).good();
+ }
+
+ struct Header
+ {
+ Header()
+ : id(*(u32*)"DCAC")
+ , key_t_size(sizeof(K))
+ , value_t_size(sizeof(V))
+ {
+ memcpy(ver, scm_rev_git_str, 40);
+ }
+
+ const u32 id;
+ const u16 key_t_size, value_t_size;
+ char ver[40];
+
+ } m_header;
+
+ std::fstream m_file;
+ u32 m_num_entries;
+};
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <array>
+#include <cstdio>
+
+#include "citraimport\common/assert.h"
+#include "citraimport\common/common_funcs.h" // snprintf compatibility define
+#include "citraimport\common/logging/backend.h"
+#include "citraimport\common/logging/filter.h"
+#include "citraimport\common/logging/log.h"
+#include "citraimport\common/logging/text_formatter.h"
+
+namespace Log {
+
+/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
+#define ALL_LOG_CLASSES() \
+ CLS(Log) \
+ CLS(Common) \
+ SUB(Common, Filesystem) \
+ SUB(Common, Memory) \
+ CLS(Core) \
+ SUB(Core, ARM11) \
+ SUB(Core, Timing) \
+ CLS(Config) \
+ CLS(Debug) \
+ SUB(Debug, Emulated) \
+ SUB(Debug, GPU) \
+ SUB(Debug, Breakpoint) \
+ CLS(Kernel) \
+ SUB(Kernel, SVC) \
+ CLS(Service) \
+ SUB(Service, SRV) \
+ SUB(Service, FS) \
+ SUB(Service, ERR) \
+ SUB(Service, APT) \
+ SUB(Service, GSP) \
+ SUB(Service, AC) \
+ SUB(Service, AM) \
+ SUB(Service, PTM) \
+ SUB(Service, LDR) \
+ SUB(Service, NIM) \
+ SUB(Service, NWM) \
+ SUB(Service, CFG) \
+ SUB(Service, DSP) \
+ SUB(Service, HID) \
+ SUB(Service, SOC) \
+ SUB(Service, Y2R) \
+ CLS(HW) \
+ SUB(HW, Memory) \
+ SUB(HW, LCD) \
+ SUB(HW, GPU) \
+ CLS(Frontend) \
+ CLS(Render) \
+ SUB(Render, Software) \
+ SUB(Render, OpenGL) \
+ CLS(Loader)
+
+// GetClassName is a macro defined by Windows.h, grrr...
+const char* GetLogClassName(Class log_class) {
+ switch (log_class) {
+#define CLS(x) case Class::x: return #x;
+#define SUB(x, y) case Class::x##_##y: return #x "." #y;
+ ALL_LOG_CLASSES()
+#undef CLS
+#undef SUB
+ case Class::Count:
+ //UNREACHABLE();
+ break;
+ }
+}
+
+const char* GetLevelName(Level log_level) {
+#define LVL(x) case Level::x: return #x
+ switch (log_level) {
+ LVL(Trace);
+ LVL(Debug);
+ LVL(Info);
+ LVL(Warning);
+ LVL(Error);
+ LVL(Critical);
+ case Level::Count:
+ //UNREACHABLE();
+ break;
+ }
+#undef LVL
+}
+
+Entry CreateEntry(Class log_class, Level log_level,
+ const char* filename, unsigned int line_nr, const char* function,
+ const char* format, va_list args) {
+ using std::chrono::steady_clock;
+ using std::chrono::duration_cast;
+
+ static steady_clock::time_point time_origin = steady_clock::now();
+
+ std::array<char, 4 * 1024> formatting_buffer;
+
+ Entry entry;
+ entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
+ entry.log_class = log_class;
+ entry.log_level = log_level;
+
+ snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function, line_nr);
+ entry.location = std::string(formatting_buffer.data());
+
+ vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
+ entry.message = std::string(formatting_buffer.data());
+
+ return std::move(entry);
+}
+
+static Filter* filter = nullptr;
+
+void SetFilter(Filter* new_filter) {
+ filter = new_filter;
+}
+
+void LogMessage(Class log_class, Level log_level,
+ const char* filename, unsigned int line_nr, const char* function,
+ const char* format, ...) {
+ if (filter != nullptr && !filter->CheckMessage(log_class, log_level))
+ return;
+
+ va_list args;
+ va_start(args, format);
+ Entry entry = CreateEntry(log_class, log_level,
+ filename, line_nr, function, format, args);
+ va_end(args);
+
+ PrintColoredMessage(entry);
+}
+
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <chrono>
+#include <cstdarg>
+#include <string>
+#include <utility>
+
+#include "citraimport\common/logging/log.h"
+
+namespace Log {
+
+class Filter;
+
+/**
+ * A log entry. Log entries are store in a structured format to permit more varied output
+ * formatting on different frontends, as well as facilitating filtering and aggregation.
+ */
+struct Entry {
+ std::chrono::microseconds timestamp;
+ Class log_class;
+ Level log_level;
+ std::string location;
+ std::string message;
+
+ Entry() = default;
+
+ // TODO(yuriks) Use defaulted move constructors once MSVC supports them
+#define MOVE(member) member(std::move(o.member))
+ Entry(Entry&& o)
+ : MOVE(timestamp), MOVE(log_class), MOVE(log_level),
+ MOVE(location), MOVE(message)
+ {}
+#undef MOVE
+
+ Entry& operator=(const Entry&& o) {
+#define MOVE(member) member = std::move(o.member)
+ MOVE(timestamp);
+ MOVE(log_class);
+ MOVE(log_level);
+ MOVE(location);
+ MOVE(message);
+#undef MOVE
+ return *this;
+ }
+};
+
+/**
+ * Returns the name of the passed log class as a C-string. Subclasses are separated by periods
+ * instead of underscores as in the enumeration.
+ */
+const char* GetLogClassName(Class log_class);
+
+/**
+ * Returns the name of the passed log level as a C-string.
+ */
+const char* GetLevelName(Level log_level);
+
+/// Creates a log entry by formatting the given source location, and message.
+Entry CreateEntry(Class log_class, Level log_level,
+ const char* filename, unsigned int line_nr, const char* function,
+ const char* format, va_list args);
+
+void SetFilter(Filter* filter);
+
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include "citraimport\common/logging/filter.h"
+#include "citraimport\common/logging/backend.h"
+#include "citraimport\common/string_util.h"
+
+namespace Log {
+
+Filter::Filter(Level default_level) {
+ ResetAll(default_level);
+}
+
+void Filter::ResetAll(Level level) {
+ class_levels.fill(level);
+}
+
+void Filter::SetClassLevel(Class log_class, Level level) {
+ class_levels[static_cast<size_t>(log_class)] = level;
+}
+
+void Filter::ParseFilterString(const std::string& filter_str) {
+ auto clause_begin = filter_str.cbegin();
+ while (clause_begin != filter_str.cend()) {
+ auto clause_end = std::find(clause_begin, filter_str.cend(), ' ');
+
+ // If clause isn't empty
+ if (clause_end != clause_begin) {
+ ParseFilterRule(clause_begin, clause_end);
+ }
+
+ if (clause_end != filter_str.cend()) {
+ // Skip over the whitespace
+ ++clause_end;
+ }
+ clause_begin = clause_end;
+ }
+}
+
+template <typename It>
+static Level GetLevelByName(const It begin, const It end) {
+ for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) {
+ const char* level_name = GetLevelName(static_cast<Level>(i));
+ if (Common::ComparePartialString(begin, end, level_name)) {
+ return static_cast<Level>(i);
+ }
+ }
+ return Level::Count;
+}
+
+template <typename It>
+static Class GetClassByName(const It begin, const It end) {
+ for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) {
+ const char* level_name = GetLogClassName(static_cast<Class>(i));
+ if (Common::ComparePartialString(begin, end, level_name)) {
+ return static_cast<Class>(i);
+ }
+ }
+ return Class::Count;
+}
+
+bool Filter::ParseFilterRule(const std::string::const_iterator begin,
+ const std::string::const_iterator end) {
+ auto level_separator = std::find(begin, end, ':');
+ if (level_separator == end) {
+ LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
+ std::string(begin, end).c_str());
+ return false;
+ }
+
+ const Level level = GetLevelByName(level_separator + 1, end);
+ if (level == Level::Count) {
+ LOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
+ return false;
+ }
+
+ if (Common::ComparePartialString(begin, level_separator, "*")) {
+ ResetAll(level);
+ return true;
+ }
+
+ const Class log_class = GetClassByName(begin, level_separator);
+ if (log_class == Class::Count) {
+ LOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
+ return false;
+ }
+
+ SetClassLevel(log_class, level);
+ return true;
+}
+
+bool Filter::CheckMessage(Class log_class, Level level) const {
+ return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
+}
+
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <cstddef>
+#include <string>
+
+#include "citraimport\common/logging/log.h"
+
+namespace Log {
+
+/**
+ * Implements a log message filter which allows different log classes to have different minimum
+ * severity levels. The filter can be changed at runtime and can be parsed from a string to allow
+ * editing via the interface or loading from a configuration file.
+ */
+class Filter {
+public:
+ /// Initializes the filter with all classes having `default_level` as the minimum level.
+ Filter(Level default_level);
+
+ /// Resets the filter so that all classes have `level` as the minimum displayed level.
+ void ResetAll(Level level);
+ /// Sets the minimum level of `log_class` (and not of its subclasses) to `level`.
+ void SetClassLevel(Class log_class, Level level);
+
+ /**
+ * Parses a filter string and applies it to this filter.
+ *
+ * A filter string consists of a space-separated list of filter rules, each of the format
+ * `<class>:<level>`. `<class>` is a log class name, with subclasses separated using periods.
+ * `*` is allowed as a class name and will reset all filters to the specified level. `<level>`
+ * a severity level name which will be set as the minimum logging level of the matched classes.
+ * Rules are applied left to right, with each rule overriding previous ones in the sequence.
+ *
+ * A few examples of filter rules:
+ * - `*:Info` -- Resets the level of all classes to Info.
+ * - `Service:Info` -- Sets the level of Service to Info.
+ * - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace.
+ */
+ void ParseFilterString(const std::string& filter_str);
+ bool ParseFilterRule(const std::string::const_iterator start, const std::string::const_iterator end);
+
+ /// Matches class/level combination against the filter, returning true if it passed.
+ bool CheckMessage(Class log_class, Level level) const;
+
+private:
+ std::array<Level, (size_t)Class::Count> class_levels;
+};
+
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "citraimport\common/common_types.h"
+
+namespace Log {
+
+/// Specifies the severity or level of detail of the log message.
+enum class Level : u8 {
+ Trace, ///< Extremely detailed and repetitive debugging information that is likely to
+ /// pollute logs.
+ Debug, ///< Less detailed debugging information.
+ Info, ///< Status information from important points during execution.
+ Warning, ///< Minor or potential problems found during execution of a task.
+ Error, ///< Major problems found during execution of a task that prevent it from being
+ /// completed.
+ Critical, ///< Major problems during execution that threathen the stability of the entire
+ /// application.
+
+ Count ///< Total number of logging levels
+};
+
+typedef u8 ClassType;
+
+/**
+ * Specifies the sub-system that generated the log message.
+ *
+ * @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in backend.cpp.
+ */
+enum class Class : ClassType {
+ Log, ///< Messages about the log system itself
+ Common, ///< Library routines
+ Common_Filesystem, ///< Filesystem interface library
+ Common_Memory, ///< Memory mapping and management functions
+ Core, ///< LLE emulation core
+ Core_ARM11, ///< ARM11 CPU core
+ Core_Timing, ///< CoreTiming functions
+ Config, ///< Emulator configuration (including commandline)
+ Debug, ///< Debugging tools
+ Debug_Emulated, ///< Debug messages from the emulated programs
+ Debug_GPU, ///< GPU debugging tools
+ Debug_Breakpoint, ///< Logging breakpoints and watchpoints
+ Kernel, ///< The HLE implementation of the CTR kernel
+ Kernel_SVC, ///< Kernel system calls
+ Service, ///< HLE implementation of system services. Each major service
+ /// should have its own subclass.
+ Service_SRV, ///< The SRV (Service Directory) implementation
+ Service_FS, ///< The FS (Filesystem) service implementation
+ Service_ERR, ///< The ERR (Error) port implementation
+ Service_APT, ///< The APT (Applets) service
+ Service_GSP, ///< The GSP (GPU control) service
+ Service_AC, ///< The AC (WiFi status) service
+ Service_AM, ///< The AM (Application manager) service
+ Service_PTM, ///< The PTM (Power status & misc.) service
+ Service_LDR, ///< The LDR (3ds dll loader) service
+ Service_NIM, ///< The NIM (Network interface manager) service
+ Service_NWM, ///< The NWM (Network wlan manager) service
+ Service_CFG, ///< The CFG (Configuration) service
+ Service_DSP, ///< The DSP (DSP control) service
+ Service_HID, ///< The HID (Human interface device) service
+ Service_SOC, ///< The SOC (Socket) service
+ Service_Y2R, ///< The Y2R (YUV to RGB conversion) service
+ HW, ///< Low-level hardware emulation
+ HW_Memory, ///< Memory-map and address translation
+ HW_LCD, ///< LCD register emulation
+ HW_GPU, ///< GPU control emulation
+ Frontend, ///< Emulator UI
+ Render, ///< Emulator video output and hardware acceleration
+ Render_Software, ///< Software renderer backend
+ Render_OpenGL, ///< OpenGL backend
+ Loader, ///< ROM loader
+
+ Count ///< Total number of logging classes
+};
+
+/// Logs a message to the global logger.
+void LogMessage(Class log_class, Level log_level,
+ const char* filename, unsigned int line_nr, const char* function,
+#ifdef _MSC_VER
+ _Printf_format_string_
+#endif
+ const char* format, ...)
+#ifdef __GNUC__
+ __attribute__((format(printf, 6, 7)))
+#endif
+ ;
+
+} // namespace Log
+
+#define LOG_GENERIC(log_class, log_level, ...) \
+ ::Log::LogMessage(log_class, log_level, __FILE__, __LINE__, __func__, __VA_ARGS__)
+
+/*#ifdef _DEBUG
+#define LOG_TRACE( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Trace, __VA_ARGS__)
+#else*/
+#define LOG_TRACE( log_class, ...) (void(0))
+//#endif
+
+#define LOG_DEBUG( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Debug, __VA_ARGS__)
+#define LOG_INFO( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Info, __VA_ARGS__)
+#define LOG_WARNING( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Warning, __VA_ARGS__)
+#define LOG_ERROR( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Error, __VA_ARGS__)
+#define LOG_CRITICAL(log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Critical, __VA_ARGS__)
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <cstdio>
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <Windows.h>
+#endif
+
+#include "citraimport\common/logging/backend.h"
+#include "citraimport\common/logging/log.h"
+#include "citraimport\common/logging/text_formatter.h"
+
+#include "citraimport\common/assert.h"
+#include "citraimport\common/common_funcs.h"
+#include "citraimport\common/string_util.h"
+
+namespace Log {
+
+// TODO(bunnei): This should be moved to a generic path manipulation library
+const char* TrimSourcePath(const char* path, const char* root) {
+ const char* p = path;
+
+ while (*p != '\0') {
+ const char* next_slash = p;
+ while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
+ ++next_slash;
+ }
+
+ bool is_src = Common::ComparePartialString(p, next_slash, root);
+ p = next_slash;
+
+ if (*p != '\0') {
+ ++p;
+ }
+ if (is_src) {
+ path = p;
+ }
+ }
+ return path;
+}
+
+void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) {
+ unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
+ unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
+
+ const char* class_name = GetLogClassName(entry.log_class);
+ const char* level_name = GetLevelName(entry.log_level);
+
+ snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s",
+ time_seconds, time_fractional, class_name, level_name,
+ TrimSourcePath(entry.location.c_str()), entry.message.c_str());
+}
+
+void PrintMessage(const Entry& entry) {
+ std::array<char, 4 * 1024> format_buffer;
+ FormatLogMessage(entry, format_buffer.data(), format_buffer.size());
+ fputs(format_buffer.data(), stderr);
+ fputc('\n', stderr);
+}
+
+void PrintColoredMessage(const Entry& entry) {
+#ifdef _WIN32
+ static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
+
+ CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
+ GetConsoleScreenBufferInfo(console_handle, &original_info);
+
+ WORD color = 0;
+ switch (entry.log_level) {
+ case Level::Trace: // Grey
+ color = FOREGROUND_INTENSITY; break;
+ case Level::Debug: // Cyan
+ color = FOREGROUND_GREEN | FOREGROUND_BLUE; break;
+ case Level::Info: // Bright gray
+ color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break;
+ case Level::Warning: // Bright yellow
+ color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break;
+ case Level::Error: // Bright red
+ color = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
+ case Level::Critical: // Bright magenta
+ color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;
+ case Level::Count:
+ //UNREACHABLE();
+ break;
+ }
+
+ SetConsoleTextAttribute(console_handle, color);
+#else
+# define ESC "\x1b"
+ const char* color = "";
+ switch (entry.log_level) {
+ case Level::Trace: // Grey
+ color = ESC "[1;30m"; break;
+ case Level::Debug: // Cyan
+ color = ESC "[0;36m"; break;
+ case Level::Info: // Bright gray
+ color = ESC "[0;37m"; break;
+ case Level::Warning: // Bright yellow
+ color = ESC "[1;33m"; break;
+ case Level::Error: // Bright red
+ color = ESC "[1;31m"; break;
+ case Level::Critical: // Bright magenta
+ color = ESC "[1;35m"; break;
+ case Level::Count:
+ UNREACHABLE();
+ }
+
+ fputs(color, stderr);
+#endif
+
+ PrintMessage(entry);
+
+#ifdef _WIN32
+ SetConsoleTextAttribute(console_handle, original_info.wAttributes);
+#else
+ fputs(ESC "[0m", stderr);
+# undef ESC
+#endif
+}
+
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+
+namespace Log {
+
+struct Entry;
+
+/**
+ * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
+ * intended to be used to strip a system-specific build directory from the `__FILE__` macro,
+ * leaving only the path relative to the sources root.
+ *
+ * @param path The input file path as a null-terminated string
+ * @param root The name of the root source directory as a null-terminated string. Path up to and
+ * including the last occurence of this name will be stripped
+ * @return A pointer to the same string passed as `path`, but starting at the trimmed portion
+ */
+const char* TrimSourcePath(const char* path, const char* root = "src");
+
+/// Formats a log entry into the provided text buffer.
+void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len);
+/// Formats and prints a log entry to stderr.
+void PrintMessage(const Entry& entry);
+/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
+void PrintColoredMessage(const Entry& entry);
+
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <memory>
+
+namespace Common {
+
+template <typename T, typename... Args>
+std::unique_ptr<T> make_unique(Args&&... args) {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+} // namespace
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <cstdlib>
+#include <type_traits>
+
+namespace MathUtil
+{
+
+inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1, unsigned length1) {
+ return (std::max(start0, start1) < std::min(start0 + length0, start1 + length1));
+}
+
+template<typename T>
+inline T Clamp(const T val, const T& min, const T& max)
+{
+ return std::max(min, std::min(max, val));
+}
+
+template<class T>
+struct Rectangle
+{
+ T left;
+ T top;
+ T right;
+ T bottom;
+
+ Rectangle() {}
+
+ Rectangle(T left, T top, T right, T bottom) : left(left), top(top), right(right), bottom(bottom) {}
+
+ T GetWidth() const { return std::abs(static_cast<typename std::make_signed<T>::type>(right - left)); }
+ T GetHeight() const { return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); }
+};
+
+} // namespace MathUtil
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+
+#include "citraimport\common/logging/log.h"
+#include "citraimport\common/memory_util.h"
+
+#ifdef _WIN32
+ #include <windows.h>
+ #include <psapi.h>
+ #include "citraimport\common/common_funcs.h"
+ #include "citraimport\common/string_util.h"
+#else
+ #include <cstdlib>
+ #include <sys/mman.h>
+#endif
+
+#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
+#include <unistd.h>
+#define PAGE_MASK (getpagesize() - 1)
+#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
+#endif
+
+// This is purposely not a full wrapper for virtualalloc/mmap, but it
+// provides exactly the primitive operations that Dolphin needs.
+
+void* AllocateExecutableMemory(size_t size, bool low)
+{
+#if defined(_WIN32)
+ void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+#else
+ static char* map_hint = nullptr;
+#if defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
+ // This OS has no flag to enforce allocation below the 4 GB boundary,
+ // but if we hint that we want a low address it is very likely we will
+ // get one.
+ // An older version of this code used MAP_FIXED, but that has the side
+ // effect of discarding already mapped pages that happen to be in the
+ // requested virtual memory range (such as the emulated RAM, sometimes).
+ if (low && (!map_hint))
+ map_hint = (char*)round_page(512*1024*1024); /* 0.5 GB rounded up to the next page */
+#endif
+ void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_ANON | MAP_PRIVATE
+#if defined(ARCHITECTURE_X64) && defined(MAP_32BIT)
+ | (low ? MAP_32BIT : 0)
+#endif
+ , -1, 0);
+#endif /* defined(_WIN32) */
+
+#ifdef _WIN32
+ if (ptr == nullptr)
+ {
+#else
+ if (ptr == MAP_FAILED)
+ {
+ ptr = nullptr;
+#endif
+ LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
+ }
+#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
+ else
+ {
+ if (low)
+ {
+ map_hint += size;
+ map_hint = (char*)round_page(map_hint); /* round up to the next page */
+ }
+ }
+#endif
+
+#if EMU_ARCH_BITS == 64
+ if ((u64)ptr >= 0x80000000 && low == true)
+ LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
+#endif
+
+ return ptr;
+}
+
+void* AllocateMemoryPages(size_t size)
+{
+#ifdef _WIN32
+ void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
+#else
+ void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+
+ if (ptr == MAP_FAILED)
+ ptr = nullptr;
+#endif
+
+ if (ptr == nullptr)
+ LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
+
+ return ptr;
+}
+
+void* AllocateAlignedMemory(size_t size,size_t alignment)
+{
+#ifdef _WIN32
+ void* ptr = _aligned_malloc(size,alignment);
+#else
+ void* ptr = nullptr;
+#ifdef ANDROID
+ ptr = memalign(alignment, size);
+#else
+ if (posix_memalign(&ptr, alignment, size) != 0)
+ LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
+#endif
+#endif
+
+ if (ptr == nullptr)
+ LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
+
+ return ptr;
+}
+
+void FreeMemoryPages(void* ptr, size_t size)
+{
+ if (ptr)
+ {
+#ifdef _WIN32
+ if (!VirtualFree(ptr, 0, MEM_RELEASE))
+ LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n%s", GetLastErrorMsg());
+#else
+ munmap(ptr, size);
+#endif
+ }
+}
+
+void FreeAlignedMemory(void* ptr)
+{
+ if (ptr)
+ {
+#ifdef _WIN32
+ _aligned_free(ptr);
+#else
+ free(ptr);
+#endif
+ }
+}
+
+void WriteProtectMemory(void* ptr, size_t size, bool allowExecute)
+{
+#ifdef _WIN32
+ DWORD oldValue;
+ if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
+ LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n%s", GetLastErrorMsg());
+#else
+ mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
+#endif
+}
+
+void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute)
+{
+#ifdef _WIN32
+ DWORD oldValue;
+ if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue))
+ LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n%s", GetLastErrorMsg());
+#else
+ mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
+#endif
+}
+
+std::string MemUsage()
+{
+#ifdef _WIN32
+#pragma comment(lib, "psapi")
+ DWORD processID = GetCurrentProcessId();
+ HANDLE hProcess;
+ PROCESS_MEMORY_COUNTERS pmc;
+ std::string Ret;
+
+ // Print information about the memory usage of the process.
+
+ hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
+ if (nullptr == hProcess) return "MemUsage Error";
+
+ if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
+ Ret = Common::StringFromFormat("%s K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str());
+
+ CloseHandle(hProcess);
+ return Ret;
+#else
+ return "";
+#endif
+}
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <string>
+
+void* AllocateExecutableMemory(size_t size, bool low = true);
+void* AllocateMemoryPages(size_t size);
+void FreeMemoryPages(void* ptr, size_t size);
+void* AllocateAlignedMemory(size_t size,size_t alignment);
+void FreeAlignedMemory(void* ptr);
+void WriteProtectMemory(void* ptr, size_t size, bool executable = false);
+void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false);
+std::string MemUsage();
+
+inline int GetPageSize() { return 4096; }
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+// Includes the MicroProfile implementation in this file for compilation
+#define MICROPROFILE_IMPL 1
+#include "citraimport\common/microprofile.h"
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+// Customized Citra settings.
+// This file wraps the MicroProfile header so that these are consistent everywhere.
+#define MICROPROFILE_WEBSERVER 0
+#define MICROPROFILE_GPU_TIMERS 0 // TODO: Implement timer queries when we upgrade to OpenGL 3.3
+#define MICROPROFILE_CONTEXT_SWITCH_TRACE 0
+#define MICROPROFILE_PER_THREAD_BUFFER_SIZE (2048<<12) // 8 MB
+
+#ifdef _WIN32
+// This isn't defined by the standard library in MSVC2015
+typedef void* HANDLE;
+#endif
+
+//#include <microprofile.h>
+
+#define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0)
+
+// On OS X, some Mach header included by MicroProfile defines these as macros, conflicting with
+// identifiers we use.
+#ifdef PAGE_SIZE
+#undef PAGE_SIZE
+#endif
+#ifdef PAGE_MASK
+#undef PAGE_MASK
+#endif
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/microprofile.h"
+
+// Customized Citra settings.
+// This file wraps the MicroProfile header so that these are consistent everywhere.
+#define MICROPROFILE_TEXT_WIDTH 6
+#define MICROPROFILE_TEXT_HEIGHT 12
+#define MICROPROFILE_HELP_ALT "Right-Click"
+#define MICROPROFILE_HELP_MOD "Ctrl"
+
+#include <microprofileui.h>
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstddef>
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <cerrno>
+#include <cstring>
+#endif
+
+// Neither Android nor OS X support TLS
+#if defined(__APPLE__) || (ANDROID && __clang__)
+#define __thread
+#endif
+
+// Generic function to get last error message.
+// Call directly after the command or use the error num.
+// This function might change the error code.
+const char* GetLastErrorMsg()
+{
+ static const size_t buff_size = 255;
+
+#ifdef _WIN32
+ static __declspec(thread) char err_str[buff_size] = {};
+
+ FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ err_str, buff_size, nullptr);
+#else
+ static __thread char err_str[buff_size] = {};
+
+ // Thread safe (XSI-compliant)
+ strerror_r(errno, err_str, buff_size);
+#endif
+
+ return err_str;
+}
--- /dev/null
+/**
+ * Copyright (C) 2005-2012 Gekko Emulator
+ *
+ * @file platform.h
+ * @author ShizZy <shizzy247@gmail.com>
+ * @date 2012-02-11
+ * @brief Platform detection macros for portable compilation
+ *
+ * @section LICENSE
+ * 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 of
+ * the License, 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 at
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * Official project repository can be found at:
+ * http://code.google.com/p/gekko-gc-emu/
+ */
+
+#pragma once
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Platform detection
+
+#if defined(ARCHITECTURE_x86_64) || defined(__aarch64__)
+ #define EMU_ARCH_BITS 64
+#elif defined(__i386) || defined(_M_IX86) || defined(__arm__) || defined(_M_ARM)
+ #define EMU_ARCH_BITS 32
+#endif
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#define NOMINMAX
+
+#include <algorithm>
+#include <cstddef>
+#include <vector>
+
+#include "citraimport\common/assert.h"
+#include "citraimport\common/profiler.h"
+#include "citraimport\common/profiler_reporting.h"
+#include "citraimport\common/synchronized_wrapper.h"
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013.
+ #define WIN32_LEAN_AND_MEAN
+ #include <Windows.h> // For QueryPerformanceCounter/Frequency
+#endif
+
+namespace Common {
+namespace Profiling {
+
+#if ENABLE_PROFILING
+thread_local Timer* Timer::current_timer = nullptr;
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013
+QPCClock::time_point QPCClock::now() {
+ static LARGE_INTEGER freq;
+ // Use this dummy local static to ensure this gets initialized once.
+ static BOOL dummy = QueryPerformanceFrequency(&freq);
+
+ LARGE_INTEGER ticks;
+ QueryPerformanceCounter(&ticks);
+
+ // This is prone to overflow when multiplying, which is why I'm using micro instead of nano. The
+ // correct way to approach this would be to just return ticks as a time_point and then subtract
+ // and do this conversion when creating a duration from two time_points, however, as far as I
+ // could tell the C++ requirements for these types are incompatible with this approach.
+ return time_point(duration(ticks.QuadPart * std::micro::den / freq.QuadPart));
+}
+#endif
+
+TimingCategory::TimingCategory(const char* name, TimingCategory* parent)
+ : accumulated_duration(0) {
+
+ ProfilingManager& manager = GetProfilingManager();
+ category_id = manager.RegisterTimingCategory(this, name);
+ if (parent != nullptr)
+ manager.SetTimingCategoryParent(category_id, parent->category_id);
+}
+
+ProfilingManager::ProfilingManager()
+ : last_frame_end(Clock::now()), this_frame_start(Clock::now()) {
+}
+
+unsigned int ProfilingManager::RegisterTimingCategory(TimingCategory* category, const char* name) {
+ TimingCategoryInfo info;
+ info.category = category;
+ info.name = name;
+ info.parent = TimingCategoryInfo::NO_PARENT;
+
+ unsigned int id = (unsigned int)timing_categories.size();
+ timing_categories.push_back(std::move(info));
+
+ return id;
+}
+
+void ProfilingManager::SetTimingCategoryParent(unsigned int category, unsigned int parent) {
+ //ASSERT(category < timing_categories.size());
+ //ASSERT(parent < timing_categories.size());
+
+ timing_categories[category].parent = parent;
+}
+
+void ProfilingManager::BeginFrame() {
+ this_frame_start = Clock::now();
+}
+
+void ProfilingManager::FinishFrame() {
+ Clock::time_point now = Clock::now();
+
+ results.interframe_time = now - last_frame_end;
+ results.frame_time = now - this_frame_start;
+
+ results.time_per_category.resize(timing_categories.size());
+ for (size_t i = 0; i < timing_categories.size(); ++i) {
+ results.time_per_category[i] = timing_categories[i].category->GetAccumulatedTime();
+ }
+
+ last_frame_end = now;
+}
+
+TimingResultsAggregator::TimingResultsAggregator(size_t window_size)
+ : max_window_size(window_size), window_size(0) {
+ interframe_times.resize(window_size, Duration::zero());
+ frame_times.resize(window_size, Duration::zero());
+}
+
+void TimingResultsAggregator::Clear() {
+ window_size = cursor = 0;
+}
+
+void TimingResultsAggregator::SetNumberOfCategories(size_t n) {
+ size_t old_size = times_per_category.size();
+ if (n == old_size)
+ return;
+
+ times_per_category.resize(n);
+
+ for (size_t i = old_size; i < n; ++i) {
+ times_per_category[i].resize(max_window_size, Duration::zero());
+ }
+}
+
+void TimingResultsAggregator::AddFrame(const ProfilingFrameResult& frame_result) {
+ SetNumberOfCategories(frame_result.time_per_category.size());
+
+ interframe_times[cursor] = frame_result.interframe_time;
+ frame_times[cursor] = frame_result.frame_time;
+ for (size_t i = 0; i < frame_result.time_per_category.size(); ++i) {
+ times_per_category[i][cursor] = frame_result.time_per_category[i];
+ }
+
+ ++cursor;
+ if (cursor == max_window_size)
+ cursor = 0;
+ if (window_size < max_window_size)
+ ++window_size;
+}
+
+static AggregatedDuration AggregateField(const std::vector<Duration>& v, size_t len) {
+ AggregatedDuration result;
+ result.avg = Duration::zero();
+ result.min = result.max = (len == 0 ? Duration::zero() : v[0]);
+
+ for (size_t i = 0; i < len; ++i) {
+ Duration value = v[i];
+ result.avg += value;
+ result.min = std::min(result.min, value);
+ result.max = std::max(result.max, value);
+ }
+ if (len != 0)
+ result.avg /= len;
+
+ return result;
+}
+
+static float tof(Common::Profiling::Duration dur) {
+ using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>;
+ return std::chrono::duration_cast<FloatMs>(dur).count();
+}
+
+AggregatedFrameResult TimingResultsAggregator::GetAggregatedResults() const {
+ AggregatedFrameResult result;
+
+ result.interframe_time = AggregateField(interframe_times, window_size);
+ result.frame_time = AggregateField(frame_times, window_size);
+
+ if (result.interframe_time.avg != Duration::zero()) {
+ result.fps = 1000.0f / tof(result.interframe_time.avg);
+ } else {
+ result.fps = 0.0f;
+ }
+
+ result.time_per_category.resize(times_per_category.size());
+ for (size_t i = 0; i < times_per_category.size(); ++i) {
+ result.time_per_category[i] = AggregateField(times_per_category[i], window_size);
+ }
+
+ return result;
+}
+
+ProfilingManager& GetProfilingManager() {
+ // Takes advantage of "magic" static initialization for race-free initialization.
+ static ProfilingManager manager;
+ return manager;
+}
+
+SynchronizedRef<TimingResultsAggregator> GetTimingResultsAggregator() {
+ static SynchronizedWrapper<TimingResultsAggregator> aggregator(30);
+ return SynchronizedRef<TimingResultsAggregator>(aggregator);
+}
+
+} // namespace Profiling
+} // namespace Common
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <chrono>
+
+#include "citraimport\common/assert.h"
+#include "citraimport\common/thread.h"
+
+namespace Common {
+namespace Profiling {
+
+// If this is defined to 0, it turns all Timers into no-ops.
+#ifndef ENABLE_PROFILING
+#define ENABLE_PROFILING 1
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013
+// MSVC up to 2013 doesn't use QueryPerformanceCounter for high_resolution_clock, so it has bad
+// precision. We manually implement a clock based on QPC to get good results.
+
+struct QPCClock {
+ using duration = std::chrono::microseconds;
+ using time_point = std::chrono::time_point<QPCClock>;
+ using rep = duration::rep;
+ using period = duration::period;
+ static const bool is_steady = false;
+
+ static time_point now();
+};
+
+using Clock = QPCClock;
+#else
+using Clock = std::chrono::high_resolution_clock;
+#endif
+
+using Duration = Clock::duration;
+
+/**
+ * Represents a timing category that measured time can be accounted towards. Should be declared as a
+ * global variable and passed to Timers.
+ */
+class TimingCategory final {
+public:
+ TimingCategory(const char* name, TimingCategory* parent = nullptr);
+
+ unsigned int GetCategoryId() const {
+ return category_id;
+ }
+
+ /// Adds some time to this category. Can safely be called from multiple threads at the same time.
+ void AddTime(Duration amount) {
+ std::atomic_fetch_add_explicit(
+ &accumulated_duration, amount.count(),
+ std::memory_order_relaxed);
+ }
+
+ /**
+ * Atomically retrieves the accumulated measured time for this category and resets the counter
+ * to zero. Can be safely called concurrently with AddTime.
+ */
+ Duration GetAccumulatedTime() {
+ return Duration(std::atomic_exchange_explicit(
+ &accumulated_duration, (Duration::rep)0,
+ std::memory_order_relaxed));
+ }
+
+private:
+ unsigned int category_id;
+ std::atomic<Duration::rep> accumulated_duration;
+};
+
+/**
+ * Measures time elapsed between a call to Start and a call to Stop and attributes it to the given
+ * TimingCategory. Start/Stop can be called multiple times on the same timer, but each call must be
+ * appropriately paired.
+ *
+ * When a Timer is started, it automatically pauses a previously running timer on the same thread,
+ * which is resumed when it is stopped. As such, no special action needs to be taken to avoid
+ * double-accounting of time on two categories.
+ */
+class Timer {
+public:
+ Timer(TimingCategory& category) : category(category) {
+ }
+
+ void Start() {
+#if ENABLE_PROFILING
+ ASSERT(!running);
+ previous_timer = current_timer;
+ current_timer = this;
+ if (previous_timer != nullptr)
+ previous_timer->StopTiming();
+
+ StartTiming();
+#endif
+ }
+
+ void Stop() {
+#if ENABLE_PROFILING
+ ASSERT(running);
+ StopTiming();
+
+ if (previous_timer != nullptr)
+ previous_timer->StartTiming();
+ current_timer = previous_timer;
+#endif
+ }
+
+private:
+#if ENABLE_PROFILING
+ void StartTiming() {
+ start = Clock::now();
+ running = true;
+ }
+
+ void StopTiming() {
+ auto duration = Clock::now() - start;
+ running = false;
+ category.AddTime(std::chrono::duration_cast<Duration>(duration));
+ }
+
+ Clock::time_point start;
+ bool running = false;
+
+ Timer* previous_timer;
+ static thread_local Timer* current_timer;
+#endif
+
+ TimingCategory& category;
+};
+
+/**
+ * A Timer that automatically starts timing when created and stops at the end of the scope. Should
+ * be used in the majority of cases.
+ */
+class ScopeTimer : public Timer {
+public:
+ ScopeTimer(TimingCategory& category) : Timer(category) {
+ Start();
+ }
+
+ ~ScopeTimer() {
+ Stop();
+ }
+};
+
+} // namespace Profiling
+} // namespace Common
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <vector>
+
+#include "citraimport\common/profiler.h"
+#include "citraimport\common/synchronized_wrapper.h"
+
+namespace Common {
+namespace Profiling {
+
+struct TimingCategoryInfo {
+ static const unsigned int NO_PARENT = -1;
+
+ TimingCategory* category;
+ const char* name;
+ unsigned int parent;
+};
+
+struct ProfilingFrameResult {
+ /// Time since the last delivered frame
+ Duration interframe_time;
+
+ /// Time spent processing a frame, excluding VSync
+ Duration frame_time;
+
+ /// Total amount of time spent inside each category in this frame. Indexed by the category id
+ std::vector<Duration> time_per_category;
+};
+
+class ProfilingManager final {
+public:
+ ProfilingManager();
+
+ unsigned int RegisterTimingCategory(TimingCategory* category, const char* name);
+ void SetTimingCategoryParent(unsigned int category, unsigned int parent);
+
+ const std::vector<TimingCategoryInfo>& GetTimingCategoriesInfo() const {
+ return timing_categories;
+ }
+
+ /// This should be called after swapping screen buffers.
+ void BeginFrame();
+ /// This should be called before swapping screen buffers.
+ void FinishFrame();
+
+ /// Get the timing results from the previous frame. This is updated when you call FinishFrame().
+ const ProfilingFrameResult& GetPreviousFrameResults() const {
+ return results;
+ }
+
+private:
+ std::vector<TimingCategoryInfo> timing_categories;
+ Clock::time_point last_frame_end;
+ Clock::time_point this_frame_start;
+
+ ProfilingFrameResult results;
+};
+
+struct AggregatedDuration {
+ Duration avg, min, max;
+};
+
+struct AggregatedFrameResult {
+ /// Time since the last delivered frame
+ AggregatedDuration interframe_time;
+
+ /// Time spent processing a frame, excluding VSync
+ AggregatedDuration frame_time;
+
+ float fps;
+
+ /// Total amount of time spent inside each category in this frame. Indexed by the category id
+ std::vector<AggregatedDuration> time_per_category;
+};
+
+class TimingResultsAggregator final {
+public:
+ TimingResultsAggregator(size_t window_size);
+
+ void Clear();
+ void SetNumberOfCategories(size_t n);
+
+ void AddFrame(const ProfilingFrameResult& frame_result);
+
+ AggregatedFrameResult GetAggregatedResults() const;
+
+ size_t max_window_size;
+ size_t window_size;
+ size_t cursor;
+
+ std::vector<Duration> interframe_times;
+ std::vector<Duration> frame_times;
+ std::vector<std::vector<Duration>> times_per_category;
+};
+
+ProfilingManager& GetProfilingManager();
+SynchronizedRef<TimingResultsAggregator> GetTimingResultsAggregator();
+
+} // namespace Profiling
+} // namespace Common
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/scm_rev.h"
+
+#define GIT_REV "@GIT_REV@"
+#define GIT_BRANCH "@GIT_BRANCH@"
+#define GIT_DESC "@GIT_DESC@"
+
+namespace Common {
+
+const char g_scm_rev[] = GIT_REV;
+const char g_scm_branch[] = GIT_BRANCH;
+const char g_scm_desc[] = GIT_DESC;
+
+} // namespace
+
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Common {
+
+extern const char g_scm_rev[];
+extern const char g_scm_branch[];
+extern const char g_scm_desc[];
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include <utility>
+
+namespace detail {
+ template <typename Func>
+ struct ScopeExitHelper {
+ explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {}
+ ~ScopeExitHelper() { func(); }
+
+ Func func;
+ };
+
+ template <typename Func>
+ ScopeExitHelper<Func> ScopeExit(Func&& func) { return ScopeExitHelper<Func>(std::move(func)); }
+}
+
+/**
+ * This macro allows you to conveniently specify a block of code that will run on scope exit. Handy
+ * for doing ad-hoc clean-up tasks in a function with multiple returns.
+ *
+ * Example usage:
+ * \code
+ * const int saved_val = g_foo;
+ * g_foo = 55;
+ * SCOPE_EXIT({ g_foo = saved_val; });
+ *
+ * if (Bar()) {
+ * return 0;
+ * } else {
+ * return 20;
+ * }
+ * \endcode
+ */
+#define SCOPE_EXIT(body) auto CONCAT2(scope_exit_helper_, __LINE__) = detail::ScopeExit([&]() body)
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cctype>
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+//#include <boost/range/algorithm/transform.hpp>
+
+#include "citraimport\common/common_paths.h"
+#include "citraimport\common/logging/log.h"
+#include "citraimport\common/string_util.h"
+
+#ifdef _MSC_VER
+ #include <Windows.h>
+ #include <codecvt>
+ #include "citraimport\common/common_funcs.h"
+#else
+ #include <iconv.h>
+#endif
+
+namespace Common {
+
+/// Make a string lowercase
+/*std::string ToLower(std::string str) {
+ boost::transform(str, str.begin(), ::tolower);
+ return str;
+}
+
+/// Make a string uppercase
+std::string ToUpper(std::string str) {
+ boost::transform(str, str.begin(), ::toupper);
+ return str;
+}*/
+
+// faster than sscanf
+bool AsciiToHex(const char* _szValue, u32& result)
+{
+ char *endptr = nullptr;
+ const u32 value = strtoul(_szValue, &endptr, 16);
+
+ if (!endptr || *endptr)
+ return false;
+
+ result = value;
+ return true;
+}
+
+/*bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args)
+{
+ int writtenCount;
+
+#ifdef _MSC_VER
+ // You would think *printf are simple, right? Iterate on each character,
+ // if it's a format specifier handle it properly, etc.
+ //
+ // Nooooo. Not according to the C standard.
+ //
+ // According to the C99 standard (7.19.6.1 "The fprintf function")
+ // The format shall be a multibyte character sequence
+ //
+ // Because some character encodings might have '%' signs in the middle of
+ // a multibyte sequence (SJIS for example only specifies that the first
+ // byte of a 2 byte sequence is "high", the second byte can be anything),
+ // printf functions have to decode the multibyte sequences and try their
+ // best to not screw up.
+ //
+ // Unfortunately, on Windows, the locale for most languages is not UTF-8
+ // as we would need. Notably, for zh_TW, Windows chooses EUC-CN as the
+ // locale, and completely fails when trying to decode UTF-8 as EUC-CN.
+ //
+ // On the other hand, the fix is simple: because we use UTF-8, no such
+ // multibyte handling is required as we can simply assume that no '%' char
+ // will be present in the middle of a multibyte sequence.
+ //
+ // This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l.
+ static locale_t c_locale = nullptr;
+ if (!c_locale)
+ c_locale = _create_locale(LC_ALL, ".1252");
+ writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args);
+#else
+ writtenCount = vsnprintf(out, outsize, format, args);
+#endif
+
+ if (writtenCount > 0 && writtenCount < outsize)
+ {
+ out[writtenCount] = '\0';
+ return true;
+ }
+ else
+ {
+ out[outsize - 1] = '\0';
+ return false;
+ }
+}
+std::string StringFromFormat(const char* format, ...)
+{
+ va_list args;
+ char *buf = nullptr;
+#ifdef _WIN32
+ int required = 0;
+
+ va_start(args, format);
+ required = _vscprintf(format, args);
+ buf = new char[required + 1];
+ CharArrayFromFormatV(buf, required + 1, format, args);
+ va_end(args);
+
+ std::string temp = buf;
+ delete[] buf;
+#else
+ va_start(args, format);
+ if (vasprintf(&buf, format, args) < 0)
+ LOG_ERROR(Common, "Unable to allocate memory for string");
+ va_end(args);
+
+ std::string temp = buf;
+ free(buf);
+#endif
+ return temp;
+}*/
+
+// For Debugging. Read out an u8 array.
+std::string ArrayToString(const u8 *data, u32 size, int line_len, bool spaces)
+{
+ std::ostringstream oss;
+ oss << std::setfill('0') << std::hex;
+
+ for (int line = 0; size; ++data, --size)
+ {
+ oss << std::setw(2) << (int)*data;
+
+ if (line_len == ++line)
+ {
+ oss << '\n';
+ line = 0;
+ }
+ else if (spaces)
+ oss << ' ';
+ }
+
+ return oss.str();
+}
+
+// Turns " hej " into "hej". Also handles tabs.
+std::string StripSpaces(const std::string &str)
+{
+ const size_t s = str.find_first_not_of(" \t\r\n");
+
+ if (str.npos != s)
+ return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1);
+ else
+ return "";
+}
+
+// "\"hello\"" is turned to "hello"
+// This one assumes that the string has already been space stripped in both
+// ends, as done by StripSpaces above, for example.
+std::string StripQuotes(const std::string& s)
+{
+ if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
+ return s.substr(1, s.size() - 2);
+ else
+ return s;
+}
+
+/*
+bool TryParse(const std::string &str, u32 *const output)
+{
+ char *endptr = nullptr;
+
+ // Reset errno to a value other than ERANGE
+ errno = 0;
+
+ unsigned long value = strtoul(str.c_str(), &endptr, 0);
+
+ if (!endptr || *endptr)
+ return false;
+
+ if (errno == ERANGE)
+ return false;
+
+#if ULONG_MAX > UINT_MAX
+ if (value >= 0x100000000ull
+ && value <= 0xFFFFFFFF00000000ull)
+ return false;
+#endif
+
+ *output = static_cast<u32>(value);
+ return true;
+}*/
+/*
+bool TryParse(const std::string &str, bool *const output)
+{
+ if ("1" == str || "true" == ToLower(str))
+ *output = true;
+ else if ("0" == str || "false" == ToLower(str))
+ *output = false;
+ else
+ return false;
+
+ return true;
+}*/
+
+std::string StringFromBool(bool value)
+{
+ return value ? "True" : "False";
+}
+
+bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension)
+{
+ if (full_path.empty())
+ return false;
+
+ size_t dir_end = full_path.find_last_of("/"
+ // windows needs the : included for something like just "C:" to be considered a directory
+#ifdef _WIN32
+ ":"
+#endif
+ );
+ if (std::string::npos == dir_end)
+ dir_end = 0;
+ else
+ dir_end += 1;
+
+ size_t fname_end = full_path.rfind('.');
+ if (fname_end < dir_end || std::string::npos == fname_end)
+ fname_end = full_path.size();
+
+ if (_pPath)
+ *_pPath = full_path.substr(0, dir_end);
+
+ if (_pFilename)
+ *_pFilename = full_path.substr(dir_end, fname_end - dir_end);
+
+ if (_pExtension)
+ *_pExtension = full_path.substr(fname_end);
+
+ return true;
+}
+
+void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename)
+{
+ _CompleteFilename = _Path;
+
+ // check for seperator
+ if (DIR_SEP_CHR != *_CompleteFilename.rbegin())
+ _CompleteFilename += DIR_SEP_CHR;
+
+ // add the filename
+ _CompleteFilename += _Filename;
+}
+
+void SplitString(const std::string& str, const char delim, std::vector<std::string>& output)
+{
+ std::istringstream iss(str);
+ output.resize(1);
+
+ while (std::getline(iss, *output.rbegin(), delim))
+ output.push_back("");
+
+ output.pop_back();
+}
+
+std::string TabsToSpaces(int tab_size, const std::string &in)
+{
+ const std::string spaces(tab_size, ' ');
+ std::string out(in);
+
+ size_t i = 0;
+ while (out.npos != (i = out.find('\t')))
+ out.replace(i, 1, spaces);
+
+ return out;
+}
+
+std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest)
+{
+ size_t pos = 0;
+
+ if (src == dest)
+ return result;
+
+ while ((pos = result.find(src, pos)) != std::string::npos)
+ {
+ result.replace(pos, src.size(), dest);
+ pos += dest.length();
+ }
+
+ return result;
+}
+
+#ifdef _MSC_VER
+
+std::string UTF16ToUTF8(const std::u16string& input)
+{
+#if _MSC_VER >= 1900
+ // Workaround for missing char16_t/char32_t instantiations in MSVC2015
+ std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert;
+ std::basic_string<__int16> tmp_buffer(input.cbegin(), input.cend());
+ return convert.to_bytes(tmp_buffer);
+#else
+ std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
+ return convert.to_bytes(input);
+#endif
+}
+
+std::u16string UTF8ToUTF16(const std::string& input)
+{
+#if _MSC_VER >= 1900
+ // Workaround for missing char16_t/char32_t instantiations in MSVC2015
+ std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert;
+ auto tmp_buffer = convert.from_bytes(input);
+ return std::u16string(tmp_buffer.cbegin(), tmp_buffer.cend());
+#else
+ std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
+ return convert.from_bytes(input);
+#endif
+}
+
+static std::string UTF16ToUTF8(const std::wstring& input)
+{
+ auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), nullptr, 0, nullptr, nullptr);
+
+ std::string output;
+ output.resize(size);
+
+ if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()), nullptr, nullptr))
+ output.clear();
+
+ return output;
+}
+
+static std::wstring CPToUTF16(u32 code_page, const std::string& input)
+{
+ auto const size = MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
+
+ std::wstring output;
+ output.resize(size);
+
+ if (size == 0 || size != MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size())))
+ output.clear();
+
+ return output;
+}
+
+std::wstring UTF8ToUTF16W(const std::string &input)
+{
+ return CPToUTF16(CP_UTF8, input);
+}
+
+std::string SHIFTJISToUTF8(const std::string& input)
+{
+ return UTF16ToUTF8(CPToUTF16(932, input));
+}
+
+std::string CP1252ToUTF8(const std::string& input)
+{
+ return UTF16ToUTF8(CPToUTF16(1252, input));
+}
+
+#else
+
+template <typename T>
+static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input)
+{
+ std::string result;
+
+ iconv_t const conv_desc = iconv_open("UTF-8", fromcode);
+ if ((iconv_t)(-1) == conv_desc)
+ {
+ LOG_ERROR(Common, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno));
+ iconv_close(conv_desc);
+ return {};
+ }
+
+ const size_t in_bytes = sizeof(T) * input.size();
+ // Multiply by 4, which is the max number of bytes to encode a codepoint
+ const size_t out_buffer_size = 4 * in_bytes;
+
+ std::string out_buffer;
+ out_buffer.resize(out_buffer_size);
+
+ auto src_buffer = &input[0];
+ size_t src_bytes = in_bytes;
+ auto dst_buffer = &out_buffer[0];
+ size_t dst_bytes = out_buffer.size();
+
+ while (0 != src_bytes)
+ {
+ size_t const iconv_result = iconv(conv_desc, (char**)(&src_buffer), &src_bytes,
+ &dst_buffer, &dst_bytes);
+
+ if (static_cast<size_t>(-1) == iconv_result)
+ {
+ if (EILSEQ == errno || EINVAL == errno)
+ {
+ // Try to skip the bad character
+ if (0 != src_bytes)
+ {
+ --src_bytes;
+ ++src_buffer;
+ }
+ }
+ else
+ {
+ LOG_ERROR(Common, "iconv failure [%s]: %s", fromcode, strerror(errno));
+ break;
+ }
+ }
+ }
+
+ out_buffer.resize(out_buffer_size - dst_bytes);
+ out_buffer.swap(result);
+
+ iconv_close(conv_desc);
+
+ return result;
+}
+
+std::u16string UTF8ToUTF16(const std::string& input)
+{
+ std::u16string result;
+
+ iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8");
+ if ((iconv_t)(-1) == conv_desc)
+ {
+ LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: %s", strerror(errno));
+ iconv_close(conv_desc);
+ return {};
+ }
+
+ const size_t in_bytes = sizeof(char) * input.size();
+ // Multiply by 4, which is the max number of bytes to encode a codepoint
+ const size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes;
+
+ std::u16string out_buffer;
+ out_buffer.resize(out_buffer_size);
+
+ char* src_buffer = const_cast<char*>(&input[0]);
+ size_t src_bytes = in_bytes;
+ char* dst_buffer = (char*)(&out_buffer[0]);
+ size_t dst_bytes = out_buffer.size();
+
+ while (0 != src_bytes)
+ {
+ size_t const iconv_result = iconv(conv_desc, &src_buffer, &src_bytes,
+ &dst_buffer, &dst_bytes);
+
+ if (static_cast<size_t>(-1) == iconv_result)
+ {
+ if (EILSEQ == errno || EINVAL == errno)
+ {
+ // Try to skip the bad character
+ if (0 != src_bytes)
+ {
+ --src_bytes;
+ ++src_buffer;
+ }
+ }
+ else
+ {
+ LOG_ERROR(Common, "iconv failure [UTF-8]: %s", strerror(errno));
+ break;
+ }
+ }
+ }
+
+ out_buffer.resize(out_buffer_size - dst_bytes);
+ out_buffer.swap(result);
+
+ iconv_close(conv_desc);
+
+ return result;
+}
+
+std::string UTF16ToUTF8(const std::u16string& input)
+{
+ return CodeToUTF8("UTF-16LE", input);
+}
+
+std::string CP1252ToUTF8(const std::string& input)
+{
+ //return CodeToUTF8("CP1252//TRANSLIT", input);
+ //return CodeToUTF8("CP1252//IGNORE", input);
+ return CodeToUTF8("CP1252", input);
+}
+
+std::string SHIFTJISToUTF8(const std::string& input)
+{
+ //return CodeToUTF8("CP932", input);
+ return CodeToUTF8("SJIS", input);
+}
+
+#endif
+
+std::string StringFromFixedZeroTerminatedBuffer(const char * buffer, size_t max_len) {
+ size_t len = 0;
+ while (len < max_len && buffer[len] != '\0')
+ ++len;
+
+ return std::string(buffer, len);
+}
+
+}
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstdarg>
+#include <cstddef>
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "citraimport\common/common_types.h"
+
+namespace Common {
+
+/// Make a string lowercase
+std::string ToLower(std::string str);
+
+/// Make a string uppercase
+std::string ToUpper(std::string str);
+
+std::string StringFromFormat(const char* format, ...);
+// Cheap!
+bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args);
+
+template<size_t Count>
+inline void CharArrayFromFormat(char (& out)[Count], const char* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ CharArrayFromFormatV(out, Count, format, args);
+ va_end(args);
+}
+
+// Good
+std::string ArrayToString(const u8 *data, u32 size, int line_len = 20, bool spaces = true);
+
+std::string StripSpaces(const std::string &s);
+std::string StripQuotes(const std::string &s);
+
+// Thousand separator. Turns 12345678 into 12,345,678
+template <typename I>
+std::string ThousandSeparate(I value, int spaces = 0)
+{
+ std::ostringstream oss;
+
+// std::locale("") seems to be broken on many platforms
+#if defined _WIN32 || (defined __linux__ && !defined __clang__)
+ oss.imbue(std::locale(""));
+#endif
+ oss << std::setw(spaces) << value;
+
+ return oss.str();
+}
+
+std::string StringFromBool(bool value);
+
+bool TryParse(const std::string &str, bool *output);
+bool TryParse(const std::string &str, u32 *output);
+
+template <typename N>
+static bool TryParse(const std::string &str, N *const output)
+{
+ std::istringstream iss(str);
+
+ N tmp = 0;
+ if (iss >> tmp)
+ {
+ *output = tmp;
+ return true;
+ }
+ else
+ return false;
+}
+
+// TODO: kill this
+bool AsciiToHex(const char* _szValue, u32& result);
+
+std::string TabsToSpaces(int tab_size, const std::string &in);
+
+void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
+
+// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe"
+bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension);
+
+void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename);
+std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest);
+
+std::string UTF16ToUTF8(const std::u16string& input);
+std::u16string UTF8ToUTF16(const std::string& input);
+
+std::string CP1252ToUTF8(const std::string& str);
+std::string SHIFTJISToUTF8(const std::string& str);
+
+#ifdef _WIN32
+
+std::wstring UTF8ToUTF16W(const std::string& str);
+
+#ifdef _UNICODE
+inline std::string TStrToUTF8(const std::wstring& str)
+{ return UTF16ToUTF8(str); }
+
+inline std::wstring UTF8ToTStr(const std::string& str)
+{ return UTF8ToUTF16W(str); }
+#else
+inline std::string TStrToUTF8(const std::string& str)
+{ return str; }
+
+inline std::string UTF8ToTStr(const std::string& str)
+{ return str; }
+#endif
+
+#endif
+
+/**
+ * Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
+ * `other` for equality.
+ */
+template <typename InIt>
+bool ComparePartialString(InIt begin, InIt end, const char* other) {
+ for (; begin != end && *other != '\0'; ++begin, ++other) {
+ if (*begin != *other) {
+ return false;
+ }
+ }
+ // Only return true if both strings finished at the same point
+ return (begin == end) == (*other == '\0');
+}
+
+/**
+ * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
+ * NUL-terminated then the string ends at max_len characters.
+ */
+std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len);
+
+}
--- /dev/null
+// Copyright (c) 2012- PPSSPP Project / Dolphin Project.
+
+// 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, version 2.0 or later versions.
+
+// 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 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#pragma once
+
+#if defined(_MSC_VER)
+ #include <cstdlib>
+#elif defined(__linux__)
+ #include <byteswap.h>
+#elif defined(__FreeBSD__)
+ #include <sys/endian.h>
+#endif
+
+#include "citraimport\common/common_types.h"
+
+// GCC 4.6+
+#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+
+#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN)
+#define COMMON_LITTLE_ENDIAN 1
+#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) && !defined(COMMON_BIG_ENDIAN)
+#define COMMON_BIG_ENDIAN 1
+#endif
+
+// LLVM/clang
+#elif __clang__
+
+#if __LITTLE_ENDIAN__ && !defined(COMMON_LITTLE_ENDIAN)
+#define COMMON_LITTLE_ENDIAN 1
+#elif __BIG_ENDIAN__ && !defined(COMMON_BIG_ENDIAN)
+#define COMMON_BIG_ENDIAN 1
+#endif
+
+// MSVC
+#elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN)
+
+#define COMMON_LITTLE_ENDIAN 1
+#endif
+
+// Worst case, default to little endian.
+#if !COMMON_BIG_ENDIAN && !COMMON_LITTLE_ENDIAN
+#define COMMON_LITTLE_ENDIAN 1
+#endif
+
+namespace Common {
+
+inline u8 swap8(u8 _data) {return _data;}
+inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) | _data[2];}
+
+#ifdef _MSC_VER
+inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);}
+inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);}
+inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);}
+#elif _M_ARM
+inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;}
+inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;}
+inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);}
+#elif __linux__
+inline u16 swap16(u16 _data) {return bswap_16(_data);}
+inline u32 swap32(u32 _data) {return bswap_32(_data);}
+inline u64 swap64(u64 _data) {return bswap_64(_data);}
+#elif __APPLE__
+inline __attribute__((always_inline)) u16 swap16(u16 _data)
+{return (_data >> 8) | (_data << 8);}
+inline __attribute__((always_inline)) u32 swap32(u32 _data)
+{return __builtin_bswap32(_data);}
+inline __attribute__((always_inline)) u64 swap64(u64 _data)
+{return __builtin_bswap64(_data);}
+#elif __FreeBSD__
+inline u16 swap16(u16 _data) {return bswap16(_data);}
+inline u32 swap32(u32 _data) {return bswap32(_data);}
+inline u64 swap64(u64 _data) {return bswap64(_data);}
+#else
+// Slow generic implementation.
+inline u16 swap16(u16 data) {return (data >> 8) | (data << 8);}
+inline u32 swap32(u32 data) {return (swap16(data) << 16) | swap16(data >> 16);}
+inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 32);}
+#endif
+
+inline float swapf(float f) {
+ union {
+ float f;
+ unsigned int u32;
+ } dat1, dat2;
+
+ dat1.f = f;
+ dat2.u32 = swap32(dat1.u32);
+
+ return dat2.f;
+}
+
+inline double swapd(double f) {
+ union {
+ double f;
+ unsigned long long u64;
+ } dat1, dat2;
+
+ dat1.f = f;
+ dat2.u64 = swap64(dat1.u64);
+
+ return dat2.f;
+}
+
+inline u16 swap16(const u8* _pData) {return swap16(*(const u16*)_pData);}
+inline u32 swap32(const u8* _pData) {return swap32(*(const u32*)_pData);}
+inline u64 swap64(const u8* _pData) {return swap64(*(const u64*)_pData);}
+
+template <int count>
+void swap(u8*);
+
+template <>
+inline void swap<1>(u8* data) { }
+
+template <>
+inline void swap<2>(u8* data) {
+ *reinterpret_cast<u16*>(data) = swap16(data);
+}
+
+template <>
+inline void swap<4>(u8* data) {
+ *reinterpret_cast<u32*>(data) = swap32(data);
+}
+
+template <>
+inline void swap<8>(u8* data) {
+ *reinterpret_cast<u64*>(data) = swap64(data);
+}
+
+} // Namespace Common
+
+
+template <typename T, typename F>
+struct swap_struct_t {
+ typedef swap_struct_t<T, F> swapped_t;
+
+protected:
+ T value = T();
+
+ static T swap(T v) {
+ return F::swap(v);
+ }
+public:
+ T const swap() const {
+ return swap(value);
+
+ }
+ swap_struct_t() = default;
+ swap_struct_t(const T &v): value(swap(v)) {}
+
+ template <typename S>
+ swapped_t& operator=(const S &source) {
+ value = swap((T)source);
+ return *this;
+ }
+
+ operator s8() const { return (s8)swap(); }
+ operator u8() const { return (u8)swap(); }
+ operator s16() const { return (s16)swap(); }
+ operator u16() const { return (u16)swap(); }
+ operator s32() const { return (s32)swap(); }
+ operator u32() const { return (u32)swap(); }
+ operator s64() const { return (s64)swap(); }
+ operator u64() const { return (u64)swap(); }
+ operator float() const { return (float)swap(); }
+ operator double() const { return (double)swap(); }
+
+ // +v
+ swapped_t operator +() const {
+ return +swap();
+ }
+ // -v
+ swapped_t operator -() const {
+ return -swap();
+ }
+
+ // v / 5
+ swapped_t operator/(const swapped_t &i) const {
+ return swap() / i.swap();
+ }
+ template <typename S>
+ swapped_t operator/(const S &i) const {
+ return swap() / i;
+ }
+
+ // v * 5
+ swapped_t operator*(const swapped_t &i) const {
+ return swap() * i.swap();
+ }
+ template <typename S>
+ swapped_t operator*(const S &i) const {
+ return swap() * i;
+ }
+
+ // v + 5
+ swapped_t operator+(const swapped_t &i) const {
+ return swap() + i.swap();
+ }
+ template <typename S>
+ swapped_t operator+(const S &i) const {
+ return swap() + (T)i;
+ }
+ // v - 5
+ swapped_t operator-(const swapped_t &i) const {
+ return swap() - i.swap();
+ }
+ template <typename S>
+ swapped_t operator-(const S &i) const {
+ return swap() - (T)i;
+ }
+
+ // v += 5
+ swapped_t& operator+=(const swapped_t &i) {
+ value = swap(swap() + i.swap());
+ return *this;
+ }
+ template <typename S>
+ swapped_t& operator+=(const S &i) {
+ value = swap(swap() + (T)i);
+ return *this;
+ }
+ // v -= 5
+ swapped_t& operator-=(const swapped_t &i) {
+ value = swap(swap() - i.swap());
+ return *this;
+ }
+ template <typename S>
+ swapped_t& operator-=(const S &i) {
+ value = swap(swap() - (T)i);
+ return *this;
+ }
+
+ // ++v
+ swapped_t& operator++() {
+ value = swap(swap()+1);
+ return *this;
+ }
+ // --v
+ swapped_t& operator--() {
+ value = swap(swap()-1);
+ return *this;
+ }
+
+ // v++
+ swapped_t operator++(int) {
+ swapped_t old = *this;
+ value = swap(swap()+1);
+ return old;
+ }
+ // v--
+ swapped_t operator--(int) {
+ swapped_t old = *this;
+ value = swap(swap()-1);
+ return old;
+ }
+ // Comparaison
+ // v == i
+ bool operator==(const swapped_t &i) const {
+ return swap() == i.swap();
+ }
+ template <typename S>
+ bool operator==(const S &i) const {
+ return swap() == i;
+ }
+
+ // v != i
+ bool operator!=(const swapped_t &i) const {
+ return swap() != i.swap();
+ }
+ template <typename S>
+ bool operator!=(const S &i) const {
+ return swap() != i;
+ }
+
+ // v > i
+ bool operator>(const swapped_t &i) const {
+ return swap() > i.swap();
+ }
+ template <typename S>
+ bool operator>(const S &i) const {
+ return swap() > i;
+ }
+
+ // v < i
+ bool operator<(const swapped_t &i) const {
+ return swap() < i.swap();
+ }
+ template <typename S>
+ bool operator<(const S &i) const {
+ return swap() < i;
+ }
+
+ // v >= i
+ bool operator>=(const swapped_t &i) const {
+ return swap() >= i.swap();
+ }
+ template <typename S>
+ bool operator>=(const S &i) const {
+ return swap() >= i;
+ }
+
+ // v <= i
+ bool operator<=(const swapped_t &i) const {
+ return swap() <= i.swap();
+ }
+ template <typename S>
+ bool operator<=(const S &i) const {
+ return swap() <= i;
+ }
+
+ // logical
+ swapped_t operator !() const {
+ return !swap();
+ }
+
+ // bitmath
+ swapped_t operator ~() const {
+ return ~swap();
+ }
+
+ swapped_t operator &(const swapped_t &b) const {
+ return swap() & b.swap();
+ }
+ template <typename S>
+ swapped_t operator &(const S &b) const {
+ return swap() & b;
+ }
+ swapped_t& operator &=(const swapped_t &b) {
+ value = swap(swap() & b.swap());
+ return *this;
+ }
+ template <typename S>
+ swapped_t& operator &=(const S b) {
+ value = swap(swap() & b);
+ return *this;
+ }
+
+ swapped_t operator |(const swapped_t &b) const {
+ return swap() | b.swap();
+ }
+ template <typename S>
+ swapped_t operator |(const S &b) const {
+ return swap() | b;
+ }
+ swapped_t& operator |=(const swapped_t &b) {
+ value = swap(swap() | b.swap());
+ return *this;
+ }
+ template <typename S>
+ swapped_t& operator |=(const S &b) {
+ value = swap(swap() | b);
+ return *this;
+ }
+
+ swapped_t operator ^(const swapped_t &b) const {
+ return swap() ^ b.swap();
+ }
+ template <typename S>
+ swapped_t operator ^(const S &b) const {
+ return swap() ^ b;
+ }
+ swapped_t& operator ^=(const swapped_t &b) {
+ value = swap(swap() ^ b.swap());
+ return *this;
+ }
+ template <typename S>
+ swapped_t& operator ^=(const S &b) {
+ value = swap(swap() ^ b);
+ return *this;
+ }
+
+ template <typename S>
+ swapped_t operator <<(const S &b) const {
+ return swap() << b;
+ }
+ template <typename S>
+ swapped_t& operator <<=(const S &b) const {
+ value = swap(swap() << b);
+ return *this;
+ }
+
+ template <typename S>
+ swapped_t operator >>(const S &b) const {
+ return swap() >> b;
+ }
+ template <typename S>
+ swapped_t& operator >>=(const S &b) const {
+ value = swap(swap() >> b);
+ return *this;
+ }
+
+ // Member
+ /** todo **/
+
+
+ // Arithmetics
+ template <typename S, typename T2, typename F2>
+ friend S operator+(const S &p, const swapped_t v);
+
+ template <typename S, typename T2, typename F2>
+ friend S operator-(const S &p, const swapped_t v);
+
+ template <typename S, typename T2, typename F2>
+ friend S operator/(const S &p, const swapped_t v);
+
+ template <typename S, typename T2, typename F2>
+ friend S operator*(const S &p, const swapped_t v);
+
+ template <typename S, typename T2, typename F2>
+ friend S operator%(const S &p, const swapped_t v);
+
+ // Arithmetics + assignements
+ template <typename S, typename T2, typename F2>
+ friend S operator+=(const S &p, const swapped_t v);
+
+ template <typename S, typename T2, typename F2>
+ friend S operator-=(const S &p, const swapped_t v);
+
+ // Bitmath
+ template <typename S, typename T2, typename F2>
+ friend S operator&(const S &p, const swapped_t v);
+
+ // Comparison
+ template <typename S, typename T2, typename F2>
+ friend bool operator<(const S &p, const swapped_t v);
+
+ template <typename S, typename T2, typename F2>
+ friend bool operator>(const S &p, const swapped_t v);
+
+ template <typename S, typename T2, typename F2>
+ friend bool operator<=(const S &p, const swapped_t v);
+
+ template <typename S, typename T2, typename F2>
+ friend bool operator>=(const S &p, const swapped_t v);
+
+ template <typename S, typename T2, typename F2>
+ friend bool operator!=(const S &p, const swapped_t v);
+
+ template <typename S, typename T2, typename F2>
+ friend bool operator==(const S &p, const swapped_t v);
+};
+
+
+// Arithmetics
+template <typename S, typename T, typename F>
+S operator+(const S &i, const swap_struct_t<T, F> v) {
+ return i + v.swap();
+}
+
+template <typename S, typename T, typename F>
+S operator-(const S &i, const swap_struct_t<T, F> v) {
+ return i - v.swap();
+}
+
+template <typename S, typename T, typename F>
+S operator/(const S &i, const swap_struct_t<T, F> v) {
+ return i / v.swap();
+}
+
+template <typename S, typename T, typename F>
+S operator*(const S &i, const swap_struct_t<T, F> v) {
+ return i * v.swap();
+}
+
+template <typename S, typename T, typename F>
+S operator%(const S &i, const swap_struct_t<T, F> v) {
+ return i % v.swap();
+}
+
+// Arithmetics + assignements
+template <typename S, typename T, typename F>
+S &operator+=(S &i, const swap_struct_t<T, F> v) {
+ i += v.swap();
+ return i;
+}
+
+template <typename S, typename T, typename F>
+S &operator-=(S &i, const swap_struct_t<T, F> v) {
+ i -= v.swap();
+ return i;
+}
+
+// Logical
+template <typename S, typename T, typename F>
+S operator&(const S &i, const swap_struct_t<T, F> v) {
+ return i & v.swap();
+}
+
+template <typename S, typename T, typename F>
+S operator&(const swap_struct_t<T, F> v, const S &i) {
+ return (S)(v.swap() & i);
+}
+
+
+// Comparaison
+template <typename S, typename T, typename F>
+bool operator<(const S &p, const swap_struct_t<T, F> v) {
+ return p < v.swap();
+}
+template <typename S, typename T, typename F>
+bool operator>(const S &p, const swap_struct_t<T, F> v) {
+ return p > v.swap();
+}
+template <typename S, typename T, typename F>
+bool operator<=(const S &p, const swap_struct_t<T, F> v) {
+ return p <= v.swap();
+}
+template <typename S, typename T, typename F>
+bool operator>=(const S &p, const swap_struct_t<T, F> v) {
+ return p >= v.swap();
+}
+template <typename S, typename T, typename F>
+bool operator!=(const S &p, const swap_struct_t<T, F> v) {
+ return p != v.swap();
+}
+template <typename S, typename T, typename F>
+bool operator==(const S &p, const swap_struct_t<T, F> v) {
+ return p == v.swap();
+}
+
+template <typename T>
+struct swap_64_t {
+ static T swap(T x) {
+ return (T)Common::swap64(*(u64 *)&x);
+ }
+};
+
+template <typename T>
+struct swap_32_t {
+ static T swap(T x) {
+ return (T)Common::swap32(*(u32 *)&x);
+ }
+};
+
+template <typename T>
+struct swap_16_t {
+ static T swap(T x) {
+ return (T)Common::swap16(*(u16 *)&x);
+ }
+};
+
+template <typename T>
+struct swap_float_t {
+ static T swap(T x) {
+ return (T)Common::swapf(*(float *)&x);
+ }
+};
+
+template <typename T>
+struct swap_double_t {
+ static T swap(T x) {
+ return (T)Common::swapd(*(double *)&x);
+ }
+};
+
+#if COMMON_LITTLE_ENDIAN
+typedef u32 u32_le;
+typedef u16 u16_le;
+typedef u64 u64_le;
+
+typedef s32 s32_le;
+typedef s16 s16_le;
+typedef s64 s64_le;
+
+typedef float float_le;
+typedef double double_le;
+
+typedef swap_struct_t<u64, swap_64_t<u64> > u64_be;
+typedef swap_struct_t<s64, swap_64_t<s64> > s64_be;
+
+typedef swap_struct_t<u32, swap_32_t<u32> > u32_be;
+typedef swap_struct_t<s32, swap_32_t<s32> > s32_be;
+
+typedef swap_struct_t<u16, swap_16_t<u16> > u16_be;
+typedef swap_struct_t<s16, swap_16_t<s16> > s16_be;
+
+typedef swap_struct_t<float, swap_float_t<float> > float_be;
+typedef swap_struct_t<double, swap_double_t<double> > double_be;
+#else
+
+typedef swap_struct_t<u64, swap_64_t<u64> > u64_le;
+typedef swap_struct_t<s64, swap_64_t<s64> > s64_le;
+
+typedef swap_struct_t<u32, swap_32_t<u32> > u32_le;
+typedef swap_struct_t<s32, swap_32_t<s32> > s32_le;
+
+typedef swap_struct_t<u16, swap_16_t<u16> > u16_le;
+typedef swap_struct_t< s16, swap_16_t<s16> > s16_le;
+
+typedef swap_struct_t<float, swap_float_t<float> > float_le;
+typedef swap_struct_t<double, swap_double_t<double> > double_le;
+
+typedef u32 u32_be;
+typedef u16 u16_be;
+typedef u64 u64_be;
+
+typedef s32 s32_be;
+typedef s16 s16_be;
+typedef s64 s64_be;
+
+typedef float float_be;
+typedef double double_be;
+
+#endif
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "citraimport\common/symbols.h"
+
+TSymbolsMap g_symbols;
+
+namespace Symbols
+{
+ bool HasSymbol(u32 address)
+ {
+ return g_symbols.find(address) != g_symbols.end();
+ }
+
+ void Add(u32 address, const std::string& name, u32 size, u32 type)
+ {
+ if (!HasSymbol(address))
+ {
+ TSymbol symbol;
+ symbol.address = address;
+ symbol.name = name;
+ symbol.size = size;
+ symbol.type = type;
+
+ g_symbols.emplace(address, symbol);
+ }
+ }
+
+ TSymbol GetSymbol(u32 address)
+ {
+ const auto iter = g_symbols.find(address);
+
+ if (iter != g_symbols.end())
+ return iter->second;
+
+ return {};
+ }
+
+ const std::string GetName(u32 address)
+ {
+ return GetSymbol(address).name;
+ }
+
+ void Remove(u32 address)
+ {
+ g_symbols.erase(address);
+ }
+
+ void Clear()
+ {
+ g_symbols.clear();
+ }
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "citraimport\common/common_types.h"
+
+struct TSymbol
+{
+ u32 address = 0;
+ std::string name;
+ u32 size = 0;
+ u32 type = 0;
+};
+
+typedef std::map<u32, TSymbol> TSymbolsMap;
+typedef std::pair<u32, TSymbol> TSymbolsPair;
+
+namespace Symbols
+{
+ bool HasSymbol(u32 address);
+
+ void Add(u32 address, const std::string& name, u32 size, u32 type);
+ TSymbol GetSymbol(u32 address);
+ const std::string GetName(u32 address);
+ void Remove(u32 address);
+ void Clear();
+}
+
--- /dev/null
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <mutex>
+
+namespace Common {
+
+/**
+ * Wraps an object, only allowing access to it via a locking reference wrapper. Good to ensure no
+ * one forgets to lock a mutex before acessing an object. To access the wrapped object construct a
+ * SyncronizedRef on this wrapper. Inspired by Rust's Mutex type (http://doc.rust-lang.org/std/sync/struct.Mutex.html).
+ */
+template <typename T>
+class SynchronizedWrapper {
+public:
+ template <typename... Args>
+ SynchronizedWrapper(Args&&... args) :
+ data(std::forward<Args>(args)...) {
+ }
+
+private:
+ template <typename U>
+ friend class SynchronizedRef;
+
+ std::mutex mutex;
+ T data;
+};
+
+/**
+ * Synchronized reference, that keeps a SynchronizedWrapper's mutex locked during its lifetime. This
+ * greatly reduces the chance that someone will access the wrapped resource without locking the
+ * mutex.
+ */
+template <typename T>
+class SynchronizedRef {
+public:
+ SynchronizedRef(SynchronizedWrapper<T>& wrapper) : wrapper(&wrapper) {
+ wrapper.mutex.lock();
+ }
+
+ SynchronizedRef(SynchronizedRef&) = delete;
+ SynchronizedRef(SynchronizedRef&& o) : wrapper(o.wrapper) {
+ o.wrapper = nullptr;
+ }
+
+ ~SynchronizedRef() {
+ if (wrapper)
+ wrapper->mutex.unlock();
+ }
+
+ SynchronizedRef& operator=(SynchronizedRef&) = delete;
+ SynchronizedRef& operator=(SynchronizedRef&& o) {
+ std::swap(wrapper, o.wrapper);
+ return *this;
+ }
+
+ T& operator*() { return wrapper->data; }
+ const T& operator*() const { return wrapper->data; }
+
+ T* operator->() { return &wrapper->data; }
+ const T* operator->() const { return &wrapper->data; }
+
+private:
+ SynchronizedWrapper<T>* wrapper;
+};
+
+} // namespace Common
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/thread.h"
+
+#ifdef __APPLE__
+ #include <mach/mach.h>
+#elif defined(_WIN32)
+ #include <Windows.h>
+#else
+ #if defined(BSD4_4) || defined(__OpenBSD__)
+ #include <pthread_np.h>
+ #else
+ #include <pthread.h>
+ #endif
+ #include <sched.h>
+#endif
+
+#ifndef _WIN32
+ #include <unistd.h>
+#endif
+
+namespace Common
+{
+
+int CurrentThreadId()
+{
+#ifdef _MSC_VER
+ return GetCurrentThreadId();
+#elif defined __APPLE__
+ return mach_thread_self();
+#else
+ return 0;
+#endif
+}
+
+#ifdef _WIN32
+// Supporting functions
+void SleepCurrentThread(int ms)
+{
+ Sleep(ms);
+}
+#endif
+
+#ifdef _MSC_VER
+
+void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask)
+{
+ SetThreadAffinityMask(thread, mask);
+}
+
+void SetCurrentThreadAffinity(u32 mask)
+{
+ SetThreadAffinityMask(GetCurrentThread(), mask);
+}
+
+void SwitchCurrentThread()
+{
+ SwitchToThread();
+}
+
+// Sets the debugger-visible name of the current thread.
+// Uses undocumented (actually, it is now documented) trick.
+// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp
+
+// This is implemented much nicer in upcoming msvc++, see:
+// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx
+void SetCurrentThreadName(const char* szThreadName)
+{
+ static const DWORD MS_VC_EXCEPTION = 0x406D1388;
+
+ #pragma pack(push,8)
+ struct THREADNAME_INFO
+ {
+ DWORD dwType; // must be 0x1000
+ LPCSTR szName; // pointer to name (in user addr space)
+ DWORD dwThreadID; // thread ID (-1=caller thread)
+ DWORD dwFlags; // reserved for future use, must be zero
+ } info;
+ #pragma pack(pop)
+
+ info.dwType = 0x1000;
+ info.szName = szThreadName;
+ info.dwThreadID = -1; //dwThreadID;
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION)
+ {}
+}
+
+#else // !MSVC_VER, so must be POSIX threads
+
+void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask)
+{
+#ifdef __APPLE__
+ thread_policy_set(pthread_mach_thread_np(thread),
+ THREAD_AFFINITY_POLICY, (integer_t *)&mask, 1);
+#elif (defined __linux__ || defined BSD4_4) && !(defined ANDROID)
+ cpu_set_t cpu_set;
+ CPU_ZERO(&cpu_set);
+
+ for (int i = 0; i != sizeof(mask) * 8; ++i)
+ if ((mask >> i) & 1)
+ CPU_SET(i, &cpu_set);
+
+ pthread_setaffinity_np(thread, sizeof(cpu_set), &cpu_set);
+#endif
+}
+
+void SetCurrentThreadAffinity(u32 mask)
+{
+ SetThreadAffinity(pthread_self(), mask);
+}
+
+#ifndef _WIN32
+void SleepCurrentThread(int ms)
+{
+ usleep(1000 * ms);
+}
+
+void SwitchCurrentThread()
+{
+ usleep(1000 * 1);
+}
+#endif
+
+// MinGW with the POSIX threading model does not support pthread_setname_np
+#if !defined(_WIN32) || defined(_MSC_VER)
+void SetCurrentThreadName(const char* szThreadName)
+{
+#ifdef __APPLE__
+ pthread_setname_np(szThreadName);
+#elif defined(__OpenBSD__)
+ pthread_set_name_np(pthread_self(), szThreadName);
+#else
+ pthread_setname_np(pthread_self(), szThreadName);
+#endif
+}
+#endif
+
+#endif
+
+} // namespace Common
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <thread>
+#include <condition_variable>
+#include <mutex>
+
+#include "citraimport/common/common_types.h"
+
+// Support for C++11's thread_local keyword was surprisingly spotty in compilers until very
+// recently. Fortunately, thread local variables have been well supported for compilers for a while,
+// but with semantics supporting only POD types, so we can use a few defines to get some amount of
+// backwards compat support.
+// WARNING: This only works correctly with POD types.
+#if defined(__clang__)
+# if !__has_feature(cxx_thread_local)
+# define thread_local __thread
+# endif
+#elif defined(__GNUC__)
+# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)
+# define thread_local __thread
+# endif
+#elif defined(_MSC_VER)
+# if _MSC_VER < 1900
+# define thread_local __declspec(thread)
+# endif
+#endif
+
+namespace Common
+{
+
+int CurrentThreadId();
+
+void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
+void SetCurrentThreadAffinity(u32 mask);
+
+class Event {
+public:
+ Event() : is_set(false) {}
+
+ void Set() {
+ std::lock_guard<std::mutex> lk(m_mutex);
+ if (!is_set) {
+ is_set = true;
+ m_condvar.notify_one();
+ }
+ }
+
+ void Wait() {
+ std::unique_lock<std::mutex> lk(m_mutex);
+ m_condvar.wait(lk, [&]{ return is_set; });
+ is_set = false;
+ }
+
+ void Reset() {
+ std::unique_lock<std::mutex> lk(m_mutex);
+ // no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration
+ is_set = false;
+ }
+
+private:
+ bool is_set;
+ std::condition_variable m_condvar;
+ std::mutex m_mutex;
+};
+
+class Barrier {
+public:
+ Barrier(size_t count) : m_count(count), m_waiting(0) {}
+
+ /// Blocks until all "count" threads have called Sync()
+ void Sync() {
+ std::unique_lock<std::mutex> lk(m_mutex);
+
+ // TODO: broken when next round of Sync()s
+ // is entered before all waiting threads return from the notify_all
+
+ if (++m_waiting == m_count) {
+ m_waiting = 0;
+ m_condvar.notify_all();
+ } else {
+ m_condvar.wait(lk, [&]{ return m_waiting == 0; });
+ }
+ }
+
+private:
+ std::condition_variable m_condvar;
+ std::mutex m_mutex;
+ const size_t m_count;
+ size_t m_waiting;
+};
+
+void SleepCurrentThread(int ms);
+void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
+
+// Use this function during a spin-wait to make the current thread
+// relax while another thread is working. This may be more efficient
+// than using events because event functions use kernel calls.
+inline void YieldCPU()
+{
+ std::this_thread::yield();
+}
+
+void SetCurrentThreadName(const char *name);
+
+} // namespace Common
--- /dev/null
+// Copyright 2014 Citra Emulator Project / PPSSPP Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <deque>
+
+#include <boost/range/algorithm_ext/erase.hpp>
+
+namespace Common {
+
+template<class T, unsigned int N>
+struct ThreadQueueList {
+ // TODO(yuriks): If performance proves to be a problem, the std::deques can be replaced with
+ // (dynamically resizable) circular buffers to remove their overhead when
+ // inserting and popping.
+
+ typedef unsigned int Priority;
+
+ // Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
+ static const Priority NUM_QUEUES = N;
+
+ ThreadQueueList() {
+ first = nullptr;
+ }
+
+ // Only for debugging, returns priority level.
+ Priority contains(const T& uid) {
+ for (Priority i = 0; i < NUM_QUEUES; ++i) {
+ Queue& cur = queues[i];
+ if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ T get_first() {
+ Queue *cur = first;
+ while (cur != nullptr) {
+ if (!cur->data.empty()) {
+ return cur->data.front();
+ }
+ cur = cur->next_nonempty;
+ }
+
+ return T();
+ }
+
+ T pop_first() {
+ Queue *cur = first;
+ while (cur != nullptr) {
+ if (!cur->data.empty()) {
+ auto tmp = std::move(cur->data.front());
+ cur->data.pop_front();
+ return tmp;
+ }
+ cur = cur->next_nonempty;
+ }
+
+ return T();
+ }
+
+ T pop_first_better(Priority priority) {
+ Queue *cur = first;
+ Queue *stop = &queues[priority];
+ while (cur < stop) {
+ if (!cur->data.empty()) {
+ auto tmp = std::move(cur->data.front());
+ cur->data.pop_front();
+ return tmp;
+ }
+ cur = cur->next_nonempty;
+ }
+
+ return T();
+ }
+
+ void push_front(Priority priority, const T& thread_id) {
+ Queue *cur = &queues[priority];
+ cur->data.push_front(thread_id);
+ }
+
+ void push_back(Priority priority, const T& thread_id) {
+ Queue *cur = &queues[priority];
+ cur->data.push_back(thread_id);
+ }
+
+ void move(const T& thread_id, Priority old_priority, Priority new_priority) {
+ remove(old_priority, thread_id);
+ prepare(new_priority);
+ push_back(new_priority, thread_id);
+ }
+
+ void remove(Priority priority, const T& thread_id) {
+ Queue *cur = &queues[priority];
+ boost::remove_erase(cur->data, thread_id);
+ }
+
+ void rotate(Priority priority) {
+ Queue *cur = &queues[priority];
+
+ if (cur->data.size() > 1) {
+ cur->data.push_back(std::move(cur->data.front()));
+ cur->data.pop_front();
+ }
+ }
+
+ void clear() {
+ queues.fill(Queue());
+ first = nullptr;
+ }
+
+ bool empty(Priority priority) const {
+ const Queue *cur = &queues[priority];
+ return cur->data.empty();
+ }
+
+ void prepare(Priority priority) {
+ Queue* cur = &queues[priority];
+ if (cur->next_nonempty == UnlinkedTag())
+ link(priority);
+ }
+
+private:
+ struct Queue {
+ // Points to the next active priority, skipping over ones that have never been used.
+ Queue* next_nonempty = UnlinkedTag();
+ // Double-ended queue of threads in this priority level
+ std::deque<T> data;
+ };
+
+ /// Special tag used to mark priority levels that have never been used.
+ static Queue* UnlinkedTag() {
+ return reinterpret_cast<Queue*>(1);
+ }
+
+ void link(Priority priority) {
+ Queue *cur = &queues[priority];
+
+ for (int i = priority - 1; i >= 0; --i) {
+ if (queues[i].next_nonempty != UnlinkedTag()) {
+ cur->next_nonempty = queues[i].next_nonempty;
+ queues[i].next_nonempty = cur;
+ return;
+ }
+ }
+
+ cur->next_nonempty = first;
+ first = cur;
+ }
+
+ // The first queue that's ever been used.
+ Queue* first;
+ // The priority level queues of thread ids.
+ std::array<Queue, NUM_QUEUES> queues;
+};
+
+} // namespace
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "citraimport\common/common_types.h"
+#include <string>
+
+namespace Common
+{
+class Timer
+{
+public:
+ Timer();
+
+ void Start();
+ void Stop();
+ void Update();
+
+ // The time difference is always returned in milliseconds, regardless of alternative internal representation
+ u64 GetTimeDifference();
+ void AddTimeDifference();
+
+ static void IncreaseResolution();
+ static void RestoreResolution();
+ static u64 GetTimeSinceJan1970();
+ static u64 GetLocalTimeSinceJan1970();
+ static double GetDoubleTime();
+
+ static std::string GetTimeFormatted();
+ std::string GetTimeElapsedFormatted() const;
+ u64 GetTimeElapsed();
+
+ static u32 GetTimeMs();
+
+private:
+ u64 m_LastTime;
+ u64 m_StartTime;
+ bool m_Running;
+};
+
+} // Namespace Common
--- /dev/null
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <cmath>
+
+namespace Math {
+
+template<typename T> class Vec2;
+template<typename T> class Vec3;
+template<typename T> class Vec4;
+
+template<typename T>
+static inline Vec2<T> MakeVec(const T& x, const T& y);
+template<typename T>
+static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z);
+template<typename T>
+static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w);
+
+
+template<typename T>
+class Vec2 {
+public:
+ T x;
+ T y;
+
+ T* AsArray() { return &x; }
+
+ Vec2() = default;
+ Vec2(const T a[2]) : x(a[0]), y(a[1]) {}
+ Vec2(const T& _x, const T& _y) : x(_x), y(_y) {}
+
+ template<typename T2>
+ Vec2<T2> Cast() const {
+ return Vec2<T2>((T2)x, (T2)y);
+ }
+
+ static Vec2 AssignToAll(const T& f)
+ {
+ return Vec2<T>(f, f);
+ }
+
+ void Write(T a[2])
+ {
+ a[0] = x; a[1] = y;
+ }
+
+ Vec2<decltype(T{}+T{})> operator +(const Vec2& other) const
+ {
+ return MakeVec(x+other.x, y+other.y);
+ }
+ void operator += (const Vec2 &other)
+ {
+ x+=other.x; y+=other.y;
+ }
+ Vec2<decltype(T{}-T{})> operator -(const Vec2& other) const
+ {
+ return MakeVec(x-other.x, y-other.y);
+ }
+ void operator -= (const Vec2& other)
+ {
+ x-=other.x; y-=other.y;
+ }
+ Vec2<decltype(-T{})> operator -() const
+ {
+ return MakeVec(-x,-y);
+ }
+ Vec2<decltype(T{}*T{})> operator * (const Vec2& other) const
+ {
+ return MakeVec(x*other.x, y*other.y);
+ }
+ template<typename V>
+ Vec2<decltype(T{}*V{})> operator * (const V& f) const
+ {
+ return MakeVec(x*f,y*f);
+ }
+ template<typename V>
+ void operator *= (const V& f)
+ {
+ x*=f; y*=f;
+ }
+ template<typename V>
+ Vec2<decltype(T{}/V{})> operator / (const V& f) const
+ {
+ return MakeVec(x/f,y/f);
+ }
+ template<typename V>
+ void operator /= (const V& f)
+ {
+ *this = *this / f;
+ }
+
+ T Length2() const
+ {
+ return x*x + y*y;
+ }
+
+ // Only implemented for T=float
+ float Length() const;
+ void SetLength(const float l);
+ Vec2 WithLength(const float l) const;
+ float Distance2To(Vec2 &other);
+ Vec2 Normalized() const;
+ float Normalize(); // returns the previous length, which is often useful
+
+ T& operator [] (int i) //allow vector[1] = 3 (vector.y=3)
+ {
+ return *((&x) + i);
+ }
+ T operator [] (const int i) const
+ {
+ return *((&x) + i);
+ }
+
+ void SetZero()
+ {
+ x=0; y=0;
+ }
+
+ // Common aliases: UV (texel coordinates), ST (texture coordinates)
+ T& u() { return x; }
+ T& v() { return y; }
+ T& s() { return x; }
+ T& t() { return y; }
+
+ const T& u() const { return x; }
+ const T& v() const { return y; }
+ const T& s() const { return x; }
+ const T& t() const { return y; }
+
+ // swizzlers - create a subvector of specific components
+ const Vec2 yx() const { return Vec2(y, x); }
+ const Vec2 vu() const { return Vec2(y, x); }
+ const Vec2 ts() const { return Vec2(y, x); }
+};
+
+template<typename T, typename V>
+Vec2<T> operator * (const V& f, const Vec2<T>& vec)
+{
+ return Vec2<T>(f*vec.x,f*vec.y);
+}
+
+typedef Vec2<float> Vec2f;
+
+template<typename T>
+class Vec3
+{
+public:
+ T x;
+ T y;
+ T z;
+
+ T* AsArray() { return &x; }
+
+ Vec3() = default;
+ Vec3(const T a[3]) : x(a[0]), y(a[1]), z(a[2]) {}
+ Vec3(const T& _x, const T& _y, const T& _z) : x(_x), y(_y), z(_z) {}
+
+ template<typename T2>
+ Vec3<T2> Cast() const {
+ return MakeVec<T2>((T2)x, (T2)y, (T2)z);
+ }
+
+ // Only implemented for T=int and T=float
+ static Vec3 FromRGB(unsigned int rgb);
+ unsigned int ToRGB() const; // alpha bits set to zero
+
+ static Vec3 AssignToAll(const T& f)
+ {
+ return MakeVec(f, f, f);
+ }
+
+ void Write(T a[3])
+ {
+ a[0] = x; a[1] = y; a[2] = z;
+ }
+
+ Vec3<decltype(T{}+T{})> operator +(const Vec3 &other) const
+ {
+ return MakeVec(x+other.x, y+other.y, z+other.z);
+ }
+ void operator += (const Vec3 &other)
+ {
+ x+=other.x; y+=other.y; z+=other.z;
+ }
+ Vec3<decltype(T{}-T{})> operator -(const Vec3 &other) const
+ {
+ return MakeVec(x-other.x, y-other.y, z-other.z);
+ }
+ void operator -= (const Vec3 &other)
+ {
+ x-=other.x; y-=other.y; z-=other.z;
+ }
+ Vec3<decltype(-T{})> operator -() const
+ {
+ return MakeVec(-x,-y,-z);
+ }
+ Vec3<decltype(T{}*T{})> operator * (const Vec3 &other) const
+ {
+ return MakeVec(x*other.x, y*other.y, z*other.z);
+ }
+ template<typename V>
+ Vec3<decltype(T{}*V{})> operator * (const V& f) const
+ {
+ return MakeVec(x*f,y*f,z*f);
+ }
+ template<typename V>
+ void operator *= (const V& f)
+ {
+ x*=f; y*=f; z*=f;
+ }
+ template<typename V>
+ Vec3<decltype(T{}/V{})> operator / (const V& f) const
+ {
+ return MakeVec(x/f,y/f,z/f);
+ }
+ template<typename V>
+ void operator /= (const V& f)
+ {
+ *this = *this / f;
+ }
+
+ T Length2() const
+ {
+ return x*x + y*y + z*z;
+ }
+
+ // Only implemented for T=float
+ float Length() const;
+ void SetLength(const float l);
+ Vec3 WithLength(const float l) const;
+ float Distance2To(Vec3 &other);
+ Vec3 Normalized() const;
+ float Normalize(); // returns the previous length, which is often useful
+
+ T& operator [] (int i) //allow vector[2] = 3 (vector.z=3)
+ {
+ return *((&x) + i);
+ }
+ T operator [] (const int i) const
+ {
+ return *((&x) + i);
+ }
+
+ void SetZero()
+ {
+ x=0; y=0; z=0;
+ }
+
+ // Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
+ T& u() { return x; }
+ T& v() { return y; }
+ T& w() { return z; }
+
+ T& r() { return x; }
+ T& g() { return y; }
+ T& b() { return z; }
+
+ T& s() { return x; }
+ T& t() { return y; }
+ T& q() { return z; }
+
+ const T& u() const { return x; }
+ const T& v() const { return y; }
+ const T& w() const { return z; }
+
+ const T& r() const { return x; }
+ const T& g() const { return y; }
+ const T& b() const { return z; }
+
+ const T& s() const { return x; }
+ const T& t() const { return y; }
+ const T& q() const { return z; }
+
+ // swizzlers - create a subvector of specific components
+ // e.g. Vec2 uv() { return Vec2(x,y); }
+ // _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all component names (x<->r) and permutations (xy<->yx)
+#define _DEFINE_SWIZZLER2(a, b, name) const Vec2<T> name() const { return Vec2<T>(a, b); }
+#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
+ _DEFINE_SWIZZLER2(a, b, a##b); \
+ _DEFINE_SWIZZLER2(a, b, a2##b2); \
+ _DEFINE_SWIZZLER2(a, b, a3##b3); \
+ _DEFINE_SWIZZLER2(a, b, a4##b4); \
+ _DEFINE_SWIZZLER2(b, a, b##a); \
+ _DEFINE_SWIZZLER2(b, a, b2##a2); \
+ _DEFINE_SWIZZLER2(b, a, b3##a3); \
+ _DEFINE_SWIZZLER2(b, a, b4##a4)
+
+ DEFINE_SWIZZLER2(x, y, r, g, u, v, s, t);
+ DEFINE_SWIZZLER2(x, z, r, b, u, w, s, q);
+ DEFINE_SWIZZLER2(y, z, g, b, v, w, t, q);
+#undef DEFINE_SWIZZLER2
+#undef _DEFINE_SWIZZLER2
+};
+
+template<typename T, typename V>
+Vec3<T> operator * (const V& f, const Vec3<T>& vec)
+{
+ return Vec3<T>(f*vec.x,f*vec.y,f*vec.z);
+}
+
+template<>
+inline float Vec3<float>::Length() const {
+ return std::sqrt(x * x + y * y + z * z);
+}
+
+template<>
+inline Vec3<float> Vec3<float>::Normalized() const {
+ return *this / Length();
+}
+
+
+typedef Vec3<float> Vec3f;
+
+template<typename T>
+class Vec4
+{
+public:
+ T x;
+ T y;
+ T z;
+ T w;
+
+ T* AsArray() { return &x; }
+
+ Vec4() = default;
+ Vec4(const T a[4]) : x(a[0]), y(a[1]), z(a[2]), w(a[3]) {}
+ Vec4(const T& _x, const T& _y, const T& _z, const T& _w) : x(_x), y(_y), z(_z), w(_w) {}
+
+ template<typename T2>
+ Vec4<T2> Cast() const {
+ return Vec4<T2>((T2)x, (T2)y, (T2)z, (T2)w);
+ }
+
+ // Only implemented for T=int and T=float
+ static Vec4 FromRGBA(unsigned int rgba);
+ unsigned int ToRGBA() const;
+
+ static Vec4 AssignToAll(const T& f) {
+ return Vec4<T>(f, f, f, f);
+ }
+
+ void Write(T a[4])
+ {
+ a[0] = x; a[1] = y; a[2] = z; a[3] = w;
+ }
+
+ Vec4<decltype(T{}+T{})> operator +(const Vec4& other) const
+ {
+ return MakeVec(x+other.x, y+other.y, z+other.z, w+other.w);
+ }
+ void operator += (const Vec4& other)
+ {
+ x+=other.x; y+=other.y; z+=other.z; w+=other.w;
+ }
+ Vec4<decltype(T{}-T{})> operator -(const Vec4 &other) const
+ {
+ return MakeVec(x-other.x, y-other.y, z-other.z, w-other.w);
+ }
+ void operator -= (const Vec4 &other)
+ {
+ x-=other.x; y-=other.y; z-=other.z; w-=other.w;
+ }
+ Vec4<decltype(-T{})> operator -() const
+ {
+ return MakeVec(-x,-y,-z,-w);
+ }
+ Vec4<decltype(T{}*T{})> operator * (const Vec4 &other) const
+ {
+ return MakeVec(x*other.x, y*other.y, z*other.z, w*other.w);
+ }
+ template<typename V>
+ Vec4<decltype(T{}*V{})> operator * (const V& f) const
+ {
+ return MakeVec(x*f,y*f,z*f,w*f);
+ }
+ template<typename V>
+ void operator *= (const V& f)
+ {
+ x*=f; y*=f; z*=f; w*=f;
+ }
+ template<typename V>
+ Vec4<decltype(T{}/V{})> operator / (const V& f) const
+ {
+ return MakeVec(x/f,y/f,z/f,w/f);
+ }
+ template<typename V>
+ void operator /= (const V& f)
+ {
+ *this = *this / f;
+ }
+
+ T Length2() const
+ {
+ return x*x + y*y + z*z + w*w;
+ }
+
+ // Only implemented for T=float
+ float Length() const;
+ void SetLength(const float l);
+ Vec4 WithLength(const float l) const;
+ float Distance2To(Vec4 &other);
+ Vec4 Normalized() const;
+ float Normalize(); // returns the previous length, which is often useful
+
+ T& operator [] (int i) //allow vector[2] = 3 (vector.z=3)
+ {
+ return *((&x) + i);
+ }
+ T operator [] (const int i) const
+ {
+ return *((&x) + i);
+ }
+
+ void SetZero()
+ {
+ x=0; y=0; z=0;
+ }
+
+ // Common alias: RGBA (colors)
+ T& r() { return x; }
+ T& g() { return y; }
+ T& b() { return z; }
+ T& a() { return w; }
+
+ const T& r() const { return x; }
+ const T& g() const { return y; }
+ const T& b() const { return z; }
+ const T& a() const { return w; }
+
+ // Swizzlers - Create a subvector of specific components
+ // e.g. Vec2 uv() { return Vec2(x,y); }
+
+ // _DEFINE_SWIZZLER2 defines a single such function
+ // DEFINE_SWIZZLER2_COMP1 defines one-component functions for all component names (x<->r)
+ // DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and permutations (xy<->yx)
+#define _DEFINE_SWIZZLER2(a, b, name) const Vec2<T> name() const { return Vec2<T>(a, b); }
+#define DEFINE_SWIZZLER2_COMP1(a, a2) \
+ _DEFINE_SWIZZLER2(a, a, a##a); \
+ _DEFINE_SWIZZLER2(a, a, a2##a2)
+#define DEFINE_SWIZZLER2_COMP2(a, b, a2, b2) \
+ _DEFINE_SWIZZLER2(a, b, a##b); \
+ _DEFINE_SWIZZLER2(a, b, a2##b2); \
+ _DEFINE_SWIZZLER2(b, a, b##a); \
+ _DEFINE_SWIZZLER2(b, a, b2##a2)
+
+ DEFINE_SWIZZLER2_COMP2(x, y, r, g);
+ DEFINE_SWIZZLER2_COMP2(x, z, r, b);
+ DEFINE_SWIZZLER2_COMP2(x, w, r, a);
+ DEFINE_SWIZZLER2_COMP2(y, z, g, b);
+ DEFINE_SWIZZLER2_COMP2(y, w, g, a);
+ DEFINE_SWIZZLER2_COMP2(z, w, b, a);
+ DEFINE_SWIZZLER2_COMP1(x, r);
+ DEFINE_SWIZZLER2_COMP1(y, g);
+ DEFINE_SWIZZLER2_COMP1(z, b);
+ DEFINE_SWIZZLER2_COMP1(w, a);
+#undef DEFINE_SWIZZLER2_COMP1
+#undef DEFINE_SWIZZLER2_COMP2
+#undef _DEFINE_SWIZZLER2
+
+#define _DEFINE_SWIZZLER3(a, b, c, name) const Vec3<T> name() const { return Vec3<T>(a, b, c); }
+#define DEFINE_SWIZZLER3_COMP1(a, a2) \
+ _DEFINE_SWIZZLER3(a, a, a, a##a##a); \
+ _DEFINE_SWIZZLER3(a, a, a, a2##a2##a2)
+#define DEFINE_SWIZZLER3_COMP3(a, b, c, a2, b2, c2) \
+ _DEFINE_SWIZZLER3(a, b, c, a##b##c); \
+ _DEFINE_SWIZZLER3(a, c, b, a##c##b); \
+ _DEFINE_SWIZZLER3(b, a, c, b##a##c); \
+ _DEFINE_SWIZZLER3(b, c, a, b##c##a); \
+ _DEFINE_SWIZZLER3(c, a, b, c##a##b); \
+ _DEFINE_SWIZZLER3(c, b, a, c##b##a); \
+ _DEFINE_SWIZZLER3(a, b, c, a2##b2##c2); \
+ _DEFINE_SWIZZLER3(a, c, b, a2##c2##b2); \
+ _DEFINE_SWIZZLER3(b, a, c, b2##a2##c2); \
+ _DEFINE_SWIZZLER3(b, c, a, b2##c2##a2); \
+ _DEFINE_SWIZZLER3(c, a, b, c2##a2##b2); \
+ _DEFINE_SWIZZLER3(c, b, a, c2##b2##a2)
+
+ DEFINE_SWIZZLER3_COMP3(x, y, z, r, g, b);
+ DEFINE_SWIZZLER3_COMP3(x, y, w, r, g, a);
+ DEFINE_SWIZZLER3_COMP3(x, z, w, r, b, a);
+ DEFINE_SWIZZLER3_COMP3(y, z, w, g, b, a);
+ DEFINE_SWIZZLER3_COMP1(x, r);
+ DEFINE_SWIZZLER3_COMP1(y, g);
+ DEFINE_SWIZZLER3_COMP1(z, b);
+ DEFINE_SWIZZLER3_COMP1(w, a);
+#undef DEFINE_SWIZZLER3_COMP1
+#undef DEFINE_SWIZZLER3_COMP3
+#undef _DEFINE_SWIZZLER3
+};
+
+
+template<typename T, typename V>
+Vec4<decltype(V{}*T{})> operator * (const V& f, const Vec4<T>& vec)
+{
+ return MakeVec(f*vec.x,f*vec.y,f*vec.z,f*vec.w);
+}
+
+typedef Vec4<float> Vec4f;
+
+
+template<typename T>
+static inline decltype(T{}*T{}+T{}*T{}) Dot(const Vec2<T>& a, const Vec2<T>& b)
+{
+ return a.x*b.x + a.y*b.y;
+}
+
+template<typename T>
+static inline decltype(T{}*T{}+T{}*T{}) Dot(const Vec3<T>& a, const Vec3<T>& b)
+{
+ return a.x*b.x + a.y*b.y + a.z*b.z;
+}
+
+template<typename T>
+static inline decltype(T{}*T{}+T{}*T{}) Dot(const Vec4<T>& a, const Vec4<T>& b)
+{
+ return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w;
+}
+
+template<typename T>
+static inline Vec3<decltype(T{}*T{}-T{}*T{})> Cross(const Vec3<T>& a, const Vec3<T>& b)
+{
+ return MakeVec(a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x);
+}
+
+// linear interpolation via float: 0.0=begin, 1.0=end
+template<typename X>
+static inline decltype(X{}*float{}+X{}*float{}) Lerp(const X& begin, const X& end, const float t)
+{
+ return begin*(1.f-t) + end*t;
+}
+
+// linear interpolation via int: 0=begin, base=end
+template<typename X, int base>
+static inline decltype((X{}*int{}+X{}*int{}) / base) LerpInt(const X& begin, const X& end, const int t)
+{
+ return (begin*(base-t) + end*t) / base;
+}
+
+// Utility vector factories
+template<typename T>
+static inline Vec2<T> MakeVec(const T& x, const T& y)
+{
+ return Vec2<T>{x, y};
+}
+
+template<typename T>
+static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z)
+{
+ return Vec3<T>{x, y, z};
+}
+
+template<typename T>
+static inline Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw)
+{
+ return MakeVec(x, y, zw[0], zw[1]);
+}
+
+template<typename T>
+static inline Vec3<T> MakeVec(const Vec2<T>& xy, const T& z)
+{
+ return MakeVec(xy[0], xy[1], z);
+}
+
+template<typename T>
+static inline Vec3<T> MakeVec(const T& x, const Vec2<T>& yz)
+{
+ return MakeVec(x, yz[0], yz[1]);
+}
+
+template<typename T>
+static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w)
+{
+ return Vec4<T>{x, y, z, w};
+}
+
+template<typename T>
+static inline Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w)
+{
+ return MakeVec(xy[0], xy[1], z, w);
+}
+
+template<typename T>
+static inline Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w)
+{
+ return MakeVec(x, yz[0], yz[1], w);
+}
+
+// NOTE: This has priority over "Vec2<Vec2<T>> MakeVec(const Vec2<T>& x, const Vec2<T>& y)".
+// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
+// out soon enough due to misuse of the returned structure.
+template<typename T>
+static inline Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw)
+{
+ return MakeVec(xy[0], xy[1], zw[0], zw[1]);
+}
+
+template<typename T>
+static inline Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w)
+{
+ return MakeVec(xyz[0], xyz[1], xyz[2], w);
+}
+
+template<typename T>
+static inline Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw)
+{
+ return MakeVec(x, yzw[0], yzw[1], yzw[2]);
+}
+
+
+} // namespace
--- /dev/null
+// Copyright (C) 2003 Dolphin Project.
+
+// 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, version 2.0 or later versions.
+
+// 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 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official SVN repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#include "abi.h"
+#include "emitter.h"
+
+using namespace Gen;
+
+// Shared code between Win64 and Unix64
+
+void XEmitter::ABI_CalculateFrameSize(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size, size_t* shadowp, size_t* subtractionp, size_t* xmm_offsetp) {
+ size_t shadow = 0;
+#if defined(_WIN32)
+ shadow = 0x20;
+#endif
+
+ int count = (mask & ABI_ALL_GPRS).Count();
+ rsp_alignment -= count * 8;
+ size_t subtraction = 0;
+ int fpr_count = (mask & ABI_ALL_FPRS).Count();
+ if (fpr_count) {
+ // If we have any XMMs to save, we must align the stack here.
+ subtraction = rsp_alignment & 0xf;
+ }
+ subtraction += 16 * fpr_count;
+ size_t xmm_base_subtraction = subtraction;
+ subtraction += needed_frame_size;
+ subtraction += shadow;
+ // Final alignment.
+ rsp_alignment -= subtraction;
+ subtraction += rsp_alignment & 0xf;
+
+ *shadowp = shadow;
+ *subtractionp = subtraction;
+ *xmm_offsetp = subtraction - xmm_base_subtraction;
+}
+
+size_t XEmitter::ABI_PushRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size) {
+ size_t shadow, subtraction, xmm_offset;
+ ABI_CalculateFrameSize(mask, rsp_alignment, needed_frame_size, &shadow, &subtraction, &xmm_offset);
+
+ for (int r : mask & ABI_ALL_GPRS)
+ PUSH((X64Reg)r);
+
+ if (subtraction)
+ SUB(64, R(RSP), subtraction >= 0x80 ? Imm32((u32)subtraction) : Imm8((u8)subtraction));
+
+ for (int x : mask & ABI_ALL_FPRS) {
+ MOVAPD(MDisp(RSP, (int)xmm_offset), (X64Reg)(x - 16));
+ xmm_offset += 16;
+ }
+
+ return shadow;
+}
+
+void XEmitter::ABI_PopRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size) {
+ size_t shadow, subtraction, xmm_offset;
+ ABI_CalculateFrameSize(mask, rsp_alignment, needed_frame_size, &shadow, &subtraction, &xmm_offset);
+
+ for (int x : mask & ABI_ALL_FPRS) {
+ MOVAPD((X64Reg) (x - 16), MDisp(RSP, (int)xmm_offset));
+ xmm_offset += 16;
+ }
+
+ if (subtraction)
+ ADD(64, R(RSP), subtraction >= 0x80 ? Imm32((u32)subtraction) : Imm8((u8)subtraction));
+
+ for (int r = 15; r >= 0; r--) {
+ if (mask[r])
+ POP((X64Reg)r);
+ }
+}
+
+// Common functions
+void XEmitter::ABI_CallFunction(const void *func) {
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionC16(const void *func, u16 param1) {
+ MOV(32, R(ABI_PARAM1), Imm32((u32)param1));
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionCC16(const void *func, u32 param1, u16 param2) {
+ MOV(32, R(ABI_PARAM1), Imm32(param1));
+ MOV(32, R(ABI_PARAM2), Imm32((u32)param2));
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionC(const void *func, u32 param1) {
+ MOV(32, R(ABI_PARAM1), Imm32(param1));
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionCC(const void *func, u32 param1, u32 param2) {
+ MOV(32, R(ABI_PARAM1), Imm32(param1));
+ MOV(32, R(ABI_PARAM2), Imm32(param2));
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionCCC(const void *func, u32 param1, u32 param2, u32 param3) {
+ MOV(32, R(ABI_PARAM1), Imm32(param1));
+ MOV(32, R(ABI_PARAM2), Imm32(param2));
+ MOV(32, R(ABI_PARAM3), Imm32(param3));
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionCCP(const void *func, u32 param1, u32 param2, void *param3) {
+ MOV(32, R(ABI_PARAM1), Imm32(param1));
+ MOV(32, R(ABI_PARAM2), Imm32(param2));
+ MOV(64, R(ABI_PARAM3), ImmPtr(param3));
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionCCCP(const void *func, u32 param1, u32 param2, u32 param3, void *param4) {
+ MOV(32, R(ABI_PARAM1), Imm32(param1));
+ MOV(32, R(ABI_PARAM2), Imm32(param2));
+ MOV(32, R(ABI_PARAM3), Imm32(param3));
+ MOV(64, R(ABI_PARAM4), ImmPtr(param4));
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionP(const void *func, void *param1) {
+ MOV(64, R(ABI_PARAM1), ImmPtr(param1));
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionPA(const void *func, void *param1, const Gen::OpArg &arg2) {
+ MOV(64, R(ABI_PARAM1), ImmPtr(param1));
+ if (!arg2.IsSimpleReg(ABI_PARAM2))
+ MOV(32, R(ABI_PARAM2), arg2);
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionPAA(const void *func, void *param1, const Gen::OpArg &arg2, const Gen::OpArg &arg3) {
+ MOV(64, R(ABI_PARAM1), ImmPtr(param1));
+ if (!arg2.IsSimpleReg(ABI_PARAM2))
+ MOV(32, R(ABI_PARAM2), arg2);
+ if (!arg3.IsSimpleReg(ABI_PARAM3))
+ MOV(32, R(ABI_PARAM3), arg3);
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionPPC(const void *func, void *param1, void *param2, u32 param3) {
+ MOV(64, R(ABI_PARAM1), ImmPtr(param1));
+ MOV(64, R(ABI_PARAM2), ImmPtr(param2));
+ MOV(32, R(ABI_PARAM3), Imm32(param3));
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+// Pass a register as a parameter.
+void XEmitter::ABI_CallFunctionR(const void *func, X64Reg reg1) {
+ if (reg1 != ABI_PARAM1)
+ MOV(32, R(ABI_PARAM1), R(reg1));
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+// Pass two registers as parameters.
+void XEmitter::ABI_CallFunctionRR(const void *func, X64Reg reg1, X64Reg reg2) {
+ if (reg2 != ABI_PARAM1) {
+ if (reg1 != ABI_PARAM1)
+ MOV(64, R(ABI_PARAM1), R(reg1));
+ if (reg2 != ABI_PARAM2)
+ MOV(64, R(ABI_PARAM2), R(reg2));
+ } else {
+ if (reg2 != ABI_PARAM2)
+ MOV(64, R(ABI_PARAM2), R(reg2));
+ if (reg1 != ABI_PARAM1)
+ MOV(64, R(ABI_PARAM1), R(reg1));
+ }
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionAC(const void *func, const Gen::OpArg &arg1, u32 param2)
+{
+ if (!arg1.IsSimpleReg(ABI_PARAM1))
+ MOV(32, R(ABI_PARAM1), arg1);
+ MOV(32, R(ABI_PARAM2), Imm32(param2));
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionACC(const void *func, const Gen::OpArg &arg1, u32 param2, u32 param3)
+{
+ if (!arg1.IsSimpleReg(ABI_PARAM1))
+ MOV(32, R(ABI_PARAM1), arg1);
+ MOV(32, R(ABI_PARAM2), Imm32(param2));
+ MOV(64, R(ABI_PARAM3), Imm64(param3));
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionA(const void *func, const Gen::OpArg &arg1)
+{
+ if (!arg1.IsSimpleReg(ABI_PARAM1))
+ MOV(32, R(ABI_PARAM1), arg1);
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
+
+void XEmitter::ABI_CallFunctionAA(const void *func, const Gen::OpArg &arg1, const Gen::OpArg &arg2)
+{
+ if (!arg1.IsSimpleReg(ABI_PARAM1))
+ MOV(32, R(ABI_PARAM1), arg1);
+ if (!arg2.IsSimpleReg(ABI_PARAM2))
+ MOV(32, R(ABI_PARAM2), arg2);
+ u64 distance = u64(func) - (u64(code) + 5);
+ if (distance >= 0x0000000080000000ULL
+ && distance < 0xFFFFFFFF80000000ULL) {
+ // Far call
+ MOV(64, R(RAX), ImmPtr(func));
+ CALLptr(R(RAX));
+ } else {
+ CALL(func);
+ }
+}
\ No newline at end of file
--- /dev/null
+// Copyright 2008 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "citraimport\common/bit_set.h"
+#include "emitter.h"
+
+// x64 ABI:s, and helpers to help follow them when JIT-ing code.
+// All convensions return values in EAX (+ possibly EDX).
+
+// Windows 64-bit
+// * 4-reg "fastcall" variant, very new-skool stack handling
+// * Callee moves stack pointer, to make room for shadow regs for the biggest function _it itself calls_
+// * Parameters passed in RCX, RDX, ... further parameters are MOVed into the allocated stack space.
+// Scratch: RAX RCX RDX R8 R9 R10 R11
+// Callee-save: RBX RSI RDI RBP R12 R13 R14 R15
+// Parameters: RCX RDX R8 R9, further MOV-ed
+
+// Linux 64-bit
+// * 6-reg "fastcall" variant, old skool stack handling (parameters are pushed)
+// Scratch: RAX RCX RDX RSI RDI R8 R9 R10 R11
+// Callee-save: RBX RBP R12 R13 R14 R15
+// Parameters: RDI RSI RDX RCX R8 R9
+
+#define ABI_ALL_FPRS BitSet32(0xffff0000)
+#define ABI_ALL_GPRS BitSet32(0x0000ffff)
+
+#ifdef _WIN32 // 64-bit Windows - the really exotic calling convention
+
+#define ABI_PARAM1 RCX
+#define ABI_PARAM2 RDX
+#define ABI_PARAM3 R8
+#define ABI_PARAM4 R9
+
+// xmm0-xmm15 use the upper 16 bits in the functions that push/pop registers.
+#define ABI_ALL_CALLER_SAVED \
+ (BitSet32 { RAX, RCX, RDX, R8, R9, R10, R11, \
+ XMM0+16, XMM1+16, XMM2+16, XMM3+16, XMM4+16, XMM5+16 })
+#else //64-bit Unix / OS X
+
+#define ABI_PARAM1 RDI
+#define ABI_PARAM2 RSI
+#define ABI_PARAM3 RDX
+#define ABI_PARAM4 RCX
+#define ABI_PARAM5 R8
+#define ABI_PARAM6 R9
+
+// TODO: Avoid pushing all 16 XMM registers when possible. Most functions we call probably
+// don't actually clobber them.
+#define ABI_ALL_CALLER_SAVED \
+ (BitSet32 { RAX, RCX, RDX, RDI, RSI, R8, R9, R10, R11 } | \
+ ABI_ALL_FPRS)
+#endif // WIN32
+
+#define ABI_ALL_CALLEE_SAVED (~ABI_ALL_CALLER_SAVED)
+
+#define ABI_RETURN RAX
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include <string>
+#include <thread>
+
+#include "citraimport/common/common_types.h"
+
+#include "cpu_detect.h"
+
+namespace Common {
+
+#ifndef _MSC_VER
+
+#ifdef __FreeBSD__
+#include <sys/types.h>
+#include <machine/cpufunc.h>
+#endif
+
+static inline void __cpuidex(int info[4], int function_id, int subfunction_id) {
+#ifdef __FreeBSD__
+ // Despite the name, this is just do_cpuid() with ECX as second input.
+ cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info);
+#else
+ info[0] = function_id; // eax
+ info[2] = subfunction_id; // ecx
+ __asm__(
+ "cpuid"
+ : "=a" (info[0]),
+ "=b" (info[1]),
+ "=c" (info[2]),
+ "=d" (info[3])
+ : "a" (function_id),
+ "c" (subfunction_id)
+ );
+#endif
+}
+
+static inline void __cpuid(int info[4], int function_id) {
+ return __cpuidex(info, function_id, 0);
+}
+
+#define _XCR_XFEATURE_ENABLED_MASK 0
+static u64 _xgetbv(u32 index) {
+ u32 eax, edx;
+ __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
+ return ((u64)edx << 32) | eax;
+}
+
+#endif // ifndef _MSC_VER
+
+// Detects the various CPU features
+static CPUCaps Detect() {
+ CPUCaps caps = {};
+
+ caps.num_cores = std::thread::hardware_concurrency();
+
+ // Assumes the CPU supports the CPUID instruction. Those that don't would likely not support
+ // Citra at all anyway
+
+ int cpu_id[4];
+ memset(caps.brand_string, 0, sizeof(caps.brand_string));
+
+ // Detect CPU's CPUID capabilities and grab CPU string
+ __cpuid(cpu_id, 0x00000000);
+ u32 max_std_fn = cpu_id[0]; // EAX
+
+ std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
+ std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
+ std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
+
+ __cpuid(cpu_id, 0x80000000);
+
+ u32 max_ex_fn = cpu_id[0];
+ if (!strcmp(caps.brand_string, "GenuineIntel"))
+ caps.vendor = CPUVendor::INTEL;
+ else if (!strcmp(caps.brand_string, "AuthenticAMD"))
+ caps.vendor = CPUVendor::AMD;
+ else
+ caps.vendor = CPUVendor::OTHER;
+
+ // Set reasonable default brand string even if brand string not available
+ strcpy(caps.cpu_string, caps.brand_string);
+
+ // Detect family and other miscellaneous features
+ if (max_std_fn >= 1) {
+ __cpuid(cpu_id, 0x00000001);
+
+ if ((cpu_id[3] >> 25) & 1) caps.sse = true;
+ if ((cpu_id[3] >> 26) & 1) caps.sse2 = true;
+ if ((cpu_id[2]) & 1) caps.sse3 = true;
+ if ((cpu_id[2] >> 9) & 1) caps.ssse3 = true;
+ if ((cpu_id[2] >> 19) & 1) caps.sse4_1 = true;
+ if ((cpu_id[2] >> 20) & 1) caps.sse4_2 = true;
+ if ((cpu_id[2] >> 22) & 1) caps.movbe = true;
+ if ((cpu_id[2] >> 25) & 1) caps.aes = true;
+
+ if ((cpu_id[3] >> 24) & 1) {
+ caps.fxsave_fxrstor = true;
+ }
+
+ // AVX support requires 3 separate checks:
+ // - Is the AVX bit set in CPUID?
+ // - Is the XSAVE bit set in CPUID?
+ // - XGETBV result has the XCR bit set.
+ if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) {
+ if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
+ caps.avx = true;
+ if ((cpu_id[2] >> 12) & 1)
+ caps.fma = true;
+ }
+ }
+
+ if (max_std_fn >= 7) {
+ __cpuidex(cpu_id, 0x00000007, 0x00000000);
+ // Can't enable AVX2 unless the XSAVE/XGETBV checks above passed
+ if ((cpu_id[1] >> 5) & 1)
+ caps.avx2 = caps.avx;
+ if ((cpu_id[1] >> 3) & 1)
+ caps.bmi1 = true;
+ if ((cpu_id[1] >> 8) & 1)
+ caps.bmi2 = true;
+ }
+ }
+
+ caps.flush_to_zero = caps.sse;
+
+ if (max_ex_fn >= 0x80000004) {
+ // Extract CPU model string
+ __cpuid(cpu_id, 0x80000002);
+ std::memcpy(caps.cpu_string, cpu_id, sizeof(cpu_id));
+ __cpuid(cpu_id, 0x80000003);
+ std::memcpy(caps.cpu_string + 16, cpu_id, sizeof(cpu_id));
+ __cpuid(cpu_id, 0x80000004);
+ std::memcpy(caps.cpu_string + 32, cpu_id, sizeof(cpu_id));
+ }
+
+ if (max_ex_fn >= 0x80000001) {
+ // Check for more features
+ __cpuid(cpu_id, 0x80000001);
+ if (cpu_id[2] & 1) caps.lahf_sahf_64 = true;
+ if ((cpu_id[2] >> 5) & 1) caps.lzcnt = true;
+ if ((cpu_id[2] >> 16) & 1) caps.fma4 = true;
+ if ((cpu_id[3] >> 29) & 1) caps.long_mode = true;
+ }
+
+ return caps;
+}
+
+const CPUCaps& GetCPUCaps() {
+ static CPUCaps caps = Detect();
+ return caps;
+}
+
+std::string GetCPUCapsString() {
+ auto caps = GetCPUCaps();
+
+ std::string sum(caps.cpu_string);
+ sum += " (";
+ sum += caps.brand_string;
+ sum += ")";
+
+ if (caps.sse) sum += ", SSE";
+ if (caps.sse2) {
+ sum += ", SSE2";
+ if (!caps.flush_to_zero) sum += " (without DAZ)";
+ }
+
+ if (caps.sse3) sum += ", SSE3";
+ if (caps.ssse3) sum += ", SSSE3";
+ if (caps.sse4_1) sum += ", SSE4.1";
+ if (caps.sse4_2) sum += ", SSE4.2";
+ if (caps.avx) sum += ", AVX";
+ if (caps.avx2) sum += ", AVX2";
+ if (caps.bmi1) sum += ", BMI1";
+ if (caps.bmi2) sum += ", BMI2";
+ if (caps.fma) sum += ", FMA";
+ if (caps.aes) sum += ", AES";
+ if (caps.movbe) sum += ", MOVBE";
+ if (caps.long_mode) sum += ", 64-bit support";
+
+ return sum;
+}
+
+} // namespace Common
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+
+namespace Common {
+
+/// x86/x64 CPU vendors that may be detected by this module
+enum class CPUVendor {
+ INTEL,
+ AMD,
+ OTHER,
+};
+
+/// x86/x64 CPU capabilities that may be detected by this module
+struct CPUCaps {
+ CPUVendor vendor;
+ char cpu_string[0x21];
+ char brand_string[0x41];
+ int num_cores;
+ bool sse;
+ bool sse2;
+ bool sse3;
+ bool ssse3;
+ bool sse4_1;
+ bool sse4_2;
+ bool lzcnt;
+ bool avx;
+ bool avx2;
+ bool bmi1;
+ bool bmi2;
+ bool fma;
+ bool fma4;
+ bool aes;
+
+ // Support for the FXSAVE and FXRSTOR instructions
+ bool fxsave_fxrstor;
+
+ bool movbe;
+
+ // This flag indicates that the hardware supports some mode in which denormal inputs and outputs
+ // are automatically set to (signed) zero.
+ bool flush_to_zero;
+
+ // Support for LAHF and SAHF instructions in 64-bit mode
+ bool lahf_sahf_64;
+
+ bool long_mode;
+};
+
+/**
+ * Gets the supported capabilities of the host CPU
+ * @return Reference to a CPUCaps struct with the detected host CPU capabilities
+ */
+const CPUCaps& GetCPUCaps();
+
+/**
+ * Gets a string summary of the name and supported capabilities of the host CPU
+ * @return String summary
+ */
+std::string GetCPUCapsString();
+
+} // namespace Common
--- /dev/null
+// Copyright (C) 2003 Dolphin Project.
+
+// 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, version 2.0 or later versions.
+
+// 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 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official SVN repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#include <cinttypes>
+#include <cstring>
+#include <cassert>
+
+#include "citraimport/common/assert.h"
+#include "citraimport/common/logging/log.h"
+#include "citraimport/common/memory_util.h"
+
+#include "abi.h"
+#include "cpu_detect.h"
+#include "emitter.h"
+
+namespace Gen
+{
+
+struct NormalOpDef
+{
+ u8 toRm8, toRm32, fromRm8, fromRm32, imm8, imm32, simm8, eaximm8, eaximm32, ext;
+};
+
+// 0xCC is code for invalid combination of immediates
+static const NormalOpDef normalops[11] =
+{
+ {0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x83, 0x04, 0x05, 0}, //ADD
+ {0x10, 0x11, 0x12, 0x13, 0x80, 0x81, 0x83, 0x14, 0x15, 2}, //ADC
+
+ {0x28, 0x29, 0x2A, 0x2B, 0x80, 0x81, 0x83, 0x2C, 0x2D, 5}, //SUB
+ {0x18, 0x19, 0x1A, 0x1B, 0x80, 0x81, 0x83, 0x1C, 0x1D, 3}, //SBB
+
+ {0x20, 0x21, 0x22, 0x23, 0x80, 0x81, 0x83, 0x24, 0x25, 4}, //AND
+ {0x08, 0x09, 0x0A, 0x0B, 0x80, 0x81, 0x83, 0x0C, 0x0D, 1}, //OR
+
+ {0x30, 0x31, 0x32, 0x33, 0x80, 0x81, 0x83, 0x34, 0x35, 6}, //XOR
+ {0x88, 0x89, 0x8A, 0x8B, 0xC6, 0xC7, 0xCC, 0xCC, 0xCC, 0}, //MOV
+
+ {0x84, 0x85, 0x84, 0x85, 0xF6, 0xF7, 0xCC, 0xA8, 0xA9, 0}, //TEST (to == from)
+ {0x38, 0x39, 0x3A, 0x3B, 0x80, 0x81, 0x83, 0x3C, 0x3D, 7}, //CMP
+
+ {0x86, 0x87, 0x86, 0x87, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 7}, //XCHG
+};
+
+enum NormalSSEOps
+{
+ sseCMP = 0xC2,
+ sseADD = 0x58, //ADD
+ sseSUB = 0x5C, //SUB
+ sseAND = 0x54, //AND
+ sseANDN = 0x55, //ANDN
+ sseOR = 0x56,
+ sseXOR = 0x57,
+ sseMUL = 0x59, //MUL
+ sseDIV = 0x5E, //DIV
+ sseMIN = 0x5D, //MIN
+ sseMAX = 0x5F, //MAX
+ sseCOMIS = 0x2F, //COMIS
+ sseUCOMIS = 0x2E, //UCOMIS
+ sseSQRT = 0x51, //SQRT
+ sseRSQRT = 0x52, //RSQRT (NO DOUBLE PRECISION!!!)
+ sseRCP = 0x53, //RCP
+ sseMOVAPfromRM = 0x28, //MOVAP from RM
+ sseMOVAPtoRM = 0x29, //MOVAP to RM
+ sseMOVUPfromRM = 0x10, //MOVUP from RM
+ sseMOVUPtoRM = 0x11, //MOVUP to RM
+ sseMOVLPfromRM= 0x12,
+ sseMOVLPtoRM = 0x13,
+ sseMOVHPfromRM= 0x16,
+ sseMOVHPtoRM = 0x17,
+ sseMOVHLPS = 0x12,
+ sseMOVLHPS = 0x16,
+ sseMOVDQfromRM = 0x6F,
+ sseMOVDQtoRM = 0x7F,
+ sseMASKMOVDQU = 0xF7,
+ sseLDDQU = 0xF0,
+ sseSHUF = 0xC6,
+ sseMOVNTDQ = 0xE7,
+ sseMOVNTP = 0x2B,
+ sseHADD = 0x7C,
+};
+
+
+void XEmitter::SetCodePtr(u8 *ptr)
+{
+ code = ptr;
+}
+
+const u8 *XEmitter::GetCodePtr() const
+{
+ return code;
+}
+
+u8 *XEmitter::GetWritableCodePtr()
+{
+ return code;
+}
+
+void XEmitter::Write8(u8 value)
+{
+ *code++ = value;
+}
+
+void XEmitter::Write16(u16 value)
+{
+ std::memcpy(code, &value, sizeof(u16));
+ code += sizeof(u16);
+}
+
+void XEmitter::Write32(u32 value)
+{
+ std::memcpy(code, &value, sizeof(u32));
+ code += sizeof(u32);
+}
+
+void XEmitter::Write64(u64 value)
+{
+ std::memcpy(code, &value, sizeof(u64));
+ code += sizeof(u64);
+}
+
+void XEmitter::ReserveCodeSpace(int bytes)
+{
+ for (int i = 0; i < bytes; i++)
+ *code++ = 0xCC;
+}
+
+const u8 *XEmitter::AlignCode4()
+{
+ int c = int((u64)code & 3);
+ if (c)
+ ReserveCodeSpace(4-c);
+ return code;
+}
+
+const u8 *XEmitter::AlignCode16()
+{
+ int c = int((u64)code & 15);
+ if (c)
+ ReserveCodeSpace(16-c);
+ return code;
+}
+
+const u8 *XEmitter::AlignCodePage()
+{
+ int c = int((u64)code & 4095);
+ if (c)
+ ReserveCodeSpace(4096-c);
+ return code;
+}
+
+// This operation modifies flags; check to see the flags are locked.
+// If the flags are locked, we should immediately and loudly fail before
+// causing a subtle JIT bug.
+void XEmitter::CheckFlags()
+{
+ assert(!flags_locked, "Attempt to modify flags while flags locked!");
+}
+
+void XEmitter::WriteModRM(int mod, int reg, int rm)
+{
+ Write8((u8)((mod << 6) | ((reg & 7) << 3) | (rm & 7)));
+}
+
+void XEmitter::WriteSIB(int scale, int index, int base)
+{
+ Write8((u8)((scale << 6) | ((index & 7) << 3) | (base & 7)));
+}
+
+void OpArg::WriteRex(XEmitter *emit, int opBits, int bits, int customOp) const
+{
+ if (customOp == -1) customOp = operandReg;
+#ifdef ARCHITECTURE_x86_64
+ u8 op = 0x40;
+ // REX.W (whether operation is a 64-bit operation)
+ if (opBits == 64) op |= 8;
+ // REX.R (whether ModR/M reg field refers to R8-R15.
+ if (customOp & 8) op |= 4;
+ // REX.X (whether ModR/M SIB index field refers to R8-R15)
+ if (indexReg & 8) op |= 2;
+ // REX.B (whether ModR/M rm or SIB base or opcode reg field refers to R8-R15)
+ if (offsetOrBaseReg & 8) op |= 1;
+ // Write REX if wr have REX bits to write, or if the operation accesses
+ // SIL, DIL, BPL, or SPL.
+ if (op != 0x40 ||
+ (scale == SCALE_NONE && bits == 8 && (offsetOrBaseReg & 0x10c) == 4) ||
+ (opBits == 8 && (customOp & 0x10c) == 4))
+ {
+ emit->Write8(op);
+ // Check the operation doesn't access AH, BH, CH, or DH.
+ DEBUG_ASSERT((offsetOrBaseReg & 0x100) == 0);
+ DEBUG_ASSERT((customOp & 0x100) == 0);
+ }
+#else
+ assert(opBits != 64);
+ assert((customOp & 8) == 0 || customOp == -1);
+ assert((indexReg & 8) == 0);
+ assert((offsetOrBaseReg & 8) == 0);
+ assert(opBits != 8 || (customOp & 0x10c) != 4 || customOp == -1);
+ assert(scale == SCALE_ATREG || bits != 8 || (offsetOrBaseReg & 0x10c) != 4);
+#endif
+}
+
+void OpArg::WriteVex(XEmitter* emit, X64Reg regOp1, X64Reg regOp2, int L, int pp, int mmmmm, int W) const
+{
+ int R = !(regOp1 & 8);
+ int X = !(indexReg & 8);
+ int B = !(offsetOrBaseReg & 8);
+
+ int vvvv = (regOp2 == X64Reg::INVALID_REG) ? 0xf : (regOp2 ^ 0xf);
+
+ // do we need any VEX fields that only appear in the three-byte form?
+ if (X == 1 && B == 1 && W == 0 && mmmmm == 1)
+ {
+ u8 RvvvvLpp = (R << 7) | (vvvv << 3) | (L << 1) | pp;
+ emit->Write8(0xC5);
+ emit->Write8(RvvvvLpp);
+ }
+ else
+ {
+ u8 RXBmmmmm = (R << 7) | (X << 6) | (B << 5) | mmmmm;
+ u8 WvvvvLpp = (W << 7) | (vvvv << 3) | (L << 1) | pp;
+ emit->Write8(0xC4);
+ emit->Write8(RXBmmmmm);
+ emit->Write8(WvvvvLpp);
+ }
+}
+
+void OpArg::WriteRest(XEmitter *emit, int extraBytes, X64Reg _operandReg,
+ bool warn_64bit_offset) const
+{
+ if (_operandReg == INVALID_REG)
+ _operandReg = (X64Reg)this->operandReg;
+ int mod = 0;
+ int ireg = indexReg;
+ bool SIB = false;
+ int _offsetOrBaseReg = this->offsetOrBaseReg;
+
+ if (scale == SCALE_RIP) //Also, on 32-bit, just an immediate address
+ {
+ // Oh, RIP addressing.
+ _offsetOrBaseReg = 5;
+ emit->WriteModRM(0, _operandReg, _offsetOrBaseReg);
+ //TODO : add some checks
+#ifdef ARCHITECTURE_x86_64
+ u64 ripAddr = (u64)emit->GetCodePtr() + 4 + extraBytes;
+ s64 distance = (s64)offset - (s64)ripAddr;
+ assert(
+ (distance < 0x80000000LL &&
+ distance >= -0x80000000LL) ||
+ !warn_64bit_offset,
+ "WriteRest: op out of range (0x%" PRIx64 " uses 0x%" PRIx64 ")",
+ ripAddr, offset);
+ s32 offs = (s32)distance;
+ emit->Write32((u32)offs);
+#else
+ emit->Write32((u32)offset);
+#endif
+ return;
+ }
+
+ if (scale == 0)
+ {
+ // Oh, no memory, Just a reg.
+ mod = 3; //11
+ }
+ else if (scale >= 1)
+ {
+ //Ah good, no scaling.
+ if (scale == SCALE_ATREG && !((_offsetOrBaseReg & 7) == 4 || (_offsetOrBaseReg & 7) == 5))
+ {
+ //Okay, we're good. No SIB necessary.
+ int ioff = (int)offset;
+ if (ioff == 0)
+ {
+ mod = 0;
+ }
+ else if (ioff<-128 || ioff>127)
+ {
+ mod = 2; //32-bit displacement
+ }
+ else
+ {
+ mod = 1; //8-bit displacement
+ }
+ }
+ else if (scale >= SCALE_NOBASE_2 && scale <= SCALE_NOBASE_8)
+ {
+ SIB = true;
+ mod = 0;
+ _offsetOrBaseReg = 5;
+ }
+ else //if (scale != SCALE_ATREG)
+ {
+ if ((_offsetOrBaseReg & 7) == 4) //this would occupy the SIB encoding :(
+ {
+ //So we have to fake it with SIB encoding :(
+ SIB = true;
+ }
+
+ if (scale >= SCALE_1 && scale < SCALE_ATREG)
+ {
+ SIB = true;
+ }
+
+ if (scale == SCALE_ATREG && ((_offsetOrBaseReg & 7) == 4))
+ {
+ SIB = true;
+ ireg = _offsetOrBaseReg;
+ }
+
+ //Okay, we're fine. Just disp encoding.
+ //We need displacement. Which size?
+ int ioff = (int)(s64)offset;
+ if (ioff < -128 || ioff > 127)
+ {
+ mod = 2; //32-bit displacement
+ }
+ else
+ {
+ mod = 1; //8-bit displacement
+ }
+ }
+ }
+
+ // Okay. Time to do the actual writing
+ // ModRM byte:
+ int oreg = _offsetOrBaseReg;
+ if (SIB)
+ oreg = 4;
+
+ // TODO(ector): WTF is this if about? I don't remember writing it :-)
+ //if (RIP)
+ // oreg = 5;
+
+ emit->WriteModRM(mod, _operandReg&7, oreg&7);
+
+ if (SIB)
+ {
+ //SIB byte
+ int ss;
+ switch (scale)
+ {
+ case SCALE_NONE: _offsetOrBaseReg = 4; ss = 0; break; //RSP
+ case SCALE_1: ss = 0; break;
+ case SCALE_2: ss = 1; break;
+ case SCALE_4: ss = 2; break;
+ case SCALE_8: ss = 3; break;
+ case SCALE_NOBASE_2: ss = 1; break;
+ case SCALE_NOBASE_4: ss = 2; break;
+ case SCALE_NOBASE_8: ss = 3; break;
+ case SCALE_ATREG: ss = 0; break;
+ default: assert(0, "Invalid scale for SIB byte"); ss = 0; break;
+ }
+ emit->Write8((u8)((ss << 6) | ((ireg&7)<<3) | (_offsetOrBaseReg&7)));
+ }
+
+ if (mod == 1) //8-bit disp
+ {
+ emit->Write8((u8)(s8)(s32)offset);
+ }
+ else if (mod == 2 || (scale >= SCALE_NOBASE_2 && scale <= SCALE_NOBASE_8)) //32-bit disp
+ {
+ emit->Write32((u32)offset);
+ }
+}
+
+// W = operand extended width (1 if 64-bit)
+// R = register# upper bit
+// X = scale amnt upper bit
+// B = base register# upper bit
+void XEmitter::Rex(int w, int r, int x, int b)
+{
+ w = w ? 1 : 0;
+ r = r ? 1 : 0;
+ x = x ? 1 : 0;
+ b = b ? 1 : 0;
+ u8 rx = (u8)(0x40 | (w << 3) | (r << 2) | (x << 1) | (b));
+ if (rx != 0x40)
+ Write8(rx);
+}
+
+void XEmitter::JMP(const u8* addr, bool force5Bytes)
+{
+ u64 fn = (u64)addr;
+ if (!force5Bytes)
+ {
+ s64 distance = (s64)(fn - ((u64)code + 2));
+ assert(distance >= -0x80 && distance < 0x80,
+ "Jump target too far away, needs force5Bytes = true");
+ //8 bits will do
+ Write8(0xEB);
+ Write8((u8)(s8)distance);
+ }
+ else
+ {
+ s64 distance = (s64)(fn - ((u64)code + 5));
+
+ assert(
+ distance >= -0x80000000LL && distance < 0x80000000LL,
+ "Jump target too far away, needs indirect register");
+ Write8(0xE9);
+ Write32((u32)(s32)distance);
+ }
+}
+
+void XEmitter::JMPptr(const OpArg& arg2)
+{
+ OpArg arg = arg2;
+ if (arg.IsImm()) assert(0, "JMPptr - Imm argument");
+ arg.operandReg = 4;
+ arg.WriteRex(this, 0, 0);
+ Write8(0xFF);
+ arg.WriteRest(this);
+}
+
+//Can be used to trap other processors, before overwriting their code
+// not used in dolphin
+void XEmitter::JMPself()
+{
+ Write8(0xEB);
+ Write8(0xFE);
+}
+
+void XEmitter::CALLptr(OpArg arg)
+{
+ if (arg.IsImm()) assert(0, "CALLptr - Imm argument");
+ arg.operandReg = 2;
+ arg.WriteRex(this, 0, 0);
+ Write8(0xFF);
+ arg.WriteRest(this);
+}
+
+void XEmitter::CALL(const void* fnptr)
+{
+ u64 distance = u64(fnptr) - (u64(code) + 5);
+ assert(
+ distance < 0x0000000080000000ULL ||
+ distance >= 0xFFFFFFFF80000000ULL,
+ "CALL out of range (%p calls %p)", code, fnptr);
+ Write8(0xE8);
+ Write32(u32(distance));
+}
+
+FixupBranch XEmitter::J(bool force5bytes)
+{
+ FixupBranch branch;
+ branch.type = force5bytes ? 1 : 0;
+ branch.ptr = code + (force5bytes ? 5 : 2);
+ if (!force5bytes)
+ {
+ //8 bits will do
+ Write8(0xEB);
+ Write8(0);
+ }
+ else
+ {
+ Write8(0xE9);
+ Write32(0);
+ }
+ return branch;
+}
+
+FixupBranch XEmitter::J_CC(CCFlags conditionCode, bool force5bytes)
+{
+ FixupBranch branch;
+ branch.type = force5bytes ? 1 : 0;
+ branch.ptr = code + (force5bytes ? 6 : 2);
+ if (!force5bytes)
+ {
+ //8 bits will do
+ Write8(0x70 + conditionCode);
+ Write8(0);
+ }
+ else
+ {
+ Write8(0x0F);
+ Write8(0x80 + conditionCode);
+ Write32(0);
+ }
+ return branch;
+}
+
+void XEmitter::J_CC(CCFlags conditionCode, const u8* addr, bool force5bytes)
+{
+ u64 fn = (u64)addr;
+ s64 distance = (s64)(fn - ((u64)code + 2));
+ if (distance < -0x80 || distance >= 0x80 || force5bytes)
+ {
+ distance = (s64)(fn - ((u64)code + 6));
+ assert(
+ distance >= -0x80000000LL && distance < 0x80000000LL,
+ "Jump target too far away, needs indirect register");
+ Write8(0x0F);
+ Write8(0x80 + conditionCode);
+ Write32((u32)(s32)distance);
+ }
+ else
+ {
+ Write8(0x70 + conditionCode);
+ Write8((u8)(s8)distance);
+ }
+}
+
+void XEmitter::SetJumpTarget(const FixupBranch& branch)
+{
+ if (branch.type == 0)
+ {
+ s64 distance = (s64)(code - branch.ptr);
+ assert(distance >= -0x80 && distance < 0x80, "Jump target too far away, needs force5Bytes = true");
+ branch.ptr[-1] = (u8)(s8)distance;
+ }
+ else if (branch.type == 1)
+ {
+ s64 distance = (s64)(code - branch.ptr);
+ assert(distance >= -0x80000000LL && distance < 0x80000000LL, "Jump target too far away, needs indirect register");
+ ((s32*)branch.ptr)[-1] = (s32)distance;
+ }
+}
+
+//Single byte opcodes
+//There is no PUSHAD/POPAD in 64-bit mode.
+void XEmitter::INT3() {Write8(0xCC);}
+void XEmitter::RET() {Write8(0xC3);}
+void XEmitter::RET_FAST() {Write8(0xF3); Write8(0xC3);} //two-byte return (rep ret) - recommended by AMD optimization manual for the case of jumping to a ret
+
+// The first sign of decadence: optimized NOPs.
+void XEmitter::NOP(size_t size)
+{
+ assert((int)size > 0);
+ while (true)
+ {
+ switch (size)
+ {
+ case 0:
+ return;
+ case 1:
+ Write8(0x90);
+ return;
+ case 2:
+ Write8(0x66); Write8(0x90);
+ return;
+ case 3:
+ Write8(0x0F); Write8(0x1F); Write8(0x00);
+ return;
+ case 4:
+ Write8(0x0F); Write8(0x1F); Write8(0x40); Write8(0x00);
+ return;
+ case 5:
+ Write8(0x0F); Write8(0x1F); Write8(0x44); Write8(0x00);
+ Write8(0x00);
+ return;
+ case 6:
+ Write8(0x66); Write8(0x0F); Write8(0x1F); Write8(0x44);
+ Write8(0x00); Write8(0x00);
+ return;
+ case 7:
+ Write8(0x0F); Write8(0x1F); Write8(0x80); Write8(0x00);
+ Write8(0x00); Write8(0x00); Write8(0x00);
+ return;
+ case 8:
+ Write8(0x0F); Write8(0x1F); Write8(0x84); Write8(0x00);
+ Write8(0x00); Write8(0x00); Write8(0x00); Write8(0x00);
+ return;
+ case 9:
+ Write8(0x66); Write8(0x0F); Write8(0x1F); Write8(0x84);
+ Write8(0x00); Write8(0x00); Write8(0x00); Write8(0x00);
+ Write8(0x00);
+ return;
+ case 10:
+ Write8(0x66); Write8(0x66); Write8(0x0F); Write8(0x1F);
+ Write8(0x84); Write8(0x00); Write8(0x00); Write8(0x00);
+ Write8(0x00); Write8(0x00);
+ return;
+ default:
+ // Even though x86 instructions are allowed to be up to 15 bytes long,
+ // AMD advises against using NOPs longer than 11 bytes because they
+ // carry a performance penalty on CPUs older than AMD family 16h.
+ Write8(0x66); Write8(0x66); Write8(0x66); Write8(0x0F);
+ Write8(0x1F); Write8(0x84); Write8(0x00); Write8(0x00);
+ Write8(0x00); Write8(0x00); Write8(0x00);
+ size -= 11;
+ continue;
+ }
+ }
+}
+
+void XEmitter::PAUSE() {Write8(0xF3); NOP();} //use in tight spinloops for energy saving on some cpu
+void XEmitter::CLC() {CheckFlags(); Write8(0xF8);} //clear carry
+void XEmitter::CMC() {CheckFlags(); Write8(0xF5);} //flip carry
+void XEmitter::STC() {CheckFlags(); Write8(0xF9);} //set carry
+
+//TODO: xchg ah, al ???
+void XEmitter::XCHG_AHAL()
+{
+ Write8(0x86);
+ Write8(0xe0);
+ // alt. 86 c4
+}
+
+//These two can not be executed on early Intel 64-bit CPU:s, only on AMD!
+void XEmitter::LAHF() {Write8(0x9F);}
+void XEmitter::SAHF() {CheckFlags(); Write8(0x9E);}
+
+void XEmitter::PUSHF() {Write8(0x9C);}
+void XEmitter::POPF() {CheckFlags(); Write8(0x9D);}
+
+void XEmitter::LFENCE() {Write8(0x0F); Write8(0xAE); Write8(0xE8);}
+void XEmitter::MFENCE() {Write8(0x0F); Write8(0xAE); Write8(0xF0);}
+void XEmitter::SFENCE() {Write8(0x0F); Write8(0xAE); Write8(0xF8);}
+
+void XEmitter::WriteSimple1Byte(int bits, u8 byte, X64Reg reg)
+{
+ if (bits == 16)
+ Write8(0x66);
+ Rex(bits == 64, 0, 0, (int)reg >> 3);
+ Write8(byte + ((int)reg & 7));
+}
+
+void XEmitter::WriteSimple2Byte(int bits, u8 byte1, u8 byte2, X64Reg reg)
+{
+ if (bits == 16)
+ Write8(0x66);
+ Rex(bits==64, 0, 0, (int)reg >> 3);
+ Write8(byte1);
+ Write8(byte2 + ((int)reg & 7));
+}
+
+void XEmitter::CWD(int bits)
+{
+ if (bits == 16)
+ Write8(0x66);
+ Rex(bits == 64, 0, 0, 0);
+ Write8(0x99);
+}
+
+void XEmitter::CBW(int bits)
+{
+ if (bits == 8)
+ Write8(0x66);
+ Rex(bits == 32, 0, 0, 0);
+ Write8(0x98);
+}
+
+//Simple opcodes
+
+
+//push/pop do not need wide to be 64-bit
+void XEmitter::PUSH(X64Reg reg) {WriteSimple1Byte(32, 0x50, reg);}
+void XEmitter::POP(X64Reg reg) {WriteSimple1Byte(32, 0x58, reg);}
+
+void XEmitter::PUSH(int bits, const OpArg& reg)
+{
+ if (reg.IsSimpleReg())
+ PUSH(reg.GetSimpleReg());
+ else if (reg.IsImm())
+ {
+ switch (reg.GetImmBits())
+ {
+ case 8:
+ Write8(0x6A);
+ Write8((u8)(s8)reg.offset);
+ break;
+ case 16:
+ Write8(0x66);
+ Write8(0x68);
+ Write16((u16)(s16)(s32)reg.offset);
+ break;
+ case 32:
+ Write8(0x68);
+ Write32((u32)reg.offset);
+ break;
+ default:
+ assert(0, "PUSH - Bad imm bits");
+ break;
+ }
+ }
+ else
+ {
+ if (bits == 16)
+ Write8(0x66);
+ reg.WriteRex(this, bits, bits);
+ Write8(0xFF);
+ reg.WriteRest(this, 0, (X64Reg)6);
+ }
+}
+
+void XEmitter::POP(int /*bits*/, const OpArg& reg)
+{
+ if (reg.IsSimpleReg())
+ POP(reg.GetSimpleReg());
+ else
+ assert(0, "POP - Unsupported encoding");
+}
+
+void XEmitter::BSWAP(int bits, X64Reg reg)
+{
+ if (bits >= 32)
+ {
+ WriteSimple2Byte(bits, 0x0F, 0xC8, reg);
+ }
+ else if (bits == 16)
+ {
+ ROL(16, R(reg), Imm8(8));
+ }
+ else if (bits == 8)
+ {
+ // Do nothing - can't bswap a single byte...
+ }
+ else
+ {
+ assert(0, "BSWAP - Wrong number of bits");
+ }
+}
+
+// Undefined opcode - reserved
+// If we ever need a way to always cause a non-breakpoint hard exception...
+void XEmitter::UD2()
+{
+ Write8(0x0F);
+ Write8(0x0B);
+}
+
+void XEmitter::PREFETCH(PrefetchLevel level, OpArg arg)
+{
+ assert(!arg.IsImm(), "PREFETCH - Imm argument");
+ arg.operandReg = (u8)level;
+ arg.WriteRex(this, 0, 0);
+ Write8(0x0F);
+ Write8(0x18);
+ arg.WriteRest(this);
+}
+
+void XEmitter::SETcc(CCFlags flag, OpArg dest)
+{
+ assert(!dest.IsImm(), "SETcc - Imm argument");
+ dest.operandReg = 0;
+ dest.WriteRex(this, 0, 8);
+ Write8(0x0F);
+ Write8(0x90 + (u8)flag);
+ dest.WriteRest(this);
+}
+
+void XEmitter::CMOVcc(int bits, X64Reg dest, OpArg src, CCFlags flag)
+{
+ assert(!src.IsImm(), "CMOVcc - Imm argument");
+ assert(bits != 8, "CMOVcc - 8 bits unsupported");
+ if (bits == 16)
+ Write8(0x66);
+ src.operandReg = dest;
+ src.WriteRex(this, bits, bits);
+ Write8(0x0F);
+ Write8(0x40 + (u8)flag);
+ src.WriteRest(this);
+}
+
+void XEmitter::WriteMulDivType(int bits, OpArg src, int ext)
+{
+ assert(!src.IsImm(), "WriteMulDivType - Imm argument");
+ CheckFlags();
+ src.operandReg = ext;
+ if (bits == 16)
+ Write8(0x66);
+ src.WriteRex(this, bits, bits, 0);
+ if (bits == 8)
+ {
+ Write8(0xF6);
+ }
+ else
+ {
+ Write8(0xF7);
+ }
+ src.WriteRest(this);
+}
+
+void XEmitter::MUL(int bits, const OpArg& src) {WriteMulDivType(bits, src, 4);}
+void XEmitter::DIV(int bits, const OpArg& src) {WriteMulDivType(bits, src, 6);}
+void XEmitter::IMUL(int bits, const OpArg& src) {WriteMulDivType(bits, src, 5);}
+void XEmitter::IDIV(int bits, const OpArg& src) {WriteMulDivType(bits, src, 7);}
+void XEmitter::NEG(int bits, const OpArg& src) {WriteMulDivType(bits, src, 3);}
+void XEmitter::NOT(int bits, const OpArg& src) {WriteMulDivType(bits, src, 2);}
+
+void XEmitter::WriteBitSearchType(int bits, X64Reg dest, OpArg src, u8 byte2, bool rep)
+{
+ assert(!src.IsImm(), "WriteBitSearchType - Imm argument");
+ CheckFlags();
+ src.operandReg = (u8)dest;
+ if (bits == 16)
+ Write8(0x66);
+ if (rep)
+ Write8(0xF3);
+ src.WriteRex(this, bits, bits);
+ Write8(0x0F);
+ Write8(byte2);
+ src.WriteRest(this);
+}
+
+void XEmitter::MOVNTI(int bits, const OpArg& dest, X64Reg src)
+{
+ if (bits <= 16)
+ assert(0, "MOVNTI - bits<=16");
+ WriteBitSearchType(bits, src, dest, 0xC3);
+}
+
+void XEmitter::BSF(int bits, X64Reg dest, const OpArg& src) {WriteBitSearchType(bits,dest,src,0xBC);} // Bottom bit to top bit
+void XEmitter::BSR(int bits, X64Reg dest, const OpArg& src) {WriteBitSearchType(bits,dest,src,0xBD);} // Top bit to bottom bit
+
+void XEmitter::TZCNT(int bits, X64Reg dest, const OpArg& src)
+{
+ CheckFlags();
+ if (!Common::GetCPUCaps().bmi1)
+ assert(0, "Trying to use BMI1 on a system that doesn't support it. Bad programmer.");
+ WriteBitSearchType(bits, dest, src, 0xBC, true);
+}
+void XEmitter::LZCNT(int bits, X64Reg dest, const OpArg& src)
+{
+ CheckFlags();
+ if (!Common::GetCPUCaps().lzcnt)
+ assert(0, "Trying to use LZCNT on a system that doesn't support it. Bad programmer.");
+ WriteBitSearchType(bits, dest, src, 0xBD, true);
+}
+
+void XEmitter::MOVSX(int dbits, int sbits, X64Reg dest, OpArg src)
+{
+ assert(!src.IsImm(), "MOVSX - Imm argument");
+ if (dbits == sbits)
+ {
+ MOV(dbits, R(dest), src);
+ return;
+ }
+ src.operandReg = (u8)dest;
+ if (dbits == 16)
+ Write8(0x66);
+ src.WriteRex(this, dbits, sbits);
+ if (sbits == 8)
+ {
+ Write8(0x0F);
+ Write8(0xBE);
+ }
+ else if (sbits == 16)
+ {
+ Write8(0x0F);
+ Write8(0xBF);
+ }
+ else if (sbits == 32 && dbits == 64)
+ {
+ Write8(0x63);
+ }
+ else
+ {
+ Crash();
+ }
+ src.WriteRest(this);
+}
+
+void XEmitter::MOVZX(int dbits, int sbits, X64Reg dest, OpArg src)
+{
+ assert(!src.IsImm(), "MOVZX - Imm argument");
+ if (dbits == sbits)
+ {
+ MOV(dbits, R(dest), src);
+ return;
+ }
+ src.operandReg = (u8)dest;
+ if (dbits == 16)
+ Write8(0x66);
+ //the 32bit result is automatically zero extended to 64bit
+ src.WriteRex(this, dbits == 64 ? 32 : dbits, sbits);
+ if (sbits == 8)
+ {
+ Write8(0x0F);
+ Write8(0xB6);
+ }
+ else if (sbits == 16)
+ {
+ Write8(0x0F);
+ Write8(0xB7);
+ }
+ else if (sbits == 32 && dbits == 64)
+ {
+ Write8(0x8B);
+ }
+ else
+ {
+ assert(0, "MOVZX - Invalid size");
+ }
+ src.WriteRest(this);
+}
+
+void XEmitter::MOVBE(int bits, const OpArg& dest, const OpArg& src)
+{
+ assert(Common::GetCPUCaps().movbe, "Generating MOVBE on a system that does not support it.");
+ if (bits == 8)
+ {
+ MOV(bits, dest, src);
+ return;
+ }
+
+ if (bits == 16)
+ Write8(0x66);
+
+ if (dest.IsSimpleReg())
+ {
+ assert(!src.IsSimpleReg() && !src.IsImm(), "MOVBE: Loading from !mem");
+ src.WriteRex(this, bits, bits, dest.GetSimpleReg());
+ Write8(0x0F); Write8(0x38); Write8(0xF0);
+ src.WriteRest(this, 0, dest.GetSimpleReg());
+ }
+ else if (src.IsSimpleReg())
+ {
+ assert(!dest.IsSimpleReg() && !dest.IsImm(), "MOVBE: Storing to !mem");
+ dest.WriteRex(this, bits, bits, src.GetSimpleReg());
+ Write8(0x0F); Write8(0x38); Write8(0xF1);
+ dest.WriteRest(this, 0, src.GetSimpleReg());
+ }
+ else
+ {
+ assert(0, "MOVBE: Not loading or storing to mem");
+ }
+}
+
+
+void XEmitter::LEA(int bits, X64Reg dest, OpArg src)
+{
+ assert(!src.IsImm(), "LEA - Imm argument");
+ src.operandReg = (u8)dest;
+ if (bits == 16)
+ Write8(0x66); //TODO: performance warning
+ src.WriteRex(this, bits, bits);
+ Write8(0x8D);
+ src.WriteRest(this, 0, INVALID_REG, bits == 64);
+}
+
+//shift can be either imm8 or cl
+void XEmitter::WriteShift(int bits, OpArg dest, const OpArg& shift, int ext)
+{
+ CheckFlags();
+ bool writeImm = false;
+ if (dest.IsImm())
+ {
+ assert(0, "WriteShift - can't shift imms");
+ }
+ if ((shift.IsSimpleReg() && shift.GetSimpleReg() != ECX) || (shift.IsImm() && shift.GetImmBits() != 8))
+ {
+ assert(0, "WriteShift - illegal argument");
+ }
+ dest.operandReg = ext;
+ if (bits == 16)
+ Write8(0x66);
+ dest.WriteRex(this, bits, bits, 0);
+ if (shift.GetImmBits() == 8)
+ {
+ //ok an imm
+ u8 imm = (u8)shift.offset;
+ if (imm == 1)
+ {
+ Write8(bits == 8 ? 0xD0 : 0xD1);
+ }
+ else
+ {
+ writeImm = true;
+ Write8(bits == 8 ? 0xC0 : 0xC1);
+ }
+ }
+ else
+ {
+ Write8(bits == 8 ? 0xD2 : 0xD3);
+ }
+ dest.WriteRest(this, writeImm ? 1 : 0);
+ if (writeImm)
+ Write8((u8)shift.offset);
+}
+
+// large rotates and shift are slower on intel than amd
+// intel likes to rotate by 1, and the op is smaller too
+void XEmitter::ROL(int bits, const OpArg& dest, const OpArg& shift) {WriteShift(bits, dest, shift, 0);}
+void XEmitter::ROR(int bits, const OpArg& dest, const OpArg& shift) {WriteShift(bits, dest, shift, 1);}
+void XEmitter::RCL(int bits, const OpArg& dest, const OpArg& shift) {WriteShift(bits, dest, shift, 2);}
+void XEmitter::RCR(int bits, const OpArg& dest, const OpArg& shift) {WriteShift(bits, dest, shift, 3);}
+void XEmitter::SHL(int bits, const OpArg& dest, const OpArg& shift) {WriteShift(bits, dest, shift, 4);}
+void XEmitter::SHR(int bits, const OpArg& dest, const OpArg& shift) {WriteShift(bits, dest, shift, 5);}
+void XEmitter::SAR(int bits, const OpArg& dest, const OpArg& shift) {WriteShift(bits, dest, shift, 7);}
+
+// index can be either imm8 or register, don't use memory destination because it's slow
+void XEmitter::WriteBitTest(int bits, const OpArg& dest, const OpArg& index, int ext)
+{
+ CheckFlags();
+ if (dest.IsImm())
+ {
+ assert(0, "WriteBitTest - can't test imms");
+ }
+ if ((index.IsImm() && index.GetImmBits() != 8))
+ {
+ assert(0, "WriteBitTest - illegal argument");
+ }
+ if (bits == 16)
+ Write8(0x66);
+ if (index.IsImm())
+ {
+ dest.WriteRex(this, bits, bits);
+ Write8(0x0F); Write8(0xBA);
+ dest.WriteRest(this, 1, (X64Reg)ext);
+ Write8((u8)index.offset);
+ }
+ else
+ {
+ X64Reg operand = index.GetSimpleReg();
+ dest.WriteRex(this, bits, bits, operand);
+ Write8(0x0F); Write8(0x83 + 8*ext);
+ dest.WriteRest(this, 1, operand);
+ }
+}
+
+void XEmitter::BT(int bits, const OpArg& dest, const OpArg& index) {WriteBitTest(bits, dest, index, 4);}
+void XEmitter::BTS(int bits, const OpArg& dest, const OpArg& index) {WriteBitTest(bits, dest, index, 5);}
+void XEmitter::BTR(int bits, const OpArg& dest, const OpArg& index) {WriteBitTest(bits, dest, index, 6);}
+void XEmitter::BTC(int bits, const OpArg& dest, const OpArg& index) {WriteBitTest(bits, dest, index, 7);}
+
+//shift can be either imm8 or cl
+void XEmitter::SHRD(int bits, const OpArg& dest, const OpArg& src, const OpArg& shift)
+{
+ CheckFlags();
+ if (dest.IsImm())
+ {
+ assert(0, "SHRD - can't use imms as destination");
+ }
+ if (!src.IsSimpleReg())
+ {
+ assert(0, "SHRD - must use simple register as source");
+ }
+ if ((shift.IsSimpleReg() && shift.GetSimpleReg() != ECX) || (shift.IsImm() && shift.GetImmBits() != 8))
+ {
+ assert(0, "SHRD - illegal shift");
+ }
+ if (bits == 16)
+ Write8(0x66);
+ X64Reg operand = src.GetSimpleReg();
+ dest.WriteRex(this, bits, bits, operand);
+ if (shift.GetImmBits() == 8)
+ {
+ Write8(0x0F); Write8(0xAC);
+ dest.WriteRest(this, 1, operand);
+ Write8((u8)shift.offset);
+ }
+ else
+ {
+ Write8(0x0F); Write8(0xAD);
+ dest.WriteRest(this, 0, operand);
+ }
+}
+
+void XEmitter::SHLD(int bits, const OpArg& dest, const OpArg& src, const OpArg& shift)
+{
+ CheckFlags();
+ if (dest.IsImm())
+ {
+ assert(0, "SHLD - can't use imms as destination");
+ }
+ if (!src.IsSimpleReg())
+ {
+ assert(0, "SHLD - must use simple register as source");
+ }
+ if ((shift.IsSimpleReg() && shift.GetSimpleReg() != ECX) || (shift.IsImm() && shift.GetImmBits() != 8))
+ {
+ assert(0, "SHLD - illegal shift");
+ }
+ if (bits == 16)
+ Write8(0x66);
+ X64Reg operand = src.GetSimpleReg();
+ dest.WriteRex(this, bits, bits, operand);
+ if (shift.GetImmBits() == 8)
+ {
+ Write8(0x0F); Write8(0xA4);
+ dest.WriteRest(this, 1, operand);
+ Write8((u8)shift.offset);
+ }
+ else
+ {
+ Write8(0x0F); Write8(0xA5);
+ dest.WriteRest(this, 0, operand);
+ }
+}
+
+void OpArg::WriteSingleByteOp(XEmitter *emit, u8 op, X64Reg _operandReg, int bits)
+{
+ if (bits == 16)
+ emit->Write8(0x66);
+
+ this->operandReg = (u8)_operandReg;
+ WriteRex(emit, bits, bits);
+ emit->Write8(op);
+ WriteRest(emit);
+}
+
+//operand can either be immediate or register
+void OpArg::WriteNormalOp(XEmitter *emit, bool toRM, NormalOp op, const OpArg& operand, int bits) const
+{
+ X64Reg _operandReg;
+ if (IsImm())
+ {
+ assert(0, "WriteNormalOp - Imm argument, wrong order");
+ }
+
+ if (bits == 16)
+ emit->Write8(0x66);
+
+ int immToWrite = 0;
+
+ if (operand.IsImm())
+ {
+ WriteRex(emit, bits, bits);
+
+ if (!toRM)
+ {
+ assert(0, "WriteNormalOp - Writing to Imm (!toRM)");
+ }
+
+ if (operand.scale == SCALE_IMM8 && bits == 8)
+ {
+ // op al, imm8
+ if (!scale && offsetOrBaseReg == AL && normalops[op].eaximm8 != 0xCC)
+ {
+ emit->Write8(normalops[op].eaximm8);
+ emit->Write8((u8)operand.offset);
+ return;
+ }
+ // mov reg, imm8
+ if (!scale && op == nrmMOV)
+ {
+ emit->Write8(0xB0 + (offsetOrBaseReg & 7));
+ emit->Write8((u8)operand.offset);
+ return;
+ }
+ // op r/m8, imm8
+ emit->Write8(normalops[op].imm8);
+ immToWrite = 8;
+ }
+ else if ((operand.scale == SCALE_IMM16 && bits == 16) ||
+ (operand.scale == SCALE_IMM32 && bits == 32) ||
+ (operand.scale == SCALE_IMM32 && bits == 64))
+ {
+ // Try to save immediate size if we can, but first check to see
+ // if the instruction supports simm8.
+ // op r/m, imm8
+ if (normalops[op].simm8 != 0xCC &&
+ ((operand.scale == SCALE_IMM16 && (s16)operand.offset == (s8)operand.offset) ||
+ (operand.scale == SCALE_IMM32 && (s32)operand.offset == (s8)operand.offset)))
+ {
+ emit->Write8(normalops[op].simm8);
+ immToWrite = 8;
+ }
+ else
+ {
+ // mov reg, imm
+ if (!scale && op == nrmMOV && bits != 64)
+ {
+ emit->Write8(0xB8 + (offsetOrBaseReg & 7));
+ if (bits == 16)
+ emit->Write16((u16)operand.offset);
+ else
+ emit->Write32((u32)operand.offset);
+ return;
+ }
+ // op eax, imm
+ if (!scale && offsetOrBaseReg == EAX && normalops[op].eaximm32 != 0xCC)
+ {
+ emit->Write8(normalops[op].eaximm32);
+ if (bits == 16)
+ emit->Write16((u16)operand.offset);
+ else
+ emit->Write32((u32)operand.offset);
+ return;
+ }
+ // op r/m, imm
+ emit->Write8(normalops[op].imm32);
+ immToWrite = bits == 16 ? 16 : 32;
+ }
+ }
+ else if ((operand.scale == SCALE_IMM8 && bits == 16) ||
+ (operand.scale == SCALE_IMM8 && bits == 32) ||
+ (operand.scale == SCALE_IMM8 && bits == 64))
+ {
+ // op r/m, imm8
+ emit->Write8(normalops[op].simm8);
+ immToWrite = 8;
+ }
+ else if (operand.scale == SCALE_IMM64 && bits == 64)
+ {
+ if (scale)
+ {
+ assert(0, "WriteNormalOp - MOV with 64-bit imm requres register destination");
+ }
+ // mov reg64, imm64
+ else if (op == nrmMOV)
+ {
+ emit->Write8(0xB8 + (offsetOrBaseReg & 7));
+ emit->Write64((u64)operand.offset);
+ return;
+ }
+ assert(0, "WriteNormalOp - Only MOV can take 64-bit imm");
+ }
+ else
+ {
+ assert(0, "WriteNormalOp - Unhandled case");
+ }
+ _operandReg = (X64Reg)normalops[op].ext; //pass extension in REG of ModRM
+ }
+ else
+ {
+ _operandReg = (X64Reg)operand.offsetOrBaseReg;
+ WriteRex(emit, bits, bits, _operandReg);
+ // op r/m, reg
+ if (toRM)
+ {
+ emit->Write8(bits == 8 ? normalops[op].toRm8 : normalops[op].toRm32);
+ }
+ // op reg, r/m
+ else
+ {
+ emit->Write8(bits == 8 ? normalops[op].fromRm8 : normalops[op].fromRm32);
+ }
+ }
+ WriteRest(emit, immToWrite >> 3, _operandReg);
+ switch (immToWrite)
+ {
+ case 0:
+ break;
+ case 8:
+ emit->Write8((u8)operand.offset);
+ break;
+ case 16:
+ emit->Write16((u16)operand.offset);
+ break;
+ case 32:
+ emit->Write32((u32)operand.offset);
+ break;
+ default:
+ assert(0, "WriteNormalOp - Unhandled case");
+ }
+}
+
+void XEmitter::WriteNormalOp(XEmitter *emit, int bits, NormalOp op, const OpArg& a1, const OpArg& a2)
+{
+ if (a1.IsImm())
+ {
+ //Booh! Can't write to an imm
+ assert(0, "WriteNormalOp - a1 cannot be imm");
+ return;
+ }
+ if (a2.IsImm())
+ {
+ a1.WriteNormalOp(emit, true, op, a2, bits);
+ }
+ else
+ {
+ if (a1.IsSimpleReg())
+ {
+ a2.WriteNormalOp(emit, false, op, a1, bits);
+ }
+ else
+ {
+ assert(a2.IsSimpleReg() || a2.IsImm(), "WriteNormalOp - a1 and a2 cannot both be memory");
+ a1.WriteNormalOp(emit, true, op, a2, bits);
+ }
+ }
+}
+
+void XEmitter::ADD (int bits, const OpArg& a1, const OpArg& a2) {CheckFlags(); WriteNormalOp(this, bits, nrmADD, a1, a2);}
+void XEmitter::ADC (int bits, const OpArg& a1, const OpArg& a2) {CheckFlags(); WriteNormalOp(this, bits, nrmADC, a1, a2);}
+void XEmitter::SUB (int bits, const OpArg& a1, const OpArg& a2) {CheckFlags(); WriteNormalOp(this, bits, nrmSUB, a1, a2);}
+void XEmitter::SBB (int bits, const OpArg& a1, const OpArg& a2) {CheckFlags(); WriteNormalOp(this, bits, nrmSBB, a1, a2);}
+void XEmitter::AND (int bits, const OpArg& a1, const OpArg& a2) {CheckFlags(); WriteNormalOp(this, bits, nrmAND, a1, a2);}
+void XEmitter::OR (int bits, const OpArg& a1, const OpArg& a2) {CheckFlags(); WriteNormalOp(this, bits, nrmOR , a1, a2);}
+void XEmitter::XOR (int bits, const OpArg& a1, const OpArg& a2) {CheckFlags(); WriteNormalOp(this, bits, nrmXOR, a1, a2);}
+void XEmitter::MOV (int bits, const OpArg& a1, const OpArg& a2)
+{
+ if (a1.IsSimpleReg() && a2.IsSimpleReg() && a1.GetSimpleReg() == a2.GetSimpleReg())
+ LOG_ERROR(Common, "Redundant MOV @ %p - bug in JIT?", code);
+ WriteNormalOp(this, bits, nrmMOV, a1, a2);
+}
+void XEmitter::TEST(int bits, const OpArg& a1, const OpArg& a2) {CheckFlags(); WriteNormalOp(this, bits, nrmTEST, a1, a2);}
+void XEmitter::CMP (int bits, const OpArg& a1, const OpArg& a2) {CheckFlags(); WriteNormalOp(this, bits, nrmCMP, a1, a2);}
+void XEmitter::XCHG(int bits, const OpArg& a1, const OpArg& a2) {WriteNormalOp(this, bits, nrmXCHG, a1, a2);}
+
+void XEmitter::IMUL(int bits, X64Reg regOp, const OpArg& a1, const OpArg& a2)
+{
+ CheckFlags();
+ if (bits == 8)
+ {
+ assert(0, "IMUL - illegal bit size!");
+ return;
+ }
+
+ if (a1.IsImm())
+ {
+ assert(0, "IMUL - second arg cannot be imm!");
+ return;
+ }
+
+ if (!a2.IsImm())
+ {
+ assert(0, "IMUL - third arg must be imm!");
+ return;
+ }
+
+ if (bits == 16)
+ Write8(0x66);
+ a1.WriteRex(this, bits, bits, regOp);
+
+ if (a2.GetImmBits() == 8 ||
+ (a2.GetImmBits() == 16 && (s8)a2.offset == (s16)a2.offset) ||
+ (a2.GetImmBits() == 32 && (s8)a2.offset == (s32)a2.offset))
+ {
+ Write8(0x6B);
+ a1.WriteRest(this, 1, regOp);
+ Write8((u8)a2.offset);
+ }
+ else
+ {
+ Write8(0x69);
+ if (a2.GetImmBits() == 16 && bits == 16)
+ {
+ a1.WriteRest(this, 2, regOp);
+ Write16((u16)a2.offset);
+ }
+ else if (a2.GetImmBits() == 32 && (bits == 32 || bits == 64))
+ {
+ a1.WriteRest(this, 4, regOp);
+ Write32((u32)a2.offset);
+ }
+ else
+ {
+ assert(0, "IMUL - unhandled case!");
+ }
+ }
+}
+
+void XEmitter::IMUL(int bits, X64Reg regOp, const OpArg& a)
+{
+ CheckFlags();
+ if (bits == 8)
+ {
+ assert(0, "IMUL - illegal bit size!");
+ return;
+ }
+
+ if (a.IsImm())
+ {
+ IMUL(bits, regOp, R(regOp), a) ;
+ return;
+ }
+
+ if (bits == 16)
+ Write8(0x66);
+ a.WriteRex(this, bits, bits, regOp);
+ Write8(0x0F);
+ Write8(0xAF);
+ a.WriteRest(this, 0, regOp);
+}
+
+
+void XEmitter::WriteSSEOp(u8 opPrefix, u16 op, X64Reg regOp, OpArg arg, int extrabytes)
+{
+ if (opPrefix)
+ Write8(opPrefix);
+ arg.operandReg = regOp;
+ arg.WriteRex(this, 0, 0);
+ Write8(0x0F);
+ if (op > 0xFF)
+ Write8((op >> 8) & 0xFF);
+ Write8(op & 0xFF);
+ arg.WriteRest(this, extrabytes);
+}
+
+void XEmitter::WriteAVXOp(u8 opPrefix, u16 op, X64Reg regOp, const OpArg& arg, int extrabytes)
+{
+ WriteAVXOp(opPrefix, op, regOp, INVALID_REG, arg, extrabytes);
+}
+
+static int GetVEXmmmmm(u16 op)
+{
+ // Currently, only 0x38 and 0x3A are used as secondary escape byte.
+ if ((op >> 8) == 0x3A)
+ return 3;
+ if ((op >> 8) == 0x38)
+ return 2;
+
+ return 1;
+}
+
+static int GetVEXpp(u8 opPrefix)
+{
+ if (opPrefix == 0x66)
+ return 1;
+ if (opPrefix == 0xF3)
+ return 2;
+ if (opPrefix == 0xF2)
+ return 3;
+
+ return 0;
+}
+
+void XEmitter::WriteAVXOp(u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg, int extrabytes)
+{
+ if (!Common::GetCPUCaps().avx)
+ assert(0, "Trying to use AVX on a system that doesn't support it. Bad programmer.");
+ int mmmmm = GetVEXmmmmm(op);
+ int pp = GetVEXpp(opPrefix);
+ // FIXME: we currently don't support 256-bit instructions, and "size" is not the vector size here
+ arg.WriteVex(this, regOp1, regOp2, 0, pp, mmmmm);
+ Write8(op & 0xFF);
+ arg.WriteRest(this, extrabytes, regOp1);
+}
+
+// Like the above, but more general; covers GPR-based VEX operations, like BMI1/2
+void XEmitter::WriteVEXOp(int size, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg, int extrabytes)
+{
+ if (size != 32 && size != 64)
+ assert(0, "VEX GPR instructions only support 32-bit and 64-bit modes!");
+ int mmmmm = GetVEXmmmmm(op);
+ int pp = GetVEXpp(opPrefix);
+ arg.WriteVex(this, regOp1, regOp2, 0, pp, mmmmm, size == 64);
+ Write8(op & 0xFF);
+ arg.WriteRest(this, extrabytes, regOp1);
+}
+
+void XEmitter::WriteBMI1Op(int size, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg, int extrabytes)
+{
+ CheckFlags();
+ if (!Common::GetCPUCaps().bmi1)
+ assert(0, "Trying to use BMI1 on a system that doesn't support it. Bad programmer.");
+ WriteVEXOp(size, opPrefix, op, regOp1, regOp2, arg, extrabytes);
+}
+
+void XEmitter::WriteBMI2Op(int size, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg, int extrabytes)
+{
+ CheckFlags();
+ if (!Common::GetCPUCaps().bmi2)
+ assert(0, "Trying to use BMI2 on a system that doesn't support it. Bad programmer.");
+ WriteVEXOp(size, opPrefix, op, regOp1, regOp2, arg, extrabytes);
+}
+
+void XEmitter::MOVD_xmm(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0x6E, dest, arg, 0);}
+void XEmitter::MOVD_xmm(const OpArg &arg, X64Reg src) {WriteSSEOp(0x66, 0x7E, src, arg, 0);}
+
+void XEmitter::MOVQ_xmm(X64Reg dest, OpArg arg)
+{
+#ifdef ARCHITECTURE_x86_64
+ // Alternate encoding
+ // This does not display correctly in MSVC's debugger, it thinks it's a MOVD
+ arg.operandReg = dest;
+ Write8(0x66);
+ arg.WriteRex(this, 64, 0);
+ Write8(0x0f);
+ Write8(0x6E);
+ arg.WriteRest(this, 0);
+#else
+ arg.operandReg = dest;
+ Write8(0xF3);
+ Write8(0x0f);
+ Write8(0x7E);
+ arg.WriteRest(this, 0);
+#endif
+}
+
+void XEmitter::MOVQ_xmm(OpArg arg, X64Reg src)
+{
+ if (src > 7 || arg.IsSimpleReg())
+ {
+ // Alternate encoding
+ // This does not display correctly in MSVC's debugger, it thinks it's a MOVD
+ arg.operandReg = src;
+ Write8(0x66);
+ arg.WriteRex(this, 64, 0);
+ Write8(0x0f);
+ Write8(0x7E);
+ arg.WriteRest(this, 0);
+ }
+ else
+ {
+ arg.operandReg = src;
+ arg.WriteRex(this, 0, 0);
+ Write8(0x66);
+ Write8(0x0f);
+ Write8(0xD6);
+ arg.WriteRest(this, 0);
+ }
+}
+
+void XEmitter::WriteMXCSR(OpArg arg, int ext)
+{
+ if (arg.IsImm() || arg.IsSimpleReg())
+ assert(0, "MXCSR - invalid operand");
+
+ arg.operandReg = ext;
+ arg.WriteRex(this, 0, 0);
+ Write8(0x0F);
+ Write8(0xAE);
+ arg.WriteRest(this);
+}
+
+void XEmitter::STMXCSR(const OpArg& memloc) {WriteMXCSR(memloc, 3);}
+void XEmitter::LDMXCSR(const OpArg& memloc) {WriteMXCSR(memloc, 2);}
+
+void XEmitter::MOVNTDQ(const OpArg& arg, X64Reg regOp) {WriteSSEOp(0x66, sseMOVNTDQ, regOp, arg);}
+void XEmitter::MOVNTPS(const OpArg& arg, X64Reg regOp) {WriteSSEOp(0x00, sseMOVNTP, regOp, arg);}
+void XEmitter::MOVNTPD(const OpArg& arg, X64Reg regOp) {WriteSSEOp(0x66, sseMOVNTP, regOp, arg);}
+
+void XEmitter::ADDSS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, sseADD, regOp, arg);}
+void XEmitter::ADDSD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, sseADD, regOp, arg);}
+void XEmitter::SUBSS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, sseSUB, regOp, arg);}
+void XEmitter::SUBSD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, sseSUB, regOp, arg);}
+void XEmitter::CMPSS(X64Reg regOp, const OpArg& arg, u8 compare) {WriteSSEOp(0xF3, sseCMP, regOp, arg, 1); Write8(compare);}
+void XEmitter::CMPSD(X64Reg regOp, const OpArg& arg, u8 compare) {WriteSSEOp(0xF2, sseCMP, regOp, arg, 1); Write8(compare);}
+void XEmitter::MULSS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, sseMUL, regOp, arg);}
+void XEmitter::MULSD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, sseMUL, regOp, arg);}
+void XEmitter::DIVSS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, sseDIV, regOp, arg);}
+void XEmitter::DIVSD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, sseDIV, regOp, arg);}
+void XEmitter::MINSS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, sseMIN, regOp, arg);}
+void XEmitter::MINSD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, sseMIN, regOp, arg);}
+void XEmitter::MAXSS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, sseMAX, regOp, arg);}
+void XEmitter::MAXSD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, sseMAX, regOp, arg);}
+void XEmitter::SQRTSS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, sseSQRT, regOp, arg);}
+void XEmitter::SQRTSD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, sseSQRT, regOp, arg);}
+void XEmitter::RCPSS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, sseRCP, regOp, arg);}
+void XEmitter::RSQRTSS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, sseRSQRT, regOp, arg);}
+
+void XEmitter::ADDPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseADD, regOp, arg);}
+void XEmitter::ADDPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseADD, regOp, arg);}
+void XEmitter::SUBPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseSUB, regOp, arg);}
+void XEmitter::SUBPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseSUB, regOp, arg);}
+void XEmitter::CMPPS(X64Reg regOp, const OpArg& arg, u8 compare) {WriteSSEOp(0x00, sseCMP, regOp, arg, 1); Write8(compare);}
+void XEmitter::CMPPD(X64Reg regOp, const OpArg& arg, u8 compare) {WriteSSEOp(0x66, sseCMP, regOp, arg, 1); Write8(compare);}
+void XEmitter::ANDPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseAND, regOp, arg);}
+void XEmitter::ANDPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseAND, regOp, arg);}
+void XEmitter::ANDNPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseANDN, regOp, arg);}
+void XEmitter::ANDNPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseANDN, regOp, arg);}
+void XEmitter::ORPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseOR, regOp, arg);}
+void XEmitter::ORPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseOR, regOp, arg);}
+void XEmitter::XORPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseXOR, regOp, arg);}
+void XEmitter::XORPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseXOR, regOp, arg);}
+void XEmitter::MULPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseMUL, regOp, arg);}
+void XEmitter::MULPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseMUL, regOp, arg);}
+void XEmitter::DIVPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseDIV, regOp, arg);}
+void XEmitter::DIVPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseDIV, regOp, arg);}
+void XEmitter::MINPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseMIN, regOp, arg);}
+void XEmitter::MINPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseMIN, regOp, arg);}
+void XEmitter::MAXPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseMAX, regOp, arg);}
+void XEmitter::MAXPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseMAX, regOp, arg);}
+void XEmitter::SQRTPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseSQRT, regOp, arg);}
+void XEmitter::SQRTPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseSQRT, regOp, arg);}
+void XEmitter::RCPPS(X64Reg regOp, const OpArg& arg) { WriteSSEOp(0x00, sseRCP, regOp, arg); }
+void XEmitter::RSQRTPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseRSQRT, regOp, arg);}
+void XEmitter::SHUFPS(X64Reg regOp, const OpArg& arg, u8 shuffle) {WriteSSEOp(0x00, sseSHUF, regOp, arg,1); Write8(shuffle);}
+void XEmitter::SHUFPD(X64Reg regOp, const OpArg& arg, u8 shuffle) {WriteSSEOp(0x66, sseSHUF, regOp, arg,1); Write8(shuffle);}
+
+void XEmitter::HADDPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, sseHADD, regOp, arg);}
+
+void XEmitter::COMISS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseCOMIS, regOp, arg);} //weird that these should be packed
+void XEmitter::COMISD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseCOMIS, regOp, arg);} //ordered
+void XEmitter::UCOMISS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseUCOMIS, regOp, arg);} //unordered
+void XEmitter::UCOMISD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseUCOMIS, regOp, arg);}
+
+void XEmitter::MOVAPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseMOVAPfromRM, regOp, arg);}
+void XEmitter::MOVAPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseMOVAPfromRM, regOp, arg);}
+void XEmitter::MOVAPS(const OpArg& arg, X64Reg regOp) {WriteSSEOp(0x00, sseMOVAPtoRM, regOp, arg);}
+void XEmitter::MOVAPD(const OpArg& arg, X64Reg regOp) {WriteSSEOp(0x66, sseMOVAPtoRM, regOp, arg);}
+
+void XEmitter::MOVUPS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, sseMOVUPfromRM, regOp, arg);}
+void XEmitter::MOVUPD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseMOVUPfromRM, regOp, arg);}
+void XEmitter::MOVUPS(const OpArg& arg, X64Reg regOp) {WriteSSEOp(0x00, sseMOVUPtoRM, regOp, arg);}
+void XEmitter::MOVUPD(const OpArg& arg, X64Reg regOp) {WriteSSEOp(0x66, sseMOVUPtoRM, regOp, arg);}
+
+void XEmitter::MOVDQA(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, sseMOVDQfromRM, regOp, arg);}
+void XEmitter::MOVDQA(const OpArg& arg, X64Reg regOp) {WriteSSEOp(0x66, sseMOVDQtoRM, regOp, arg);}
+void XEmitter::MOVDQU(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, sseMOVDQfromRM, regOp, arg);}
+void XEmitter::MOVDQU(const OpArg& arg, X64Reg regOp) {WriteSSEOp(0xF3, sseMOVDQtoRM, regOp, arg);}
+
+void XEmitter::MOVSS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, sseMOVUPfromRM, regOp, arg);}
+void XEmitter::MOVSD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, sseMOVUPfromRM, regOp, arg);}
+void XEmitter::MOVSS(const OpArg& arg, X64Reg regOp) {WriteSSEOp(0xF3, sseMOVUPtoRM, regOp, arg);}
+void XEmitter::MOVSD(const OpArg& arg, X64Reg regOp) {WriteSSEOp(0xF2, sseMOVUPtoRM, regOp, arg);}
+
+void XEmitter::MOVLPS(X64Reg regOp, const OpArg& arg) { WriteSSEOp(0x00, sseMOVLPfromRM, regOp, arg); }
+void XEmitter::MOVLPD(X64Reg regOp, const OpArg& arg) { WriteSSEOp(0x66, sseMOVLPfromRM, regOp, arg); }
+void XEmitter::MOVLPS(const OpArg& arg, X64Reg regOp) { WriteSSEOp(0x00, sseMOVLPtoRM, regOp, arg); }
+void XEmitter::MOVLPD(const OpArg& arg, X64Reg regOp) { WriteSSEOp(0x66, sseMOVLPtoRM, regOp, arg); }
+
+void XEmitter::MOVHPS(X64Reg regOp, const OpArg& arg) { WriteSSEOp(0x00, sseMOVHPfromRM, regOp, arg); }
+void XEmitter::MOVHPD(X64Reg regOp, const OpArg& arg) { WriteSSEOp(0x66, sseMOVHPfromRM, regOp, arg); }
+void XEmitter::MOVHPS(const OpArg& arg, X64Reg regOp) { WriteSSEOp(0x00, sseMOVHPtoRM, regOp, arg); }
+void XEmitter::MOVHPD(const OpArg& arg, X64Reg regOp) { WriteSSEOp(0x66, sseMOVHPtoRM, regOp, arg); }
+
+void XEmitter::MOVHLPS(X64Reg regOp1, X64Reg regOp2) {WriteSSEOp(0x00, sseMOVHLPS, regOp1, R(regOp2));}
+void XEmitter::MOVLHPS(X64Reg regOp1, X64Reg regOp2) {WriteSSEOp(0x00, sseMOVLHPS, regOp1, R(regOp2));}
+
+void XEmitter::CVTPS2PD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, 0x5A, regOp, arg);}
+void XEmitter::CVTPD2PS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, 0x5A, regOp, arg);}
+
+void XEmitter::CVTSD2SS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, 0x5A, regOp, arg);}
+void XEmitter::CVTSS2SD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, 0x5A, regOp, arg);}
+void XEmitter::CVTSD2SI(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, 0x2D, regOp, arg);}
+void XEmitter::CVTSS2SI(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, 0x2D, regOp, arg);}
+void XEmitter::CVTSI2SD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, 0x2A, regOp, arg);}
+void XEmitter::CVTSI2SS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, 0x2A, regOp, arg);}
+
+void XEmitter::CVTDQ2PD(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, 0xE6, regOp, arg);}
+void XEmitter::CVTDQ2PS(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x00, 0x5B, regOp, arg);}
+void XEmitter::CVTPD2DQ(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, 0xE6, regOp, arg);}
+void XEmitter::CVTPS2DQ(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, 0x5B, regOp, arg);}
+
+void XEmitter::CVTTSD2SI(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF2, 0x2C, regOp, arg);}
+void XEmitter::CVTTSS2SI(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, 0x2C, regOp, arg);}
+void XEmitter::CVTTPS2DQ(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0xF3, 0x5B, regOp, arg);}
+void XEmitter::CVTTPD2DQ(X64Reg regOp, const OpArg& arg) {WriteSSEOp(0x66, 0xE6, regOp, arg);}
+
+void XEmitter::MASKMOVDQU(X64Reg dest, X64Reg src) {WriteSSEOp(0x66, sseMASKMOVDQU, dest, R(src));}
+
+void XEmitter::MOVMSKPS(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x00, 0x50, dest, arg);}
+void XEmitter::MOVMSKPD(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0x50, dest, arg);}
+
+void XEmitter::LDDQU(X64Reg dest, const OpArg& arg) {WriteSSEOp(0xF2, sseLDDQU, dest, arg);} // For integer data only
+
+// THESE TWO ARE UNTESTED.
+void XEmitter::UNPCKLPS(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x00, 0x14, dest, arg);}
+void XEmitter::UNPCKHPS(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x00, 0x15, dest, arg);}
+
+void XEmitter::UNPCKLPD(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0x14, dest, arg);}
+void XEmitter::UNPCKHPD(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0x15, dest, arg);}
+
+void XEmitter::MOVDDUP(X64Reg regOp, const OpArg& arg)
+{
+ if (Common::GetCPUCaps().sse3)
+ {
+ WriteSSEOp(0xF2, 0x12, regOp, arg); //SSE3 movddup
+ }
+ else
+ {
+ // Simulate this instruction with SSE2 instructions
+ if (!arg.IsSimpleReg(regOp))
+ MOVSD(regOp, arg);
+ UNPCKLPD(regOp, R(regOp));
+ }
+}
+
+//There are a few more left
+
+// Also some integer instructions are missing
+void XEmitter::PACKSSDW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0x6B, dest, arg);}
+void XEmitter::PACKSSWB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0x63, dest, arg);}
+void XEmitter::PACKUSWB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0x67, dest, arg);}
+
+void XEmitter::PUNPCKLBW(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0x60, dest, arg);}
+void XEmitter::PUNPCKLWD(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0x61, dest, arg);}
+void XEmitter::PUNPCKLDQ(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0x62, dest, arg);}
+void XEmitter::PUNPCKLQDQ(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0x6C, dest, arg);}
+
+void XEmitter::PSRLW(X64Reg reg, int shift)
+{
+ WriteSSEOp(0x66, 0x71, (X64Reg)2, R(reg));
+ Write8(shift);
+}
+
+void XEmitter::PSRLD(X64Reg reg, int shift)
+{
+ WriteSSEOp(0x66, 0x72, (X64Reg)2, R(reg));
+ Write8(shift);
+}
+
+void XEmitter::PSRLQ(X64Reg reg, int shift)
+{
+ WriteSSEOp(0x66, 0x73, (X64Reg)2, R(reg));
+ Write8(shift);
+}
+
+void XEmitter::PSRLQ(X64Reg reg, const OpArg& arg)
+{
+ WriteSSEOp(0x66, 0xd3, reg, arg);
+}
+
+void XEmitter::PSRLDQ(X64Reg reg, int shift) {
+ WriteSSEOp(0x66, 0x73, (X64Reg)3, R(reg));
+ Write8(shift);
+}
+
+void XEmitter::PSLLW(X64Reg reg, int shift)
+{
+ WriteSSEOp(0x66, 0x71, (X64Reg)6, R(reg));
+ Write8(shift);
+}
+
+void XEmitter::PSLLD(X64Reg reg, int shift)
+{
+ WriteSSEOp(0x66, 0x72, (X64Reg)6, R(reg));
+ Write8(shift);
+}
+
+void XEmitter::PSLLQ(X64Reg reg, int shift)
+{
+ WriteSSEOp(0x66, 0x73, (X64Reg)6, R(reg));
+ Write8(shift);
+}
+
+void XEmitter::PSLLDQ(X64Reg reg, int shift) {
+ WriteSSEOp(0x66, 0x73, (X64Reg)7, R(reg));
+ Write8(shift);
+}
+
+void XEmitter::PSRAW(X64Reg reg, int shift)
+{
+ WriteSSEOp(0x66, 0x71, (X64Reg)4, R(reg));
+ Write8(shift);
+}
+
+void XEmitter::PSRAD(X64Reg reg, int shift)
+{
+ WriteSSEOp(0x66, 0x72, (X64Reg)4, R(reg));
+ Write8(shift);
+}
+
+void XEmitter::WriteSSSE3Op(u8 opPrefix, u16 op, X64Reg regOp, const OpArg& arg, int extrabytes)
+{
+ if (!Common::GetCPUCaps().ssse3)
+ assert(0, "Trying to use SSSE3 on a system that doesn't support it. Bad programmer.");
+ WriteSSEOp(opPrefix, op, regOp, arg, extrabytes);
+}
+
+void XEmitter::WriteSSE41Op(u8 opPrefix, u16 op, X64Reg regOp, const OpArg& arg, int extrabytes)
+{
+ if (!Common::GetCPUCaps().sse4_1)
+ assert(0, "Trying to use SSE4.1 on a system that doesn't support it. Bad programmer.");
+ WriteSSEOp(opPrefix, op, regOp, arg, extrabytes);
+}
+
+void XEmitter::PSHUFB(X64Reg dest, const OpArg& arg) {WriteSSSE3Op(0x66, 0x3800, dest, arg);}
+void XEmitter::PTEST(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3817, dest, arg);}
+void XEmitter::PACKUSDW(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x382b, dest, arg);}
+void XEmitter::DPPS(X64Reg dest, const OpArg& arg, u8 mask) {WriteSSE41Op(0x66, 0x3A40, dest, arg, 1); Write8(mask);}
+
+void XEmitter::PMINSB(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3838, dest, arg);}
+void XEmitter::PMINSD(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3839, dest, arg);}
+void XEmitter::PMINUW(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x383a, dest, arg);}
+void XEmitter::PMINUD(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x383b, dest, arg);}
+void XEmitter::PMAXSB(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x383c, dest, arg);}
+void XEmitter::PMAXSD(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x383d, dest, arg);}
+void XEmitter::PMAXUW(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x383e, dest, arg);}
+void XEmitter::PMAXUD(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x383f, dest, arg);}
+
+void XEmitter::PMOVSXBW(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3820, dest, arg);}
+void XEmitter::PMOVSXBD(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3821, dest, arg);}
+void XEmitter::PMOVSXBQ(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3822, dest, arg);}
+void XEmitter::PMOVSXWD(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3823, dest, arg);}
+void XEmitter::PMOVSXWQ(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3824, dest, arg);}
+void XEmitter::PMOVSXDQ(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3825, dest, arg);}
+void XEmitter::PMOVZXBW(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3830, dest, arg);}
+void XEmitter::PMOVZXBD(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3831, dest, arg);}
+void XEmitter::PMOVZXBQ(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3832, dest, arg);}
+void XEmitter::PMOVZXWD(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3833, dest, arg);}
+void XEmitter::PMOVZXWQ(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3834, dest, arg);}
+void XEmitter::PMOVZXDQ(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3835, dest, arg);}
+
+void XEmitter::PBLENDVB(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3810, dest, arg);}
+void XEmitter::BLENDVPS(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3814, dest, arg);}
+void XEmitter::BLENDVPD(X64Reg dest, const OpArg& arg) {WriteSSE41Op(0x66, 0x3815, dest, arg);}
+void XEmitter::BLENDPS(X64Reg dest, const OpArg& arg, u8 blend) { WriteSSE41Op(0x66, 0x3A0C, dest, arg, 1); Write8(blend); }
+void XEmitter::BLENDPD(X64Reg dest, const OpArg& arg, u8 blend) { WriteSSE41Op(0x66, 0x3A0D, dest, arg, 1); Write8(blend); }
+
+void XEmitter::ROUNDSS(X64Reg dest, const OpArg& arg, u8 mode) {WriteSSE41Op(0x66, 0x3A0A, dest, arg, 1); Write8(mode);}
+void XEmitter::ROUNDSD(X64Reg dest, const OpArg& arg, u8 mode) {WriteSSE41Op(0x66, 0x3A0B, dest, arg, 1); Write8(mode);}
+void XEmitter::ROUNDPS(X64Reg dest, const OpArg& arg, u8 mode) {WriteSSE41Op(0x66, 0x3A08, dest, arg, 1); Write8(mode);}
+void XEmitter::ROUNDPD(X64Reg dest, const OpArg& arg, u8 mode) {WriteSSE41Op(0x66, 0x3A09, dest, arg, 1); Write8(mode);}
+
+void XEmitter::PAND(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xDB, dest, arg);}
+void XEmitter::PANDN(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xDF, dest, arg);}
+void XEmitter::PXOR(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xEF, dest, arg);}
+void XEmitter::POR(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xEB, dest, arg);}
+
+void XEmitter::PADDB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xFC, dest, arg);}
+void XEmitter::PADDW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xFD, dest, arg);}
+void XEmitter::PADDD(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xFE, dest, arg);}
+void XEmitter::PADDQ(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xD4, dest, arg);}
+
+void XEmitter::PADDSB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xEC, dest, arg);}
+void XEmitter::PADDSW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xED, dest, arg);}
+void XEmitter::PADDUSB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xDC, dest, arg);}
+void XEmitter::PADDUSW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xDD, dest, arg);}
+
+void XEmitter::PSUBB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xF8, dest, arg);}
+void XEmitter::PSUBW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xF9, dest, arg);}
+void XEmitter::PSUBD(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xFA, dest, arg);}
+void XEmitter::PSUBQ(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xFB, dest, arg);}
+
+void XEmitter::PSUBSB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xE8, dest, arg);}
+void XEmitter::PSUBSW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xE9, dest, arg);}
+void XEmitter::PSUBUSB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xD8, dest, arg);}
+void XEmitter::PSUBUSW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xD9, dest, arg);}
+
+void XEmitter::PAVGB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xE0, dest, arg);}
+void XEmitter::PAVGW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xE3, dest, arg);}
+
+void XEmitter::PCMPEQB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0x74, dest, arg);}
+void XEmitter::PCMPEQW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0x75, dest, arg);}
+void XEmitter::PCMPEQD(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0x76, dest, arg);}
+
+void XEmitter::PCMPGTB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0x64, dest, arg);}
+void XEmitter::PCMPGTW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0x65, dest, arg);}
+void XEmitter::PCMPGTD(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0x66, dest, arg);}
+
+void XEmitter::PEXTRW(X64Reg dest, const OpArg& arg, u8 subreg) {WriteSSEOp(0x66, 0xC5, dest, arg, 1); Write8(subreg);}
+void XEmitter::PINSRW(X64Reg dest, const OpArg& arg, u8 subreg) {WriteSSEOp(0x66, 0xC4, dest, arg, 1); Write8(subreg);}
+
+void XEmitter::PMADDWD(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xF5, dest, arg); }
+void XEmitter::PSADBW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xF6, dest, arg);}
+
+void XEmitter::PMAXSW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xEE, dest, arg); }
+void XEmitter::PMAXUB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xDE, dest, arg); }
+void XEmitter::PMINSW(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xEA, dest, arg); }
+void XEmitter::PMINUB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xDA, dest, arg); }
+
+void XEmitter::PMOVMSKB(X64Reg dest, const OpArg& arg) {WriteSSEOp(0x66, 0xD7, dest, arg); }
+void XEmitter::PSHUFD(X64Reg regOp, const OpArg& arg, u8 shuffle) {WriteSSEOp(0x66, 0x70, regOp, arg, 1); Write8(shuffle);}
+void XEmitter::PSHUFLW(X64Reg regOp, const OpArg& arg, u8 shuffle) {WriteSSEOp(0xF2, 0x70, regOp, arg, 1); Write8(shuffle);}
+void XEmitter::PSHUFHW(X64Reg regOp, const OpArg& arg, u8 shuffle) {WriteSSEOp(0xF3, 0x70, regOp, arg, 1); Write8(shuffle);}
+
+// VEX
+void XEmitter::VADDSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteAVXOp(0xF2, sseADD, regOp1, regOp2, arg);}
+void XEmitter::VSUBSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteAVXOp(0xF2, sseSUB, regOp1, regOp2, arg);}
+void XEmitter::VMULSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteAVXOp(0xF2, sseMUL, regOp1, regOp2, arg);}
+void XEmitter::VDIVSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteAVXOp(0xF2, sseDIV, regOp1, regOp2, arg);}
+void XEmitter::VADDPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteAVXOp(0x66, sseADD, regOp1, regOp2, arg);}
+void XEmitter::VSUBPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteAVXOp(0x66, sseSUB, regOp1, regOp2, arg);}
+void XEmitter::VMULPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteAVXOp(0x66, sseMUL, regOp1, regOp2, arg);}
+void XEmitter::VDIVPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteAVXOp(0x66, sseDIV, regOp1, regOp2, arg);}
+void XEmitter::VSQRTSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteAVXOp(0xF2, sseSQRT, regOp1, regOp2, arg);}
+void XEmitter::VSHUFPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg, u8 shuffle) {WriteAVXOp(0x66, sseSHUF, regOp1, regOp2, arg, 1); Write8(shuffle);}
+void XEmitter::VUNPCKLPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg){WriteAVXOp(0x66, 0x14, regOp1, regOp2, arg);}
+void XEmitter::VUNPCKHPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg){WriteAVXOp(0x66, 0x15, regOp1, regOp2, arg);}
+
+void XEmitter::VANDPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x00, sseAND, regOp1, regOp2, arg); }
+void XEmitter::VANDPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, sseAND, regOp1, regOp2, arg); }
+void XEmitter::VANDNPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x00, sseANDN, regOp1, regOp2, arg); }
+void XEmitter::VANDNPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, sseANDN, regOp1, regOp2, arg); }
+void XEmitter::VORPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x00, sseOR, regOp1, regOp2, arg); }
+void XEmitter::VORPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, sseOR, regOp1, regOp2, arg); }
+void XEmitter::VXORPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x00, sseXOR, regOp1, regOp2, arg); }
+void XEmitter::VXORPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, sseXOR, regOp1, regOp2, arg); }
+
+void XEmitter::VPAND(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0xDB, regOp1, regOp2, arg); }
+void XEmitter::VPANDN(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0xDF, regOp1, regOp2, arg); }
+void XEmitter::VPOR(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0xEB, regOp1, regOp2, arg); }
+void XEmitter::VPXOR(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0xEF, regOp1, regOp2, arg); }
+
+void XEmitter::VFMADD132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x3898, regOp1, regOp2, arg); }
+void XEmitter::VFMADD213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38A8, regOp1, regOp2, arg); }
+void XEmitter::VFMADD231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38B8, regOp1, regOp2, arg); }
+void XEmitter::VFMADD132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x3898, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMADD213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38A8, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMADD231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38B8, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMADD132SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x3899, regOp1, regOp2, arg); }
+void XEmitter::VFMADD213SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38A9, regOp1, regOp2, arg); }
+void XEmitter::VFMADD231SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38B9, regOp1, regOp2, arg); }
+void XEmitter::VFMADD132SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x3899, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMADD213SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38A9, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMADD231SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38B9, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMSUB132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x389A, regOp1, regOp2, arg); }
+void XEmitter::VFMSUB213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38AA, regOp1, regOp2, arg); }
+void XEmitter::VFMSUB231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38BA, regOp1, regOp2, arg); }
+void XEmitter::VFMSUB132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x389A, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMSUB213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38AA, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMSUB231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38BA, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMSUB132SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x389B, regOp1, regOp2, arg); }
+void XEmitter::VFMSUB213SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38AB, regOp1, regOp2, arg); }
+void XEmitter::VFMSUB231SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38BB, regOp1, regOp2, arg); }
+void XEmitter::VFMSUB132SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x389B, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMSUB213SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38AB, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMSUB231SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38BB, regOp1, regOp2, arg, 1); }
+void XEmitter::VFNMADD132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x389C, regOp1, regOp2, arg); }
+void XEmitter::VFNMADD213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38AC, regOp1, regOp2, arg); }
+void XEmitter::VFNMADD231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38BC, regOp1, regOp2, arg); }
+void XEmitter::VFNMADD132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x389C, regOp1, regOp2, arg, 1); }
+void XEmitter::VFNMADD213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38AC, regOp1, regOp2, arg, 1); }
+void XEmitter::VFNMADD231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38BC, regOp1, regOp2, arg, 1); }
+void XEmitter::VFNMADD132SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x389D, regOp1, regOp2, arg); }
+void XEmitter::VFNMADD213SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38AD, regOp1, regOp2, arg); }
+void XEmitter::VFNMADD231SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38BD, regOp1, regOp2, arg); }
+void XEmitter::VFNMADD132SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x389D, regOp1, regOp2, arg, 1); }
+void XEmitter::VFNMADD213SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38AD, regOp1, regOp2, arg, 1); }
+void XEmitter::VFNMADD231SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38BD, regOp1, regOp2, arg, 1); }
+void XEmitter::VFNMSUB132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x389E, regOp1, regOp2, arg); }
+void XEmitter::VFNMSUB213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38AE, regOp1, regOp2, arg); }
+void XEmitter::VFNMSUB231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38BE, regOp1, regOp2, arg); }
+void XEmitter::VFNMSUB132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x389E, regOp1, regOp2, arg, 1); }
+void XEmitter::VFNMSUB213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38AE, regOp1, regOp2, arg, 1); }
+void XEmitter::VFNMSUB231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38BE, regOp1, regOp2, arg, 1); }
+void XEmitter::VFNMSUB132SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x389F, regOp1, regOp2, arg); }
+void XEmitter::VFNMSUB213SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38AF, regOp1, regOp2, arg); }
+void XEmitter::VFNMSUB231SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38BF, regOp1, regOp2, arg); }
+void XEmitter::VFNMSUB132SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x389F, regOp1, regOp2, arg, 1); }
+void XEmitter::VFNMSUB213SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38AF, regOp1, regOp2, arg, 1); }
+void XEmitter::VFNMSUB231SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38BF, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMADDSUB132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x3896, regOp1, regOp2, arg); }
+void XEmitter::VFMADDSUB213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38A6, regOp1, regOp2, arg); }
+void XEmitter::VFMADDSUB231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38B6, regOp1, regOp2, arg); }
+void XEmitter::VFMADDSUB132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x3896, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMADDSUB213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38A6, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMADDSUB231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38B6, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMSUBADD132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x3897, regOp1, regOp2, arg); }
+void XEmitter::VFMSUBADD213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38A7, regOp1, regOp2, arg); }
+void XEmitter::VFMSUBADD231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38B7, regOp1, regOp2, arg); }
+void XEmitter::VFMSUBADD132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x3897, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMSUBADD213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38A7, regOp1, regOp2, arg, 1); }
+void XEmitter::VFMSUBADD231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg) { WriteAVXOp(0x66, 0x38B7, regOp1, regOp2, arg, 1); }
+
+void XEmitter::SARX(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2) {WriteBMI2Op(bits, 0xF3, 0x38F7, regOp1, regOp2, arg);}
+void XEmitter::SHLX(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2) {WriteBMI2Op(bits, 0x66, 0x38F7, regOp1, regOp2, arg);}
+void XEmitter::SHRX(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2) {WriteBMI2Op(bits, 0xF2, 0x38F7, regOp1, regOp2, arg);}
+void XEmitter::RORX(int bits, X64Reg regOp, const OpArg& arg, u8 rotate) {WriteBMI2Op(bits, 0xF2, 0x3AF0, regOp, INVALID_REG, arg, 1); Write8(rotate);}
+void XEmitter::PEXT(int bits, X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteBMI2Op(bits, 0xF3, 0x38F5, regOp1, regOp2, arg);}
+void XEmitter::PDEP(int bits, X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteBMI2Op(bits, 0xF2, 0x38F5, regOp1, regOp2, arg);}
+void XEmitter::MULX(int bits, X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteBMI2Op(bits, 0xF2, 0x38F6, regOp2, regOp1, arg);}
+void XEmitter::BZHI(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2) {WriteBMI2Op(bits, 0x00, 0x38F5, regOp1, regOp2, arg);}
+void XEmitter::BLSR(int bits, X64Reg regOp, const OpArg& arg) {WriteBMI1Op(bits, 0x00, 0x38F3, (X64Reg)0x1, regOp, arg);}
+void XEmitter::BLSMSK(int bits, X64Reg regOp, const OpArg& arg) {WriteBMI1Op(bits, 0x00, 0x38F3, (X64Reg)0x2, regOp, arg);}
+void XEmitter::BLSI(int bits, X64Reg regOp, const OpArg& arg) {WriteBMI1Op(bits, 0x00, 0x38F3, (X64Reg)0x3, regOp, arg);}
+void XEmitter::BEXTR(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2){WriteBMI1Op(bits, 0x00, 0x38F7, regOp1, regOp2, arg);}
+void XEmitter::ANDN(int bits, X64Reg regOp1, X64Reg regOp2, const OpArg& arg) {WriteBMI1Op(bits, 0x00, 0x38F2, regOp1, regOp2, arg);}
+
+// Prefixes
+
+void XEmitter::LOCK() { Write8(0xF0); }
+void XEmitter::REP() { Write8(0xF3); }
+void XEmitter::REPNE() { Write8(0xF2); }
+void XEmitter::FSOverride() { Write8(0x64); }
+void XEmitter::GSOverride() { Write8(0x65); }
+
+void XEmitter::FWAIT()
+{
+ Write8(0x9B);
+}
+
+// TODO: make this more generic
+void XEmitter::WriteFloatLoadStore(int bits, FloatOp op, FloatOp op_80b, const OpArg& arg)
+{
+ int mf = 0;
+ assert(!(bits == 80 && op_80b == floatINVALID), "WriteFloatLoadStore: 80 bits not supported for this instruction");
+ switch (bits)
+ {
+ case 32: mf = 0; break;
+ case 64: mf = 4; break;
+ case 80: mf = 2; break;
+ default: assert(0, "WriteFloatLoadStore: invalid bits (should be 32/64/80)");
+ }
+ Write8(0xd9 | mf);
+ // x87 instructions use the reg field of the ModR/M byte as opcode:
+ if (bits == 80)
+ op = op_80b;
+ arg.WriteRest(this, 0, (X64Reg) op);
+}
+
+void XEmitter::FLD(int bits, const OpArg& src) {WriteFloatLoadStore(bits, floatLD, floatLD80, src);}
+void XEmitter::FST(int bits, const OpArg& dest) {WriteFloatLoadStore(bits, floatST, floatINVALID, dest);}
+void XEmitter::FSTP(int bits, const OpArg& dest) {WriteFloatLoadStore(bits, floatSTP, floatSTP80, dest);}
+void XEmitter::FNSTSW_AX() { Write8(0xDF); Write8(0xE0); }
+
+void XEmitter::RDTSC() { Write8(0x0F); Write8(0x31); }
+
+void XCodeBlock::PoisonMemory() {
+ // x86/64: 0xCC = breakpoint
+ memset(region, 0xCC, region_size);
+}
+
+}
--- /dev/null
+// Copyright (C) 2003 Dolphin Project.
+
+// 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, version 2.0 or later versions.
+
+// 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 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official SVN repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#pragma once
+
+#include "citraimport\common/assert.h"
+#include "citraimport\common/bit_set.h"
+#include "citraimport\common/common_types.h"
+#include "citraimport\common/code_block.h"
+
+#if defined(ARCHITECTURE_x86_64) && !defined(_ARCH_64)
+#define _ARCH_64
+#endif
+
+#ifdef _ARCH_64
+#define PTRBITS 64
+#else
+#define PTRBITS 32
+#endif
+
+namespace Gen
+{
+
+enum X64Reg
+{
+ EAX = 0, EBX = 3, ECX = 1, EDX = 2,
+ ESI = 6, EDI = 7, EBP = 5, ESP = 4,
+
+ RAX = 0, RBX = 3, RCX = 1, RDX = 2,
+ RSI = 6, RDI = 7, RBP = 5, RSP = 4,
+ R8 = 8, R9 = 9, R10 = 10,R11 = 11,
+ R12 = 12,R13 = 13,R14 = 14,R15 = 15,
+
+ AL = 0, BL = 3, CL = 1, DL = 2,
+ SIL = 6, DIL = 7, BPL = 5, SPL = 4,
+ AH = 0x104, BH = 0x107, CH = 0x105, DH = 0x106,
+
+ AX = 0, BX = 3, CX = 1, DX = 2,
+ SI = 6, DI = 7, BP = 5, SP = 4,
+
+ XMM0=0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7,
+ XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15,
+
+ YMM0=0, YMM1, YMM2, YMM3, YMM4, YMM5, YMM6, YMM7,
+ YMM8, YMM9, YMM10, YMM11, YMM12, YMM13, YMM14, YMM15,
+
+ INVALID_REG = 0xFFFFFFFF
+};
+
+enum CCFlags
+{
+ CC_O = 0,
+ CC_NO = 1,
+ CC_B = 2, CC_C = 2, CC_NAE = 2,
+ CC_NB = 3, CC_NC = 3, CC_AE = 3,
+ CC_Z = 4, CC_E = 4,
+ CC_NZ = 5, CC_NE = 5,
+ CC_BE = 6, CC_NA = 6,
+ CC_NBE = 7, CC_A = 7,
+ CC_S = 8,
+ CC_NS = 9,
+ CC_P = 0xA, CC_PE = 0xA,
+ CC_NP = 0xB, CC_PO = 0xB,
+ CC_L = 0xC, CC_NGE = 0xC,
+ CC_NL = 0xD, CC_GE = 0xD,
+ CC_LE = 0xE, CC_NG = 0xE,
+ CC_NLE = 0xF, CC_G = 0xF
+};
+
+enum
+{
+ NUMGPRs = 16,
+ NUMXMMs = 16,
+};
+
+enum
+{
+ SCALE_NONE = 0,
+ SCALE_1 = 1,
+ SCALE_2 = 2,
+ SCALE_4 = 4,
+ SCALE_8 = 8,
+ SCALE_ATREG = 16,
+ //SCALE_NOBASE_1 is not supported and can be replaced with SCALE_ATREG
+ SCALE_NOBASE_2 = 34,
+ SCALE_NOBASE_4 = 36,
+ SCALE_NOBASE_8 = 40,
+ SCALE_RIP = 0xFF,
+ SCALE_IMM8 = 0xF0,
+ SCALE_IMM16 = 0xF1,
+ SCALE_IMM32 = 0xF2,
+ SCALE_IMM64 = 0xF3,
+};
+
+enum NormalOp {
+ nrmADD,
+ nrmADC,
+ nrmSUB,
+ nrmSBB,
+ nrmAND,
+ nrmOR ,
+ nrmXOR,
+ nrmMOV,
+ nrmTEST,
+ nrmCMP,
+ nrmXCHG,
+};
+
+enum {
+ CMP_EQ = 0,
+ CMP_LT = 1,
+ CMP_LE = 2,
+ CMP_UNORD = 3,
+ CMP_NEQ = 4,
+ CMP_NLT = 5,
+ CMP_NLE = 6,
+ CMP_ORD = 7,
+};
+
+enum FloatOp {
+ floatLD = 0,
+ floatST = 2,
+ floatSTP = 3,
+ floatLD80 = 5,
+ floatSTP80 = 7,
+
+ floatINVALID = -1,
+};
+
+enum FloatRound {
+ FROUND_NEAREST = 0,
+ FROUND_FLOOR = 1,
+ FROUND_CEIL = 2,
+ FROUND_ZERO = 3,
+ FROUND_MXCSR = 4,
+
+ FROUND_RAISE_PRECISION = 0,
+ FROUND_IGNORE_PRECISION = 8,
+};
+
+class XEmitter;
+
+// RIP addressing does not benefit from micro op fusion on Core arch
+struct OpArg
+{
+ OpArg() {} // dummy op arg, used for storage
+ OpArg(u64 _offset, int _scale, X64Reg rmReg = RAX, X64Reg scaledReg = RAX)
+ {
+ operandReg = 0;
+ scale = (u8)_scale;
+ offsetOrBaseReg = (u16)rmReg;
+ indexReg = (u16)scaledReg;
+ //if scale == 0 never mind offsetting
+ offset = _offset;
+ }
+ bool operator==(const OpArg &b) const
+ {
+ return operandReg == b.operandReg && scale == b.scale && offsetOrBaseReg == b.offsetOrBaseReg &&
+ indexReg == b.indexReg && offset == b.offset;
+ }
+ void WriteRex(XEmitter *emit, int opBits, int bits, int customOp = -1) const;
+ void WriteVex(XEmitter* emit, X64Reg regOp1, X64Reg regOp2, int L, int pp, int mmmmm, int W = 0) const;
+ void WriteRest(XEmitter *emit, int extraBytes=0, X64Reg operandReg=INVALID_REG, bool warn_64bit_offset = true) const;
+ void WriteFloatModRM(XEmitter *emit, FloatOp op);
+ void WriteSingleByteOp(XEmitter *emit, u8 op, X64Reg operandReg, int bits);
+ // This one is public - must be written to
+ u64 offset; // use RIP-relative as much as possible - 64-bit immediates are not available.
+ u16 operandReg;
+
+ void WriteNormalOp(XEmitter *emit, bool toRM, NormalOp op, const OpArg &operand, int bits) const;
+ bool IsImm() const {return scale == SCALE_IMM8 || scale == SCALE_IMM16 || scale == SCALE_IMM32 || scale == SCALE_IMM64;}
+ bool IsSimpleReg() const {return scale == SCALE_NONE;}
+ bool IsSimpleReg(X64Reg reg) const
+ {
+ if (!IsSimpleReg())
+ return false;
+ return GetSimpleReg() == reg;
+ }
+
+ bool CanDoOpWith(const OpArg &other) const
+ {
+ if (IsSimpleReg()) return true;
+ if (!IsSimpleReg() && !other.IsSimpleReg() && !other.IsImm()) return false;
+ return true;
+ }
+
+ int GetImmBits() const
+ {
+ switch (scale)
+ {
+ case SCALE_IMM8: return 8;
+ case SCALE_IMM16: return 16;
+ case SCALE_IMM32: return 32;
+ case SCALE_IMM64: return 64;
+ default: return -1;
+ }
+ }
+
+ void SetImmBits(int bits) {
+ switch (bits)
+ {
+ case 8: scale = SCALE_IMM8; break;
+ case 16: scale = SCALE_IMM16; break;
+ case 32: scale = SCALE_IMM32; break;
+ case 64: scale = SCALE_IMM64; break;
+ }
+ }
+
+ X64Reg GetSimpleReg() const
+ {
+ if (scale == SCALE_NONE)
+ return (X64Reg)offsetOrBaseReg;
+ else
+ return INVALID_REG;
+ }
+
+ u32 GetImmValue() const {
+ return (u32)offset;
+ }
+
+ // For loops.
+ void IncreaseOffset(int sz) {
+ offset += sz;
+ }
+
+private:
+ u8 scale;
+ u16 offsetOrBaseReg;
+ u16 indexReg;
+};
+
+inline OpArg M(const void *ptr) {return OpArg((u64)ptr, (int)SCALE_RIP);}
+template <typename T>
+inline OpArg M(const T *ptr) {return OpArg((u64)(const void *)ptr, (int)SCALE_RIP);}
+inline OpArg R(X64Reg value) {return OpArg(0, SCALE_NONE, value);}
+inline OpArg MatR(X64Reg value) {return OpArg(0, SCALE_ATREG, value);}
+
+inline OpArg MDisp(X64Reg value, int offset)
+{
+ return OpArg((u32)offset, SCALE_ATREG, value);
+}
+
+inline OpArg MComplex(X64Reg base, X64Reg scaled, int scale, int offset)
+{
+ return OpArg(offset, scale, base, scaled);
+}
+
+inline OpArg MScaled(X64Reg scaled, int scale, int offset)
+{
+ if (scale == SCALE_1)
+ return OpArg(offset, SCALE_ATREG, scaled);
+ else
+ return OpArg(offset, scale | 0x20, RAX, scaled);
+}
+
+inline OpArg MRegSum(X64Reg base, X64Reg offset)
+{
+ return MComplex(base, offset, 1, 0);
+}
+
+inline OpArg Imm8 (u8 imm) {return OpArg(imm, SCALE_IMM8);}
+inline OpArg Imm16(u16 imm) {return OpArg(imm, SCALE_IMM16);} //rarely used
+inline OpArg Imm32(u32 imm) {return OpArg(imm, SCALE_IMM32);}
+inline OpArg Imm64(u64 imm) {return OpArg(imm, SCALE_IMM64);}
+inline OpArg UImmAuto(u32 imm) {
+ return OpArg(imm, imm >= 128 ? SCALE_IMM32 : SCALE_IMM8);
+}
+inline OpArg SImmAuto(s32 imm) {
+ return OpArg(imm, (imm >= 128 || imm < -128) ? SCALE_IMM32 : SCALE_IMM8);
+}
+
+#ifdef _ARCH_64
+inline OpArg ImmPtr(const void* imm) {return Imm64((u64)imm);}
+#else
+inline OpArg ImmPtr(const void* imm) {return Imm32((u32)imm);}
+#endif
+
+inline u32 PtrOffset(const void* ptr, const void* base)
+{
+#ifdef _ARCH_64
+ s64 distance = (s64)ptr-(s64)base;
+ if (distance >= 0x80000000LL ||
+ distance < -0x80000000LL)
+ {
+ ASSERT_MSG(0, "pointer offset out of range");
+ return 0;
+ }
+
+ return (u32)distance;
+#else
+ return (u32)ptr-(u32)base;
+#endif
+}
+
+//usage: int a[]; ARRAY_OFFSET(a,10)
+#define ARRAY_OFFSET(array,index) ((u32)((u64)&(array)[index]-(u64)&(array)[0]))
+//usage: struct {int e;} s; STRUCT_OFFSET(s,e)
+#define STRUCT_OFFSET(str,elem) ((u32)((u64)&(str).elem-(u64)&(str)))
+
+struct FixupBranch
+{
+ u8 *ptr;
+ int type; //0 = 8bit 1 = 32bit
+};
+
+enum SSECompare
+{
+ EQ = 0,
+ LT,
+ LE,
+ UNORD,
+ NEQ,
+ NLT,
+ NLE,
+ ORD,
+};
+
+class XEmitter
+{
+ friend struct OpArg; // for Write8 etc
+private:
+ u8 *code;
+ bool flags_locked;
+
+ void CheckFlags();
+
+ void Rex(int w, int r, int x, int b);
+ void WriteSimple1Byte(int bits, u8 byte, X64Reg reg);
+ void WriteSimple2Byte(int bits, u8 byte1, u8 byte2, X64Reg reg);
+ void WriteMulDivType(int bits, OpArg src, int ext);
+ void WriteBitSearchType(int bits, X64Reg dest, OpArg src, u8 byte2, bool rep = false);
+ void WriteShift(int bits, OpArg dest, const OpArg& shift, int ext);
+ void WriteBitTest(int bits, const OpArg& dest, const OpArg& index, int ext);
+ void WriteMXCSR(OpArg arg, int ext);
+ void WriteSSEOp(u8 opPrefix, u16 op, X64Reg regOp, OpArg arg, int extrabytes = 0);
+ void WriteSSSE3Op(u8 opPrefix, u16 op, X64Reg regOp, const OpArg& arg, int extrabytes = 0);
+ void WriteSSE41Op(u8 opPrefix, u16 op, X64Reg regOp, const OpArg& arg, int extrabytes = 0);
+ void WriteAVXOp(u8 opPrefix, u16 op, X64Reg regOp, const OpArg& arg, int extrabytes = 0);
+ void WriteAVXOp(u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg, int extrabytes = 0);
+ void WriteVEXOp(int size, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg, int extrabytes = 0);
+ void WriteBMI1Op(int size, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg, int extrabytes = 0);
+ void WriteBMI2Op(int size, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg, int extrabytes = 0);
+ void WriteFloatLoadStore(int bits, FloatOp op, FloatOp op_80b, const OpArg& arg);
+ void WriteNormalOp(XEmitter *emit, int bits, NormalOp op, const OpArg& a1, const OpArg& a2);
+
+ void ABI_CalculateFrameSize(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size, size_t* shadowp, size_t* subtractionp, size_t* xmm_offsetp);
+
+protected:
+ void Write8(u8 value);
+ void Write16(u16 value);
+ void Write32(u32 value);
+ void Write64(u64 value);
+
+public:
+ XEmitter() { code = nullptr; flags_locked = false; }
+ XEmitter(u8 *code_ptr) { code = code_ptr; flags_locked = false; }
+ virtual ~XEmitter() {}
+
+ void WriteModRM(int mod, int rm, int reg);
+ void WriteSIB(int scale, int index, int base);
+
+ void SetCodePtr(u8 *ptr);
+ void ReserveCodeSpace(int bytes);
+ const u8 *AlignCode4();
+ const u8 *AlignCode16();
+ const u8 *AlignCodePage();
+ const u8 *GetCodePtr() const;
+ u8 *GetWritableCodePtr();
+
+ void LockFlags() { flags_locked = true; }
+ void UnlockFlags() { flags_locked = false; }
+
+ // Looking for one of these? It's BANNED!! Some instructions are slow on modern CPU
+ // INC, DEC, LOOP, LOOPNE, LOOPE, ENTER, LEAVE, XCHG, XLAT, REP MOVSB/MOVSD, REP SCASD + other string instr.,
+ // INC and DEC are slow on Intel Core, but not on AMD. They create a
+ // false flag dependency because they only update a subset of the flags.
+ // XCHG is SLOW and should be avoided.
+
+ // Debug breakpoint
+ void INT3();
+
+ // Do nothing
+ void NOP(size_t count = 1);
+
+ // Save energy in wait-loops on P4 only. Probably not too useful.
+ void PAUSE();
+
+ // Flag control
+ void STC();
+ void CLC();
+ void CMC();
+
+ // These two can not be executed in 64-bit mode on early Intel 64-bit CPU:s, only on Core2 and AMD!
+ void LAHF(); // 3 cycle vector path
+ void SAHF(); // direct path fast
+
+
+ // Stack control
+ void PUSH(X64Reg reg);
+ void POP(X64Reg reg);
+ void PUSH(int bits, const OpArg& reg);
+ void POP(int bits, const OpArg& reg);
+ void PUSHF();
+ void POPF();
+
+ // Flow control
+ void RET();
+ void RET_FAST();
+ void UD2();
+ FixupBranch J(bool force5bytes = false);
+
+ void JMP(const u8* addr, bool force5Bytes = false);
+ void JMPptr(const OpArg& arg);
+ void JMPself(); //infinite loop!
+#ifdef CALL
+#undef CALL
+#endif
+ void CALL(const void* fnptr);
+ void CALLptr(OpArg arg);
+
+ FixupBranch J_CC(CCFlags conditionCode, bool force5bytes = false);
+ void J_CC(CCFlags conditionCode, const u8* addr, bool force5Bytes = false);
+
+ void SetJumpTarget(const FixupBranch& branch);
+
+ void SETcc(CCFlags flag, OpArg dest);
+ // Note: CMOV brings small if any benefit on current cpus.
+ void CMOVcc(int bits, X64Reg dest, OpArg src, CCFlags flag);
+
+ // Fences
+ void LFENCE();
+ void MFENCE();
+ void SFENCE();
+
+ // Bit scan
+ void BSF(int bits, X64Reg dest, const OpArg& src); // Bottom bit to top bit
+ void BSR(int bits, X64Reg dest, const OpArg& src); // Top bit to bottom bit
+
+ // Cache control
+ enum PrefetchLevel
+ {
+ PF_NTA, //Non-temporal (data used once and only once)
+ PF_T0, //All cache levels
+ PF_T1, //Levels 2+ (aliased to T0 on AMD)
+ PF_T2, //Levels 3+ (aliased to T0 on AMD)
+ };
+ void PREFETCH(PrefetchLevel level, OpArg arg);
+ void MOVNTI(int bits, const OpArg& dest, X64Reg src);
+ void MOVNTDQ(const OpArg& arg, X64Reg regOp);
+ void MOVNTPS(const OpArg& arg, X64Reg regOp);
+ void MOVNTPD(const OpArg& arg, X64Reg regOp);
+
+ // Multiplication / division
+ void MUL(int bits, const OpArg& src); //UNSIGNED
+ void IMUL(int bits, const OpArg& src); //SIGNED
+ void IMUL(int bits, X64Reg regOp, const OpArg& src);
+ void IMUL(int bits, X64Reg regOp, const OpArg& src, const OpArg& imm);
+ void DIV(int bits, const OpArg& src);
+ void IDIV(int bits, const OpArg& src);
+
+ // Shift
+ void ROL(int bits, const OpArg& dest, const OpArg& shift);
+ void ROR(int bits, const OpArg& dest, const OpArg& shift);
+ void RCL(int bits, const OpArg& dest, const OpArg& shift);
+ void RCR(int bits, const OpArg& dest, const OpArg& shift);
+ void SHL(int bits, const OpArg& dest, const OpArg& shift);
+ void SHR(int bits, const OpArg& dest, const OpArg& shift);
+ void SAR(int bits, const OpArg& dest, const OpArg& shift);
+
+ // Bit Test
+ void BT(int bits, const OpArg& dest, const OpArg& index);
+ void BTS(int bits, const OpArg& dest, const OpArg& index);
+ void BTR(int bits, const OpArg& dest, const OpArg& index);
+ void BTC(int bits, const OpArg& dest, const OpArg& index);
+
+ // Double-Precision Shift
+ void SHRD(int bits, const OpArg& dest, const OpArg& src, const OpArg& shift);
+ void SHLD(int bits, const OpArg& dest, const OpArg& src, const OpArg& shift);
+
+ // Extend EAX into EDX in various ways
+ void CWD(int bits = 16);
+ void CDQ() {CWD(32);}
+ void CQO() {CWD(64);}
+ void CBW(int bits = 8);
+ void CWDE() {CBW(16);}
+ void CDQE() {CBW(32);}
+
+ // Load effective address
+ void LEA(int bits, X64Reg dest, OpArg src);
+
+ // Integer arithmetic
+ void NEG(int bits, const OpArg& src);
+ void ADD(int bits, const OpArg& a1, const OpArg& a2);
+ void ADC(int bits, const OpArg& a1, const OpArg& a2);
+ void SUB(int bits, const OpArg& a1, const OpArg& a2);
+ void SBB(int bits, const OpArg& a1, const OpArg& a2);
+ void AND(int bits, const OpArg& a1, const OpArg& a2);
+ void CMP(int bits, const OpArg& a1, const OpArg& a2);
+
+ // Bit operations
+ void NOT (int bits, const OpArg& src);
+ void OR(int bits, const OpArg& a1, const OpArg& a2);
+ void XOR(int bits, const OpArg& a1, const OpArg& a2);
+ void MOV(int bits, const OpArg& a1, const OpArg& a2);
+ void TEST(int bits, const OpArg& a1, const OpArg& a2);
+
+ // Are these useful at all? Consider removing.
+ void XCHG(int bits, const OpArg& a1, const OpArg& a2);
+ void XCHG_AHAL();
+
+ // Byte swapping (32 and 64-bit only).
+ void BSWAP(int bits, X64Reg reg);
+
+ // Sign/zero extension
+ void MOVSX(int dbits, int sbits, X64Reg dest, OpArg src); //automatically uses MOVSXD if necessary
+ void MOVZX(int dbits, int sbits, X64Reg dest, OpArg src);
+
+ // Available only on Atom or >= Haswell so far. Test with GetCPUCaps().movbe.
+ void MOVBE(int dbits, const OpArg& dest, const OpArg& src);
+
+ // Available only on AMD >= Phenom or Intel >= Haswell
+ void LZCNT(int bits, X64Reg dest, const OpArg& src);
+ // Note: this one is actually part of BMI1
+ void TZCNT(int bits, X64Reg dest, const OpArg& src);
+
+ // WARNING - These two take 11-13 cycles and are VectorPath! (AMD64)
+ void STMXCSR(const OpArg& memloc);
+ void LDMXCSR(const OpArg& memloc);
+
+ // Prefixes
+ void LOCK();
+ void REP();
+ void REPNE();
+ void FSOverride();
+ void GSOverride();
+
+ // x87
+ enum x87StatusWordBits {
+ x87_InvalidOperation = 0x1,
+ x87_DenormalizedOperand = 0x2,
+ x87_DivisionByZero = 0x4,
+ x87_Overflow = 0x8,
+ x87_Underflow = 0x10,
+ x87_Precision = 0x20,
+ x87_StackFault = 0x40,
+ x87_ErrorSummary = 0x80,
+ x87_C0 = 0x100,
+ x87_C1 = 0x200,
+ x87_C2 = 0x400,
+ x87_TopOfStack = 0x2000 | 0x1000 | 0x800,
+ x87_C3 = 0x4000,
+ x87_FPUBusy = 0x8000,
+ };
+
+ void FLD(int bits, const OpArg& src);
+ void FST(int bits, const OpArg& dest);
+ void FSTP(int bits, const OpArg& dest);
+ void FNSTSW_AX();
+ void FWAIT();
+
+ // SSE/SSE2: Floating point arithmetic
+ void ADDSS(X64Reg regOp, const OpArg& arg);
+ void ADDSD(X64Reg regOp, const OpArg& arg);
+ void SUBSS(X64Reg regOp, const OpArg& arg);
+ void SUBSD(X64Reg regOp, const OpArg& arg);
+ void MULSS(X64Reg regOp, const OpArg& arg);
+ void MULSD(X64Reg regOp, const OpArg& arg);
+ void DIVSS(X64Reg regOp, const OpArg& arg);
+ void DIVSD(X64Reg regOp, const OpArg& arg);
+ void MINSS(X64Reg regOp, const OpArg& arg);
+ void MINSD(X64Reg regOp, const OpArg& arg);
+ void MAXSS(X64Reg regOp, const OpArg& arg);
+ void MAXSD(X64Reg regOp, const OpArg& arg);
+ void SQRTSS(X64Reg regOp, const OpArg& arg);
+ void SQRTSD(X64Reg regOp, const OpArg& arg);
+ void RCPSS(X64Reg regOp, const OpArg& arg);
+ void RSQRTSS(X64Reg regOp, const OpArg& arg);
+
+ // SSE/SSE2: Floating point bitwise (yes)
+ void CMPSS(X64Reg regOp, const OpArg& arg, u8 compare);
+ void CMPSD(X64Reg regOp, const OpArg& arg, u8 compare);
+
+ void CMPEQSS(X64Reg regOp, const OpArg& arg) { CMPSS(regOp, arg, CMP_EQ); }
+ void CMPLTSS(X64Reg regOp, const OpArg& arg) { CMPSS(regOp, arg, CMP_LT); }
+ void CMPLESS(X64Reg regOp, const OpArg& arg) { CMPSS(regOp, arg, CMP_LE); }
+ void CMPUNORDSS(X64Reg regOp, const OpArg& arg) { CMPSS(regOp, arg, CMP_UNORD); }
+ void CMPNEQSS(X64Reg regOp, const OpArg& arg) { CMPSS(regOp, arg, CMP_NEQ); }
+ void CMPNLTSS(X64Reg regOp, const OpArg& arg) { CMPSS(regOp, arg, CMP_NLT); }
+ void CMPORDSS(X64Reg regOp, const OpArg& arg) { CMPSS(regOp, arg, CMP_ORD); }
+
+ // SSE/SSE2: Floating point packed arithmetic (x4 for float, x2 for double)
+ void ADDPS(X64Reg regOp, const OpArg& arg);
+ void ADDPD(X64Reg regOp, const OpArg& arg);
+ void SUBPS(X64Reg regOp, const OpArg& arg);
+ void SUBPD(X64Reg regOp, const OpArg& arg);
+ void CMPPS(X64Reg regOp, const OpArg& arg, u8 compare);
+ void CMPPD(X64Reg regOp, const OpArg& arg, u8 compare);
+ void MULPS(X64Reg regOp, const OpArg& arg);
+ void MULPD(X64Reg regOp, const OpArg& arg);
+ void DIVPS(X64Reg regOp, const OpArg& arg);
+ void DIVPD(X64Reg regOp, const OpArg& arg);
+ void MINPS(X64Reg regOp, const OpArg& arg);
+ void MINPD(X64Reg regOp, const OpArg& arg);
+ void MAXPS(X64Reg regOp, const OpArg& arg);
+ void MAXPD(X64Reg regOp, const OpArg& arg);
+ void SQRTPS(X64Reg regOp, const OpArg& arg);
+ void SQRTPD(X64Reg regOp, const OpArg& arg);
+ void RCPPS(X64Reg regOp, const OpArg& arg);
+ void RSQRTPS(X64Reg regOp, const OpArg& arg);
+
+ // SSE/SSE2: Floating point packed bitwise (x4 for float, x2 for double)
+ void ANDPS(X64Reg regOp, const OpArg& arg);
+ void ANDPD(X64Reg regOp, const OpArg& arg);
+ void ANDNPS(X64Reg regOp, const OpArg& arg);
+ void ANDNPD(X64Reg regOp, const OpArg& arg);
+ void ORPS(X64Reg regOp, const OpArg& arg);
+ void ORPD(X64Reg regOp, const OpArg& arg);
+ void XORPS(X64Reg regOp, const OpArg& arg);
+ void XORPD(X64Reg regOp, const OpArg& arg);
+
+ // SSE/SSE2: Shuffle components. These are tricky - see Intel documentation.
+ void SHUFPS(X64Reg regOp, const OpArg& arg, u8 shuffle);
+ void SHUFPD(X64Reg regOp, const OpArg& arg, u8 shuffle);
+
+ // SSE/SSE2: Useful alternative to shuffle in some cases.
+ void MOVDDUP(X64Reg regOp, const OpArg& arg);
+
+ // SSE3: Horizontal operations in SIMD registers. Very slow! shufps-based code beats it handily on Ivy.
+ void HADDPS(X64Reg dest, const OpArg& src);
+
+ // SSE4: Further horizontal operations - dot products. These are weirdly flexible, the arg contains both a read mask and a write "mask".
+ void DPPS(X64Reg dest, const OpArg& src, u8 arg);
+
+ void UNPCKLPS(X64Reg dest, const OpArg& src);
+ void UNPCKHPS(X64Reg dest, const OpArg& src);
+ void UNPCKLPD(X64Reg dest, const OpArg& src);
+ void UNPCKHPD(X64Reg dest, const OpArg& src);
+
+ // SSE/SSE2: Compares.
+ void COMISS(X64Reg regOp, const OpArg& arg);
+ void COMISD(X64Reg regOp, const OpArg& arg);
+ void UCOMISS(X64Reg regOp, const OpArg& arg);
+ void UCOMISD(X64Reg regOp, const OpArg& arg);
+
+ // SSE/SSE2: Moves. Use the right data type for your data, in most cases.
+ void MOVAPS(X64Reg regOp, const OpArg& arg);
+ void MOVAPD(X64Reg regOp, const OpArg& arg);
+ void MOVAPS(const OpArg& arg, X64Reg regOp);
+ void MOVAPD(const OpArg& arg, X64Reg regOp);
+
+ void MOVUPS(X64Reg regOp, const OpArg& arg);
+ void MOVUPD(X64Reg regOp, const OpArg& arg);
+ void MOVUPS(const OpArg& arg, X64Reg regOp);
+ void MOVUPD(const OpArg& arg, X64Reg regOp);
+
+ void MOVDQA(X64Reg regOp, const OpArg& arg);
+ void MOVDQA(const OpArg& arg, X64Reg regOp);
+ void MOVDQU(X64Reg regOp, const OpArg& arg);
+ void MOVDQU(const OpArg& arg, X64Reg regOp);
+
+ void MOVSS(X64Reg regOp, const OpArg& arg);
+ void MOVSD(X64Reg regOp, const OpArg& arg);
+ void MOVSS(const OpArg& arg, X64Reg regOp);
+ void MOVSD(const OpArg& arg, X64Reg regOp);
+
+ void MOVLPS(X64Reg regOp, const OpArg& arg);
+ void MOVLPD(X64Reg regOp, const OpArg& arg);
+ void MOVLPS(const OpArg& arg, X64Reg regOp);
+ void MOVLPD(const OpArg& arg, X64Reg regOp);
+
+ void MOVHPS(X64Reg regOp, const OpArg& arg);
+ void MOVHPD(X64Reg regOp, const OpArg& arg);
+ void MOVHPS(const OpArg& arg, X64Reg regOp);
+ void MOVHPD(const OpArg& arg, X64Reg regOp);
+
+ void MOVHLPS(X64Reg regOp1, X64Reg regOp2);
+ void MOVLHPS(X64Reg regOp1, X64Reg regOp2);
+
+ void MOVD_xmm(X64Reg dest, const OpArg& arg);
+ void MOVQ_xmm(X64Reg dest, OpArg arg);
+ void MOVD_xmm(const OpArg& arg, X64Reg src);
+ void MOVQ_xmm(OpArg arg, X64Reg src);
+
+ // SSE/SSE2: Generates a mask from the high bits of the components of the packed register in question.
+ void MOVMSKPS(X64Reg dest, const OpArg& arg);
+ void MOVMSKPD(X64Reg dest, const OpArg& arg);
+
+ // SSE2: Selective byte store, mask in src register. EDI/RDI specifies store address. This is a weird one.
+ void MASKMOVDQU(X64Reg dest, X64Reg src);
+ void LDDQU(X64Reg dest, const OpArg& src);
+
+ // SSE/SSE2: Data type conversions.
+ void CVTPS2PD(X64Reg dest, const OpArg& src);
+ void CVTPD2PS(X64Reg dest, const OpArg& src);
+ void CVTSS2SD(X64Reg dest, const OpArg& src);
+ void CVTSI2SS(X64Reg dest, const OpArg& src);
+ void CVTSD2SS(X64Reg dest, const OpArg& src);
+ void CVTSI2SD(X64Reg dest, const OpArg& src);
+ void CVTDQ2PD(X64Reg regOp, const OpArg& arg);
+ void CVTPD2DQ(X64Reg regOp, const OpArg& arg);
+ void CVTDQ2PS(X64Reg regOp, const OpArg& arg);
+ void CVTPS2DQ(X64Reg regOp, const OpArg& arg);
+
+ void CVTTPS2DQ(X64Reg regOp, const OpArg& arg);
+ void CVTTPD2DQ(X64Reg regOp, const OpArg& arg);
+
+ // Destinations are X64 regs (rax, rbx, ...) for these instructions.
+ void CVTSS2SI(X64Reg xregdest, const OpArg& src);
+ void CVTSD2SI(X64Reg xregdest, const OpArg& src);
+ void CVTTSS2SI(X64Reg xregdest, const OpArg& arg);
+ void CVTTSD2SI(X64Reg xregdest, const OpArg& arg);
+
+ // SSE2: Packed integer instructions
+ void PACKSSDW(X64Reg dest, const OpArg& arg);
+ void PACKSSWB(X64Reg dest, const OpArg& arg);
+ void PACKUSDW(X64Reg dest, const OpArg& arg);
+ void PACKUSWB(X64Reg dest, const OpArg& arg);
+
+ void PUNPCKLBW(X64Reg dest, const OpArg &arg);
+ void PUNPCKLWD(X64Reg dest, const OpArg &arg);
+ void PUNPCKLDQ(X64Reg dest, const OpArg &arg);
+ void PUNPCKLQDQ(X64Reg dest, const OpArg &arg);
+
+ void PTEST(X64Reg dest, const OpArg& arg);
+ void PAND(X64Reg dest, const OpArg& arg);
+ void PANDN(X64Reg dest, const OpArg& arg);
+ void PXOR(X64Reg dest, const OpArg& arg);
+ void POR(X64Reg dest, const OpArg& arg);
+
+ void PADDB(X64Reg dest, const OpArg& arg);
+ void PADDW(X64Reg dest, const OpArg& arg);
+ void PADDD(X64Reg dest, const OpArg& arg);
+ void PADDQ(X64Reg dest, const OpArg& arg);
+
+ void PADDSB(X64Reg dest, const OpArg& arg);
+ void PADDSW(X64Reg dest, const OpArg& arg);
+ void PADDUSB(X64Reg dest, const OpArg& arg);
+ void PADDUSW(X64Reg dest, const OpArg& arg);
+
+ void PSUBB(X64Reg dest, const OpArg& arg);
+ void PSUBW(X64Reg dest, const OpArg& arg);
+ void PSUBD(X64Reg dest, const OpArg& arg);
+ void PSUBQ(X64Reg dest, const OpArg& arg);
+
+ void PSUBSB(X64Reg dest, const OpArg& arg);
+ void PSUBSW(X64Reg dest, const OpArg& arg);
+ void PSUBUSB(X64Reg dest, const OpArg& arg);
+ void PSUBUSW(X64Reg dest, const OpArg& arg);
+
+ void PAVGB(X64Reg dest, const OpArg& arg);
+ void PAVGW(X64Reg dest, const OpArg& arg);
+
+ void PCMPEQB(X64Reg dest, const OpArg& arg);
+ void PCMPEQW(X64Reg dest, const OpArg& arg);
+ void PCMPEQD(X64Reg dest, const OpArg& arg);
+
+ void PCMPGTB(X64Reg dest, const OpArg& arg);
+ void PCMPGTW(X64Reg dest, const OpArg& arg);
+ void PCMPGTD(X64Reg dest, const OpArg& arg);
+
+ void PEXTRW(X64Reg dest, const OpArg& arg, u8 subreg);
+ void PINSRW(X64Reg dest, const OpArg& arg, u8 subreg);
+
+ void PMADDWD(X64Reg dest, const OpArg& arg);
+ void PSADBW(X64Reg dest, const OpArg& arg);
+
+ void PMAXSW(X64Reg dest, const OpArg& arg);
+ void PMAXUB(X64Reg dest, const OpArg& arg);
+ void PMINSW(X64Reg dest, const OpArg& arg);
+ void PMINUB(X64Reg dest, const OpArg& arg);
+ // SSE4: More MAX/MIN instructions.
+ void PMINSB(X64Reg dest, const OpArg& arg);
+ void PMINSD(X64Reg dest, const OpArg& arg);
+ void PMINUW(X64Reg dest, const OpArg& arg);
+ void PMINUD(X64Reg dest, const OpArg& arg);
+ void PMAXSB(X64Reg dest, const OpArg& arg);
+ void PMAXSD(X64Reg dest, const OpArg& arg);
+ void PMAXUW(X64Reg dest, const OpArg& arg);
+ void PMAXUD(X64Reg dest, const OpArg& arg);
+
+ void PMOVMSKB(X64Reg dest, const OpArg& arg);
+ void PSHUFD(X64Reg dest, const OpArg& arg, u8 shuffle);
+ void PSHUFB(X64Reg dest, const OpArg& arg);
+
+ void PSHUFLW(X64Reg dest, const OpArg& arg, u8 shuffle);
+ void PSHUFHW(X64Reg dest, const OpArg& arg, u8 shuffle);
+
+ void PSRLW(X64Reg reg, int shift);
+ void PSRLD(X64Reg reg, int shift);
+ void PSRLQ(X64Reg reg, int shift);
+ void PSRLQ(X64Reg reg, const OpArg& arg);
+ void PSRLDQ(X64Reg reg, int shift);
+
+ void PSLLW(X64Reg reg, int shift);
+ void PSLLD(X64Reg reg, int shift);
+ void PSLLQ(X64Reg reg, int shift);
+ void PSLLDQ(X64Reg reg, int shift);
+
+ void PSRAW(X64Reg reg, int shift);
+ void PSRAD(X64Reg reg, int shift);
+
+ // SSE4: data type conversions
+ void PMOVSXBW(X64Reg dest, const OpArg& arg);
+ void PMOVSXBD(X64Reg dest, const OpArg& arg);
+ void PMOVSXBQ(X64Reg dest, const OpArg& arg);
+ void PMOVSXWD(X64Reg dest, const OpArg& arg);
+ void PMOVSXWQ(X64Reg dest, const OpArg& arg);
+ void PMOVSXDQ(X64Reg dest, const OpArg& arg);
+ void PMOVZXBW(X64Reg dest, const OpArg& arg);
+ void PMOVZXBD(X64Reg dest, const OpArg& arg);
+ void PMOVZXBQ(X64Reg dest, const OpArg& arg);
+ void PMOVZXWD(X64Reg dest, const OpArg& arg);
+ void PMOVZXWQ(X64Reg dest, const OpArg& arg);
+ void PMOVZXDQ(X64Reg dest, const OpArg& arg);
+
+ // SSE4: variable blend instructions (xmm0 implicit argument)
+ void PBLENDVB(X64Reg dest, const OpArg& arg);
+ void BLENDVPS(X64Reg dest, const OpArg& arg);
+ void BLENDVPD(X64Reg dest, const OpArg& arg);
+ void BLENDPS(X64Reg dest, const OpArg& arg, u8 blend);
+ void BLENDPD(X64Reg dest, const OpArg& arg, u8 blend);
+
+ // SSE4: rounding (see FloatRound for mode or use ROUNDNEARSS, etc. helpers.)
+ void ROUNDSS(X64Reg dest, const OpArg& arg, u8 mode);
+ void ROUNDSD(X64Reg dest, const OpArg& arg, u8 mode);
+ void ROUNDPS(X64Reg dest, const OpArg& arg, u8 mode);
+ void ROUNDPD(X64Reg dest, const OpArg& arg, u8 mode);
+
+ void ROUNDNEARSS(X64Reg dest, const OpArg& arg) { ROUNDSS(dest, arg, FROUND_NEAREST); }
+ void ROUNDFLOORSS(X64Reg dest, const OpArg& arg) { ROUNDSS(dest, arg, FROUND_FLOOR); }
+ void ROUNDCEILSS(X64Reg dest, const OpArg& arg) { ROUNDSS(dest, arg, FROUND_CEIL); }
+ void ROUNDZEROSS(X64Reg dest, const OpArg& arg) { ROUNDSS(dest, arg, FROUND_ZERO); }
+
+ void ROUNDNEARSD(X64Reg dest, const OpArg& arg) { ROUNDSD(dest, arg, FROUND_NEAREST); }
+ void ROUNDFLOORSD(X64Reg dest, const OpArg& arg) { ROUNDSD(dest, arg, FROUND_FLOOR); }
+ void ROUNDCEILSD(X64Reg dest, const OpArg& arg) { ROUNDSD(dest, arg, FROUND_CEIL); }
+ void ROUNDZEROSD(X64Reg dest, const OpArg& arg) { ROUNDSD(dest, arg, FROUND_ZERO); }
+
+ void ROUNDNEARPS(X64Reg dest, const OpArg& arg) { ROUNDPS(dest, arg, FROUND_NEAREST); }
+ void ROUNDFLOORPS(X64Reg dest, const OpArg& arg) { ROUNDPS(dest, arg, FROUND_FLOOR); }
+ void ROUNDCEILPS(X64Reg dest, const OpArg& arg) { ROUNDPS(dest, arg, FROUND_CEIL); }
+ void ROUNDZEROPS(X64Reg dest, const OpArg& arg) { ROUNDPS(dest, arg, FROUND_ZERO); }
+
+ void ROUNDNEARPD(X64Reg dest, const OpArg& arg) { ROUNDPD(dest, arg, FROUND_NEAREST); }
+ void ROUNDFLOORPD(X64Reg dest, const OpArg& arg) { ROUNDPD(dest, arg, FROUND_FLOOR); }
+ void ROUNDCEILPD(X64Reg dest, const OpArg& arg) { ROUNDPD(dest, arg, FROUND_CEIL); }
+ void ROUNDZEROPD(X64Reg dest, const OpArg& arg) { ROUNDPD(dest, arg, FROUND_ZERO); }
+
+ // AVX
+ void VADDSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VSUBSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VMULSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VDIVSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VADDPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VSUBPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VMULPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VDIVPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VSQRTSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VSHUFPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg, u8 shuffle);
+ void VUNPCKLPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VUNPCKHPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+
+ void VANDPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VANDPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VANDNPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VANDNPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VORPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VORPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VXORPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VXORPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+
+ void VPAND(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VPANDN(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VPOR(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VPXOR(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+
+ // FMA3
+ void VFMADD132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADD213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADD231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADD132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADD213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADD231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADD132SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADD213SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADD231SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADD132SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADD213SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADD231SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUB132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUB213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUB231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUB132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUB213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUB231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUB132SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUB213SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUB231SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUB132SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUB213SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUB231SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMADD132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMADD213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMADD231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMADD132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMADD213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMADD231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMADD132SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMADD213SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMADD231SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMADD132SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMADD213SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMADD231SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMSUB132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMSUB213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMSUB231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMSUB132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMSUB213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMSUB231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMSUB132SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMSUB213SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMSUB231SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMSUB132SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMSUB213SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFNMSUB231SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADDSUB132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADDSUB213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADDSUB231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADDSUB132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADDSUB213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMADDSUB231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUBADD132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUBADD213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUBADD231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUBADD132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUBADD213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void VFMSUBADD231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+
+ // VEX GPR instructions
+ void SARX(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2);
+ void SHLX(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2);
+ void SHRX(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2);
+ void RORX(int bits, X64Reg regOp, const OpArg& arg, u8 rotate);
+ void PEXT(int bits, X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void PDEP(int bits, X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void MULX(int bits, X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+ void BZHI(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2);
+ void BLSR(int bits, X64Reg regOp, const OpArg& arg);
+ void BLSMSK(int bits, X64Reg regOp, const OpArg& arg);
+ void BLSI(int bits, X64Reg regOp, const OpArg& arg);
+ void BEXTR(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2);
+ void ANDN(int bits, X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
+
+ void RDTSC();
+
+ // Utility functions
+ // The difference between this and CALL is that this aligns the stack
+ // where appropriate.
+ void ABI_CallFunction(const void* func);
+ template <typename T>
+ void ABI_CallFunction(T (*func)()) {
+ ABI_CallFunction((const void*)func);
+ }
+
+ void ABI_CallFunction(const u8* func) {
+ ABI_CallFunction((const void*)func);
+ }
+ void ABI_CallFunctionC16(const void* func, u16 param1);
+ void ABI_CallFunctionCC16(const void* func, u32 param1, u16 param2);
+
+
+ // These only support u32 parameters, but that's enough for a lot of uses.
+ // These will destroy the 1 or 2 first "parameter regs".
+ void ABI_CallFunctionC(const void* func, u32 param1);
+ void ABI_CallFunctionCC(const void* func, u32 param1, u32 param2);
+ void ABI_CallFunctionCCC(const void* func, u32 param1, u32 param2, u32 param3);
+ void ABI_CallFunctionCCP(const void* func, u32 param1, u32 param2, void* param3);
+ void ABI_CallFunctionCCCP(const void* func, u32 param1, u32 param2, u32 param3, void* param4);
+ void ABI_CallFunctionP(const void* func, void* param1);
+ void ABI_CallFunctionPA(const void* func, void* param1, const OpArg& arg2);
+ void ABI_CallFunctionPAA(const void* func, void* param1, const OpArg& arg2, const OpArg& arg3);
+ void ABI_CallFunctionPPC(const void* func, void* param1, void* param2, u32 param3);
+ void ABI_CallFunctionAC(const void* func, const OpArg& arg1, u32 param2);
+ void ABI_CallFunctionACC(const void* func, const OpArg& arg1, u32 param2, u32 param3);
+ void ABI_CallFunctionA(const void* func, const OpArg& arg1);
+ void ABI_CallFunctionAA(const void* func, const OpArg& arg1, const OpArg& arg2);
+
+ // Pass a register as a parameter.
+ void ABI_CallFunctionR(const void* func, X64Reg reg1);
+ void ABI_CallFunctionRR(const void* func, X64Reg reg1, X64Reg reg2);
+
+ template <typename Tr, typename T1>
+ void ABI_CallFunctionC(Tr (*func)(T1), u32 param1) {
+ ABI_CallFunctionC((const void*)func, param1);
+ }
+
+ /**
+ * Saves specified registers and adjusts the stack to be 16-byte aligned as required by the ABI
+ *
+ * @param mask Registers to push on the stack (high 16 bits are XMMs, low 16 bits are GPRs)
+ * @param rsp_alignment Current alignment of the stack pointer, must be 0 or 8
+ * @param needed_frame_size Additional space needed, e.g., for function arguments passed on the stack
+ * @return Size of the shadow space, i.e., offset of the frame
+ */
+ size_t ABI_PushRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size = 0);
+
+ /**
+ * Restores specified registers and adjusts the stack to its original alignment, i.e., the alignment before
+ * the matching PushRegistersAndAdjustStack.
+ *
+ * @param mask Registers to restores from the stack (high 16 bits are XMMs, low 16 bits are GPRs)
+ * @param rsp_alignment Original alignment before the matching PushRegistersAndAdjustStack, must be 0 or 8
+ * @param needed_frame_size Additional space that was needed
+ * @warning Stack must be currently 16-byte aligned
+ */
+ void ABI_PopRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size = 0);
+
+ #ifdef _M_IX86
+ static int ABI_GetNumXMMRegs() { return 8; }
+ #else
+ static int ABI_GetNumXMMRegs() { return 16; }
+ #endif
+}; // class XEmitter
+
+
+// Everything that needs to generate X86 code should inherit from this.
+// You get memory management for free, plus, you can use all the MOV etc functions without
+// having to prefix them with gen-> or something similar.
+
+class XCodeBlock : public CodeBlock<XEmitter> {
+public:
+ void PoisonMemory() override;
+};
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cmath>
+
+
+#include "citraimport\GPU\window\emu_window.h"
+
+/* //hid todo
+void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) {
+ pad_state.hex |= KeyMap::GetPadKey(key).hex;
+}
+
+void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) {
+ pad_state.hex &= ~KeyMap::GetPadKey(key).hex;
+}*/
+
+/**
+ * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
+ * @param layout FramebufferLayout object describing the framebuffer size and screen positions
+ * @param framebuffer_x Framebuffer x-coordinate to check
+ * @param framebuffer_y Framebuffer y-coordinate to check
+ * @return True if the coordinates are within the touchpad, otherwise false
+ */
+static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsigned framebuffer_x,
+ unsigned framebuffer_y) {
+ return (framebuffer_y >= layout.bottom_screen.top &&
+ framebuffer_y < layout.bottom_screen.bottom &&
+ framebuffer_x >= layout.bottom_screen.left &&
+ framebuffer_x < layout.bottom_screen.right);
+}
+
+std::tuple<unsigned,unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) {
+
+ new_x = std::max(new_x, framebuffer_layout.bottom_screen.left);
+ new_x = std::min(new_x, framebuffer_layout.bottom_screen.right-1);
+
+ new_y = std::max(new_y, framebuffer_layout.bottom_screen.top);
+ new_y = std::min(new_y, framebuffer_layout.bottom_screen.bottom-1);
+
+ return std::make_tuple(new_x, new_y);
+}
+
+/*void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
+ if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
+ return;
+
+ touch_x = VideoCore::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) /
+ (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
+ touch_y = VideoCore::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) /
+ (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
+
+ touch_pressed = true;
+ pad_state.touch = 1;
+}
+
+void EmuWindow::TouchReleased() {
+ touch_pressed = false;
+ touch_x = 0;
+ touch_y = 0;
+ pad_state.touch = 0;
+}
+
+void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
+ if (!touch_pressed)
+ return;
+
+ if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
+ std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
+
+ TouchPressed(framebuffer_x, framebuffer_y);
+}*/ //hid todo
+
+EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) {
+
+
+ EmuWindow::FramebufferLayout res = { width, height, {}, {} };
+
+ float window_aspect_ratio = static_cast<float>(height) / width;
+ float emulation_aspect_ratio = static_cast<float>(VideoCore::kScreenTopHeight * 2) /
+ VideoCore::kScreenTopWidth;
+
+ if (window_aspect_ratio > emulation_aspect_ratio) {
+ // Window is narrower than the emulation content => apply borders to the top and bottom
+ int viewport_height = static_cast<int>(std::round(emulation_aspect_ratio * width));
+
+ res.top_screen.left = 0;
+ res.top_screen.right = res.top_screen.left + width;
+ res.top_screen.top = (height - viewport_height) / 2;
+ res.top_screen.bottom = res.top_screen.top + viewport_height / 2;
+
+ int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) /
+ VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left));
+ int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
+
+ res.bottom_screen.left = bottom_border;
+ res.bottom_screen.right = res.bottom_screen.left + bottom_width;
+ res.bottom_screen.top = res.top_screen.bottom;
+ res.bottom_screen.bottom = res.bottom_screen.top + viewport_height / 2;
+ } else {
+ // Otherwise, apply borders to the left and right sides of the window.
+ int viewport_width = static_cast<int>(std::round(height / emulation_aspect_ratio));
+
+ res.top_screen.left = (width - viewport_width) / 2;
+ res.top_screen.right = res.top_screen.left + viewport_width;
+ res.top_screen.top = 0;
+ res.top_screen.bottom = res.top_screen.top + height / 2;
+
+ int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) /
+ VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left));
+ int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
+
+ res.bottom_screen.left = res.top_screen.left + bottom_border;
+ res.bottom_screen.right = res.bottom_screen.left + bottom_width;
+ res.bottom_screen.top = res.top_screen.bottom;
+ res.bottom_screen.bottom = res.bottom_screen.top + height / 2;
+ }
+
+ return res;
+}
--- /dev/null
+set(SRCS
+ src/glad.c
+ )
+set(HEADERS
+ include/KHR/khrplatform.h
+ include/glad/glad.h
+ )
+
+create_directory_groups(${SRCS} ${HEADERS})
+add_library(glad STATIC ${SRCS} ${HEADERS})
+target_include_directories(glad PUBLIC "include/")
+if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
+ target_link_libraries(glad dl)
+endif()
--- /dev/null
+These files were generated by the [glad](https://github.com/Dav1dde/glad) OpenGL loader generator and have been checked in as-is. You can re-generate them using glad with the following command:
+
+```
+python -m glad --profile core --out-path glad/ --api gl=3.3,gles=3.0
+```
--- /dev/null
+#ifndef __khrplatform_h_
+#define __khrplatform_h_
+
+/*
+** Copyright (c) 2008-2009 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+/* Khronos platform-specific types and definitions.
+ *
+ * $Revision: 23298 $ on $Date: 2013-09-30 17:07:13 -0700 (Mon, 30 Sep 2013) $
+ *
+ * Adopters may modify this file to suit their platform. Adopters are
+ * encouraged to submit platform specific modifications to the Khronos
+ * group so that they can be included in future versions of this file.
+ * Please submit changes by sending them to the public Khronos Bugzilla
+ * (http://khronos.org/bugzilla) by filing a bug against product
+ * "Khronos (general)" component "Registry".
+ *
+ * A predefined template which fills in some of the bug fields can be
+ * reached using http://tinyurl.com/khrplatform-h-bugreport, but you
+ * must create a Bugzilla login first.
+ *
+ *
+ * See the Implementer's Guidelines for information about where this file
+ * should be located on your system and for more details of its use:
+ * http://www.khronos.org/registry/implementers_guide.pdf
+ *
+ * This file should be included as
+ * #include <KHR/khrplatform.h>
+ * by Khronos client API header files that use its types and defines.
+ *
+ * The types in khrplatform.h should only be used to define API-specific types.
+ *
+ * Types defined in khrplatform.h:
+ * khronos_int8_t signed 8 bit
+ * khronos_uint8_t unsigned 8 bit
+ * khronos_int16_t signed 16 bit
+ * khronos_uint16_t unsigned 16 bit
+ * khronos_int32_t signed 32 bit
+ * khronos_uint32_t unsigned 32 bit
+ * khronos_int64_t signed 64 bit
+ * khronos_uint64_t unsigned 64 bit
+ * khronos_intptr_t signed same number of bits as a pointer
+ * khronos_uintptr_t unsigned same number of bits as a pointer
+ * khronos_ssize_t signed size
+ * khronos_usize_t unsigned size
+ * khronos_float_t signed 32 bit floating point
+ * khronos_time_ns_t unsigned 64 bit time in nanoseconds
+ * khronos_utime_nanoseconds_t unsigned time interval or absolute time in
+ * nanoseconds
+ * khronos_stime_nanoseconds_t signed time interval in nanoseconds
+ * khronos_boolean_enum_t enumerated boolean type. This should
+ * only be used as a base type when a client API's boolean type is
+ * an enum. Client APIs which use an integer or other type for
+ * booleans cannot use this as the base type for their boolean.
+ *
+ * Tokens defined in khrplatform.h:
+ *
+ * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
+ *
+ * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
+ * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
+ *
+ * Calling convention macros defined in this file:
+ * KHRONOS_APICALL
+ * KHRONOS_APIENTRY
+ * KHRONOS_APIATTRIBUTES
+ *
+ * These may be used in function prototypes as:
+ *
+ * KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
+ * int arg1,
+ * int arg2) KHRONOS_APIATTRIBUTES;
+ */
+
+/*-------------------------------------------------------------------------
+ * Definition of KHRONOS_APICALL
+ *-------------------------------------------------------------------------
+ * This precedes the return type of the function in the function prototype.
+ */
+#if defined(_WIN32) && !defined(__SCITECH_SNAP__)
+# define KHRONOS_APICALL __declspec(dllimport)
+#elif defined (__SYMBIAN32__)
+# define KHRONOS_APICALL IMPORT_C
+#else
+# define KHRONOS_APICALL
+#endif
+
+/*-------------------------------------------------------------------------
+ * Definition of KHRONOS_APIENTRY
+ *-------------------------------------------------------------------------
+ * This follows the return type of the function and precedes the function
+ * name in the function prototype.
+ */
+#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
+ /* Win32 but not WinCE */
+# define KHRONOS_APIENTRY __stdcall
+#else
+# define KHRONOS_APIENTRY
+#endif
+
+/*-------------------------------------------------------------------------
+ * Definition of KHRONOS_APIATTRIBUTES
+ *-------------------------------------------------------------------------
+ * This follows the closing parenthesis of the function prototype arguments.
+ */
+#if defined (__ARMCC_2__)
+#define KHRONOS_APIATTRIBUTES __softfp
+#else
+#define KHRONOS_APIATTRIBUTES
+#endif
+
+/*-------------------------------------------------------------------------
+ * basic type definitions
+ *-----------------------------------------------------------------------*/
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
+
+
+/*
+ * Using <stdint.h>
+ */
+#include <stdint.h>
+typedef int32_t khronos_int32_t;
+typedef uint32_t khronos_uint32_t;
+typedef int64_t khronos_int64_t;
+typedef uint64_t khronos_uint64_t;
+#define KHRONOS_SUPPORT_INT64 1
+#define KHRONOS_SUPPORT_FLOAT 1
+
+#elif defined(__VMS ) || defined(__sgi)
+
+/*
+ * Using <inttypes.h>
+ */
+#include <inttypes.h>
+typedef int32_t khronos_int32_t;
+typedef uint32_t khronos_uint32_t;
+typedef int64_t khronos_int64_t;
+typedef uint64_t khronos_uint64_t;
+#define KHRONOS_SUPPORT_INT64 1
+#define KHRONOS_SUPPORT_FLOAT 1
+
+#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
+
+/*
+ * Win32
+ */
+typedef __int32 khronos_int32_t;
+typedef unsigned __int32 khronos_uint32_t;
+typedef __int64 khronos_int64_t;
+typedef unsigned __int64 khronos_uint64_t;
+#define KHRONOS_SUPPORT_INT64 1
+#define KHRONOS_SUPPORT_FLOAT 1
+
+#elif defined(__sun__) || defined(__digital__)
+
+/*
+ * Sun or Digital
+ */
+typedef int khronos_int32_t;
+typedef unsigned int khronos_uint32_t;
+#if defined(__arch64__) || defined(_LP64)
+typedef long int khronos_int64_t;
+typedef unsigned long int khronos_uint64_t;
+#else
+typedef long long int khronos_int64_t;
+typedef unsigned long long int khronos_uint64_t;
+#endif /* __arch64__ */
+#define KHRONOS_SUPPORT_INT64 1
+#define KHRONOS_SUPPORT_FLOAT 1
+
+#elif 0
+
+/*
+ * Hypothetical platform with no float or int64 support
+ */
+typedef int khronos_int32_t;
+typedef unsigned int khronos_uint32_t;
+#define KHRONOS_SUPPORT_INT64 0
+#define KHRONOS_SUPPORT_FLOAT 0
+
+#else
+
+/*
+ * Generic fallback
+ */
+#include <stdint.h>
+typedef int32_t khronos_int32_t;
+typedef uint32_t khronos_uint32_t;
+typedef int64_t khronos_int64_t;
+typedef uint64_t khronos_uint64_t;
+#define KHRONOS_SUPPORT_INT64 1
+#define KHRONOS_SUPPORT_FLOAT 1
+
+#endif
+
+
+/*
+ * Types that are (so far) the same on all platforms
+ */
+typedef signed char khronos_int8_t;
+typedef unsigned char khronos_uint8_t;
+typedef signed short int khronos_int16_t;
+typedef unsigned short int khronos_uint16_t;
+
+/*
+ * Types that differ between LLP64 and LP64 architectures - in LLP64,
+ * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
+ * to be the only LLP64 architecture in current use.
+ */
+#ifdef _WIN64
+typedef signed long long int khronos_intptr_t;
+typedef unsigned long long int khronos_uintptr_t;
+typedef signed long long int khronos_ssize_t;
+typedef unsigned long long int khronos_usize_t;
+#else
+typedef signed long int khronos_intptr_t;
+typedef unsigned long int khronos_uintptr_t;
+typedef signed long int khronos_ssize_t;
+typedef unsigned long int khronos_usize_t;
+#endif
+
+#if KHRONOS_SUPPORT_FLOAT
+/*
+ * Float type
+ */
+typedef float khronos_float_t;
+#endif
+
+#if KHRONOS_SUPPORT_INT64
+/* Time types
+ *
+ * These types can be used to represent a time interval in nanoseconds or
+ * an absolute Unadjusted System Time. Unadjusted System Time is the number
+ * of nanoseconds since some arbitrary system event (e.g. since the last
+ * time the system booted). The Unadjusted System Time is an unsigned
+ * 64 bit value that wraps back to 0 every 584 years. Time intervals
+ * may be either signed or unsigned.
+ */
+typedef khronos_uint64_t khronos_utime_nanoseconds_t;
+typedef khronos_int64_t khronos_stime_nanoseconds_t;
+#endif
+
+/*
+ * Dummy value used to pad enum types to 32 bits.
+ */
+#ifndef KHRONOS_MAX_ENUM
+#define KHRONOS_MAX_ENUM 0x7FFFFFFF
+#endif
+
+/*
+ * Enumerated boolean type
+ *
+ * Values other than zero should be considered to be true. Therefore
+ * comparisons should not be made against KHRONOS_TRUE.
+ */
+typedef enum {
+ KHRONOS_FALSE = 0,
+ KHRONOS_TRUE = 1,
+ KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
+} khronos_boolean_enum_t;
+
+#endif /* __khrplatform_h_ */
--- /dev/null
+
+#ifndef __glad_h_
+#define __glad_h_
+
+#ifdef __gl_h_
+#error OpenGL header already included, remove this include, glad already provides it
+#endif
+#define __gl_h_
+
+#ifdef __gl2_h_
+#error OpenGL ES 2 header already included, remove this include, glad already provides it
+#endif
+#define __gl2_h_
+
+#ifdef __gl3_h_
+#error OpenGL ES 3 header already included, remove this include, glad already provides it
+#endif
+#define __gl3_h_
+
+#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#include <windows.h>
+#endif
+
+#ifndef APIENTRY
+#define APIENTRY
+#endif
+#ifndef APIENTRYP
+#define APIENTRYP APIENTRY *
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct gladGLversionStruct {
+ int major;
+ int minor;
+};
+
+extern struct gladGLversionStruct GLVersion;
+
+typedef void* (* GLADloadproc)(const char *name);
+
+#ifndef GLAPI
+# if defined(GLAD_GLAPI_EXPORT)
+# if defined(WIN32) || defined(__CYGWIN__)
+# if defined(GLAD_GLAPI_EXPORT_BUILD)
+# if defined(__GNUC__)
+# define GLAPI __attribute__ ((dllexport)) extern
+# else
+# define GLAPI __declspec(dllexport) extern
+# endif
+# else
+# if defined(__GNUC__)
+# define GLAPI __attribute__ ((dllimport)) extern
+# else
+# define GLAPI __declspec(dllimport) extern
+# endif
+# endif
+# elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD)
+# define GLAPI __attribute__ ((visibility ("default"))) extern
+# else
+# define GLAPI extern
+# endif
+# else
+# define GLAPI extern
+# endif
+#endif
+
+GLAPI int gladLoadGL(void);
+
+GLAPI int gladLoadGLLoader(GLADloadproc);
+
+GLAPI int gladLoadGLES2Loader(GLADloadproc);
+
+#include <stddef.h>
+#include <citraimport\glad\include\KHR/khrplatform.h>
+#ifndef GLEXT_64_TYPES_DEFINED
+/* This code block is duplicated in glxext.h, so must be protected */
+#define GLEXT_64_TYPES_DEFINED
+/* Define int32_t, int64_t, and uint64_t types for UST/MSC */
+/* (as used in the GL_EXT_timer_query extension). */
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+#elif defined(__sun__) || defined(__digital__)
+#include <inttypes.h>
+#if defined(__STDC__)
+#if defined(__arch64__) || defined(_LP64)
+typedef long int int64_t;
+typedef unsigned long int uint64_t;
+#else
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+#endif /* __arch64__ */
+#endif /* __STDC__ */
+#elif defined( __VMS ) || defined(__sgi)
+#include <inttypes.h>
+#elif defined(__SCO__) || defined(__USLC__)
+#include <stdint.h>
+#elif defined(__UNIXOS2__) || defined(__SOL64__)
+typedef long int int32_t;
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+#elif defined(_WIN32) && defined(__GNUC__)
+#include <stdint.h>
+#elif defined(_WIN32)
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+/* Fallback if nothing above works */
+#include <inttypes.h>
+#endif
+#endif
+typedef unsigned int GLenum;
+typedef unsigned char GLboolean;
+typedef unsigned int GLbitfield;
+typedef void GLvoid;
+typedef signed char GLbyte;
+typedef short GLshort;
+typedef int GLint;
+typedef int GLclampx;
+typedef unsigned char GLubyte;
+typedef unsigned short GLushort;
+typedef unsigned int GLuint;
+typedef int GLsizei;
+typedef float GLfloat;
+typedef float GLclampf;
+typedef double GLdouble;
+typedef double GLclampd;
+typedef void *GLeglImageOES;
+typedef char GLchar;
+typedef char GLcharARB;
+#ifdef __APPLE__
+typedef void *GLhandleARB;
+#else
+typedef unsigned int GLhandleARB;
+#endif
+typedef unsigned short GLhalfARB;
+typedef unsigned short GLhalf;
+typedef GLint GLfixed;
+typedef ptrdiff_t GLintptr;
+typedef ptrdiff_t GLsizeiptr;
+typedef int64_t GLint64;
+typedef uint64_t GLuint64;
+typedef ptrdiff_t GLintptrARB;
+typedef ptrdiff_t GLsizeiptrARB;
+typedef int64_t GLint64EXT;
+typedef uint64_t GLuint64EXT;
+typedef struct __GLsync *GLsync;
+struct _cl_context;
+struct _cl_event;
+typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
+typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
+typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
+typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam);
+typedef unsigned short GLhalfNV;
+typedef GLintptr GLvdpauSurfaceNV;
+#define GL_DEPTH_BUFFER_BIT 0x00000100
+#define GL_STENCIL_BUFFER_BIT 0x00000400
+#define GL_COLOR_BUFFER_BIT 0x00004000
+#define GL_FALSE 0
+#define GL_TRUE 1
+#define GL_POINTS 0x0000
+#define GL_LINES 0x0001
+#define GL_LINE_LOOP 0x0002
+#define GL_LINE_STRIP 0x0003
+#define GL_TRIANGLES 0x0004
+#define GL_TRIANGLE_STRIP 0x0005
+#define GL_TRIANGLE_FAN 0x0006
+#define GL_NEVER 0x0200
+#define GL_LESS 0x0201
+#define GL_EQUAL 0x0202
+#define GL_LEQUAL 0x0203
+#define GL_GREATER 0x0204
+#define GL_NOTEQUAL 0x0205
+#define GL_GEQUAL 0x0206
+#define GL_ALWAYS 0x0207
+#define GL_ZERO 0
+#define GL_ONE 1
+#define GL_SRC_COLOR 0x0300
+#define GL_ONE_MINUS_SRC_COLOR 0x0301
+#define GL_SRC_ALPHA 0x0302
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+#define GL_DST_ALPHA 0x0304
+#define GL_ONE_MINUS_DST_ALPHA 0x0305
+#define GL_DST_COLOR 0x0306
+#define GL_ONE_MINUS_DST_COLOR 0x0307
+#define GL_SRC_ALPHA_SATURATE 0x0308
+#define GL_NONE 0
+#define GL_FRONT_LEFT 0x0400
+#define GL_FRONT_RIGHT 0x0401
+#define GL_BACK_LEFT 0x0402
+#define GL_BACK_RIGHT 0x0403
+#define GL_FRONT 0x0404
+#define GL_BACK 0x0405
+#define GL_LEFT 0x0406
+#define GL_RIGHT 0x0407
+#define GL_FRONT_AND_BACK 0x0408
+#define GL_NO_ERROR 0
+#define GL_INVALID_ENUM 0x0500
+#define GL_INVALID_VALUE 0x0501
+#define GL_INVALID_OPERATION 0x0502
+#define GL_OUT_OF_MEMORY 0x0505
+#define GL_CW 0x0900
+#define GL_CCW 0x0901
+#define GL_POINT_SIZE 0x0B11
+#define GL_POINT_SIZE_RANGE 0x0B12
+#define GL_POINT_SIZE_GRANULARITY 0x0B13
+#define GL_LINE_SMOOTH 0x0B20
+#define GL_LINE_WIDTH 0x0B21
+#define GL_LINE_WIDTH_RANGE 0x0B22
+#define GL_LINE_WIDTH_GRANULARITY 0x0B23
+#define GL_POLYGON_MODE 0x0B40
+#define GL_POLYGON_SMOOTH 0x0B41
+#define GL_CULL_FACE 0x0B44
+#define GL_CULL_FACE_MODE 0x0B45
+#define GL_FRONT_FACE 0x0B46
+#define GL_DEPTH_RANGE 0x0B70
+#define GL_DEPTH_TEST 0x0B71
+#define GL_DEPTH_WRITEMASK 0x0B72
+#define GL_DEPTH_CLEAR_VALUE 0x0B73
+#define GL_DEPTH_FUNC 0x0B74
+#define GL_STENCIL_TEST 0x0B90
+#define GL_STENCIL_CLEAR_VALUE 0x0B91
+#define GL_STENCIL_FUNC 0x0B92
+#define GL_STENCIL_VALUE_MASK 0x0B93
+#define GL_STENCIL_FAIL 0x0B94
+#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95
+#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96
+#define GL_STENCIL_REF 0x0B97
+#define GL_STENCIL_WRITEMASK 0x0B98
+#define GL_VIEWPORT 0x0BA2
+#define GL_DITHER 0x0BD0
+#define GL_BLEND_DST 0x0BE0
+#define GL_BLEND_SRC 0x0BE1
+#define GL_BLEND 0x0BE2
+#define GL_LOGIC_OP_MODE 0x0BF0
+#define GL_COLOR_LOGIC_OP 0x0BF2
+#define GL_DRAW_BUFFER 0x0C01
+#define GL_READ_BUFFER 0x0C02
+#define GL_SCISSOR_BOX 0x0C10
+#define GL_SCISSOR_TEST 0x0C11
+#define GL_COLOR_CLEAR_VALUE 0x0C22
+#define GL_COLOR_WRITEMASK 0x0C23
+#define GL_DOUBLEBUFFER 0x0C32
+#define GL_STEREO 0x0C33
+#define GL_LINE_SMOOTH_HINT 0x0C52
+#define GL_POLYGON_SMOOTH_HINT 0x0C53
+#define GL_UNPACK_SWAP_BYTES 0x0CF0
+#define GL_UNPACK_LSB_FIRST 0x0CF1
+#define GL_UNPACK_ROW_LENGTH 0x0CF2
+#define GL_UNPACK_SKIP_ROWS 0x0CF3
+#define GL_UNPACK_SKIP_PIXELS 0x0CF4
+#define GL_UNPACK_ALIGNMENT 0x0CF5
+#define GL_PACK_SWAP_BYTES 0x0D00
+#define GL_PACK_LSB_FIRST 0x0D01
+#define GL_PACK_ROW_LENGTH 0x0D02
+#define GL_PACK_SKIP_ROWS 0x0D03
+#define GL_PACK_SKIP_PIXELS 0x0D04
+#define GL_PACK_ALIGNMENT 0x0D05
+#define GL_MAX_TEXTURE_SIZE 0x0D33
+#define GL_MAX_VIEWPORT_DIMS 0x0D3A
+#define GL_SUBPIXEL_BITS 0x0D50
+#define GL_TEXTURE_1D 0x0DE0
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_POLYGON_OFFSET_UNITS 0x2A00
+#define GL_POLYGON_OFFSET_POINT 0x2A01
+#define GL_POLYGON_OFFSET_LINE 0x2A02
+#define GL_POLYGON_OFFSET_FILL 0x8037
+#define GL_POLYGON_OFFSET_FACTOR 0x8038
+#define GL_TEXTURE_BINDING_1D 0x8068
+#define GL_TEXTURE_BINDING_2D 0x8069
+#define GL_TEXTURE_WIDTH 0x1000
+#define GL_TEXTURE_HEIGHT 0x1001
+#define GL_TEXTURE_INTERNAL_FORMAT 0x1003
+#define GL_TEXTURE_BORDER_COLOR 0x1004
+#define GL_TEXTURE_RED_SIZE 0x805C
+#define GL_TEXTURE_GREEN_SIZE 0x805D
+#define GL_TEXTURE_BLUE_SIZE 0x805E
+#define GL_TEXTURE_ALPHA_SIZE 0x805F
+#define GL_DONT_CARE 0x1100
+#define GL_FASTEST 0x1101
+#define GL_NICEST 0x1102
+#define GL_BYTE 0x1400
+#define GL_UNSIGNED_BYTE 0x1401
+#define GL_SHORT 0x1402
+#define GL_UNSIGNED_SHORT 0x1403
+#define GL_INT 0x1404
+#define GL_UNSIGNED_INT 0x1405
+#define GL_FLOAT 0x1406
+#define GL_DOUBLE 0x140A
+#define GL_CLEAR 0x1500
+#define GL_AND 0x1501
+#define GL_AND_REVERSE 0x1502
+#define GL_COPY 0x1503
+#define GL_AND_INVERTED 0x1504
+#define GL_NOOP 0x1505
+#define GL_XOR 0x1506
+#define GL_OR 0x1507
+#define GL_NOR 0x1508
+#define GL_EQUIV 0x1509
+#define GL_INVERT 0x150A
+#define GL_OR_REVERSE 0x150B
+#define GL_COPY_INVERTED 0x150C
+#define GL_OR_INVERTED 0x150D
+#define GL_NAND 0x150E
+#define GL_SET 0x150F
+#define GL_TEXTURE 0x1702
+#define GL_COLOR 0x1800
+#define GL_DEPTH 0x1801
+#define GL_STENCIL 0x1802
+#define GL_STENCIL_INDEX 0x1901
+#define GL_DEPTH_COMPONENT 0x1902
+#define GL_RED 0x1903
+#define GL_GREEN 0x1904
+#define GL_BLUE 0x1905
+#define GL_ALPHA 0x1906
+#define GL_RGB 0x1907
+#define GL_RGBA 0x1908
+#define GL_POINT 0x1B00
+#define GL_LINE 0x1B01
+#define GL_FILL 0x1B02
+#define GL_KEEP 0x1E00
+#define GL_REPLACE 0x1E01
+#define GL_INCR 0x1E02
+#define GL_DECR 0x1E03
+#define GL_VENDOR 0x1F00
+#define GL_RENDERER 0x1F01
+#define GL_VERSION 0x1F02
+#define GL_EXTENSIONS 0x1F03
+#define GL_NEAREST 0x2600
+#define GL_LINEAR 0x2601
+#define GL_NEAREST_MIPMAP_NEAREST 0x2700
+#define GL_LINEAR_MIPMAP_NEAREST 0x2701
+#define GL_NEAREST_MIPMAP_LINEAR 0x2702
+#define GL_LINEAR_MIPMAP_LINEAR 0x2703
+#define GL_TEXTURE_MAG_FILTER 0x2800
+#define GL_TEXTURE_MIN_FILTER 0x2801
+#define GL_TEXTURE_WRAP_S 0x2802
+#define GL_TEXTURE_WRAP_T 0x2803
+#define GL_PROXY_TEXTURE_1D 0x8063
+#define GL_PROXY_TEXTURE_2D 0x8064
+#define GL_REPEAT 0x2901
+#define GL_R3_G3_B2 0x2A10
+#define GL_RGB4 0x804F
+#define GL_RGB5 0x8050
+#define GL_RGB8 0x8051
+#define GL_RGB10 0x8052
+#define GL_RGB12 0x8053
+#define GL_RGB16 0x8054
+#define GL_RGBA2 0x8055
+#define GL_RGBA4 0x8056
+#define GL_RGB5_A1 0x8057
+#define GL_RGBA8 0x8058
+#define GL_RGB10_A2 0x8059
+#define GL_RGBA12 0x805A
+#define GL_RGBA16 0x805B
+#define GL_UNSIGNED_BYTE_3_3_2 0x8032
+#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
+#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
+#define GL_UNSIGNED_INT_8_8_8_8 0x8035
+#define GL_UNSIGNED_INT_10_10_10_2 0x8036
+#define GL_TEXTURE_BINDING_3D 0x806A
+#define GL_PACK_SKIP_IMAGES 0x806B
+#define GL_PACK_IMAGE_HEIGHT 0x806C
+#define GL_UNPACK_SKIP_IMAGES 0x806D
+#define GL_UNPACK_IMAGE_HEIGHT 0x806E
+#define GL_TEXTURE_3D 0x806F
+#define GL_PROXY_TEXTURE_3D 0x8070
+#define GL_TEXTURE_DEPTH 0x8071
+#define GL_TEXTURE_WRAP_R 0x8072
+#define GL_MAX_3D_TEXTURE_SIZE 0x8073
+#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
+#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
+#define GL_BGR 0x80E0
+#define GL_BGRA 0x80E1
+#define GL_MAX_ELEMENTS_VERTICES 0x80E8
+#define GL_MAX_ELEMENTS_INDICES 0x80E9
+#define GL_CLAMP_TO_EDGE 0x812F
+#define GL_TEXTURE_MIN_LOD 0x813A
+#define GL_TEXTURE_MAX_LOD 0x813B
+#define GL_TEXTURE_BASE_LEVEL 0x813C
+#define GL_TEXTURE_MAX_LEVEL 0x813D
+#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12
+#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13
+#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22
+#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23
+#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E
+#define GL_TEXTURE0 0x84C0
+#define GL_TEXTURE1 0x84C1
+#define GL_TEXTURE2 0x84C2
+#define GL_TEXTURE3 0x84C3
+#define GL_TEXTURE4 0x84C4
+#define GL_TEXTURE5 0x84C5
+#define GL_TEXTURE6 0x84C6
+#define GL_TEXTURE7 0x84C7
+#define GL_TEXTURE8 0x84C8
+#define GL_TEXTURE9 0x84C9
+#define GL_TEXTURE10 0x84CA
+#define GL_TEXTURE11 0x84CB
+#define GL_TEXTURE12 0x84CC
+#define GL_TEXTURE13 0x84CD
+#define GL_TEXTURE14 0x84CE
+#define GL_TEXTURE15 0x84CF
+#define GL_TEXTURE16 0x84D0
+#define GL_TEXTURE17 0x84D1
+#define GL_TEXTURE18 0x84D2
+#define GL_TEXTURE19 0x84D3
+#define GL_TEXTURE20 0x84D4
+#define GL_TEXTURE21 0x84D5
+#define GL_TEXTURE22 0x84D6
+#define GL_TEXTURE23 0x84D7
+#define GL_TEXTURE24 0x84D8
+#define GL_TEXTURE25 0x84D9
+#define GL_TEXTURE26 0x84DA
+#define GL_TEXTURE27 0x84DB
+#define GL_TEXTURE28 0x84DC
+#define GL_TEXTURE29 0x84DD
+#define GL_TEXTURE30 0x84DE
+#define GL_TEXTURE31 0x84DF
+#define GL_ACTIVE_TEXTURE 0x84E0
+#define GL_MULTISAMPLE 0x809D
+#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E
+#define GL_SAMPLE_ALPHA_TO_ONE 0x809F
+#define GL_SAMPLE_COVERAGE 0x80A0
+#define GL_SAMPLE_BUFFERS 0x80A8
+#define GL_SAMPLES 0x80A9
+#define GL_SAMPLE_COVERAGE_VALUE 0x80AA
+#define GL_SAMPLE_COVERAGE_INVERT 0x80AB
+#define GL_TEXTURE_CUBE_MAP 0x8513
+#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
+#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B
+#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C
+#define GL_COMPRESSED_RGB 0x84ED
+#define GL_COMPRESSED_RGBA 0x84EE
+#define GL_TEXTURE_COMPRESSION_HINT 0x84EF
+#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0
+#define GL_TEXTURE_COMPRESSED 0x86A1
+#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2
+#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3
+#define GL_CLAMP_TO_BORDER 0x812D
+#define GL_BLEND_DST_RGB 0x80C8
+#define GL_BLEND_SRC_RGB 0x80C9
+#define GL_BLEND_DST_ALPHA 0x80CA
+#define GL_BLEND_SRC_ALPHA 0x80CB
+#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128
+#define GL_DEPTH_COMPONENT16 0x81A5
+#define GL_DEPTH_COMPONENT24 0x81A6
+#define GL_DEPTH_COMPONENT32 0x81A7
+#define GL_MIRRORED_REPEAT 0x8370
+#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD
+#define GL_TEXTURE_LOD_BIAS 0x8501
+#define GL_INCR_WRAP 0x8507
+#define GL_DECR_WRAP 0x8508
+#define GL_TEXTURE_DEPTH_SIZE 0x884A
+#define GL_TEXTURE_COMPARE_MODE 0x884C
+#define GL_TEXTURE_COMPARE_FUNC 0x884D
+#define GL_FUNC_ADD 0x8006
+#define GL_FUNC_SUBTRACT 0x800A
+#define GL_FUNC_REVERSE_SUBTRACT 0x800B
+#define GL_MIN 0x8007
+#define GL_MAX 0x8008
+#define GL_CONSTANT_COLOR 0x8001
+#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
+#define GL_CONSTANT_ALPHA 0x8003
+#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
+#define GL_BUFFER_SIZE 0x8764
+#define GL_BUFFER_USAGE 0x8765
+#define GL_QUERY_COUNTER_BITS 0x8864
+#define GL_CURRENT_QUERY 0x8865
+#define GL_QUERY_RESULT 0x8866
+#define GL_QUERY_RESULT_AVAILABLE 0x8867
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define GL_ARRAY_BUFFER_BINDING 0x8894
+#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
+#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
+#define GL_READ_ONLY 0x88B8
+#define GL_WRITE_ONLY 0x88B9
+#define GL_READ_WRITE 0x88BA
+#define GL_BUFFER_ACCESS 0x88BB
+#define GL_BUFFER_MAPPED 0x88BC
+#define GL_BUFFER_MAP_POINTER 0x88BD
+#define GL_STREAM_DRAW 0x88E0
+#define GL_STREAM_READ 0x88E1
+#define GL_STREAM_COPY 0x88E2
+#define GL_STATIC_DRAW 0x88E4
+#define GL_STATIC_READ 0x88E5
+#define GL_STATIC_COPY 0x88E6
+#define GL_DYNAMIC_DRAW 0x88E8
+#define GL_DYNAMIC_READ 0x88E9
+#define GL_DYNAMIC_COPY 0x88EA
+#define GL_SAMPLES_PASSED 0x8914
+#define GL_SRC1_ALPHA 0x8589
+#define GL_BLEND_EQUATION_RGB 0x8009
+#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
+#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
+#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
+#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
+#define GL_CURRENT_VERTEX_ATTRIB 0x8626
+#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
+#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
+#define GL_STENCIL_BACK_FUNC 0x8800
+#define GL_STENCIL_BACK_FAIL 0x8801
+#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802
+#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803
+#define GL_MAX_DRAW_BUFFERS 0x8824
+#define GL_DRAW_BUFFER0 0x8825
+#define GL_DRAW_BUFFER1 0x8826
+#define GL_DRAW_BUFFER2 0x8827
+#define GL_DRAW_BUFFER3 0x8828
+#define GL_DRAW_BUFFER4 0x8829
+#define GL_DRAW_BUFFER5 0x882A
+#define GL_DRAW_BUFFER6 0x882B
+#define GL_DRAW_BUFFER7 0x882C
+#define GL_DRAW_BUFFER8 0x882D
+#define GL_DRAW_BUFFER9 0x882E
+#define GL_DRAW_BUFFER10 0x882F
+#define GL_DRAW_BUFFER11 0x8830
+#define GL_DRAW_BUFFER12 0x8831
+#define GL_DRAW_BUFFER13 0x8832
+#define GL_DRAW_BUFFER14 0x8833
+#define GL_DRAW_BUFFER15 0x8834
+#define GL_BLEND_EQUATION_ALPHA 0x883D
+#define GL_MAX_VERTEX_ATTRIBS 0x8869
+#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
+#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
+#define GL_FRAGMENT_SHADER 0x8B30
+#define GL_VERTEX_SHADER 0x8B31
+#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49
+#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A
+#define GL_MAX_VARYING_FLOATS 0x8B4B
+#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C
+#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
+#define GL_SHADER_TYPE 0x8B4F
+#define GL_FLOAT_VEC2 0x8B50
+#define GL_FLOAT_VEC3 0x8B51
+#define GL_FLOAT_VEC4 0x8B52
+#define GL_INT_VEC2 0x8B53
+#define GL_INT_VEC3 0x8B54
+#define GL_INT_VEC4 0x8B55
+#define GL_BOOL 0x8B56
+#define GL_BOOL_VEC2 0x8B57
+#define GL_BOOL_VEC3 0x8B58
+#define GL_BOOL_VEC4 0x8B59
+#define GL_FLOAT_MAT2 0x8B5A
+#define GL_FLOAT_MAT3 0x8B5B
+#define GL_FLOAT_MAT4 0x8B5C
+#define GL_SAMPLER_1D 0x8B5D
+#define GL_SAMPLER_2D 0x8B5E
+#define GL_SAMPLER_3D 0x8B5F
+#define GL_SAMPLER_CUBE 0x8B60
+#define GL_SAMPLER_1D_SHADOW 0x8B61
+#define GL_SAMPLER_2D_SHADOW 0x8B62
+#define GL_DELETE_STATUS 0x8B80
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_LINK_STATUS 0x8B82
+#define GL_VALIDATE_STATUS 0x8B83
+#define GL_INFO_LOG_LENGTH 0x8B84
+#define GL_ATTACHED_SHADERS 0x8B85
+#define GL_ACTIVE_UNIFORMS 0x8B86
+#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87
+#define GL_SHADER_SOURCE_LENGTH 0x8B88
+#define GL_ACTIVE_ATTRIBUTES 0x8B89
+#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
+#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B
+#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
+#define GL_CURRENT_PROGRAM 0x8B8D
+#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0
+#define GL_LOWER_LEFT 0x8CA1
+#define GL_UPPER_LEFT 0x8CA2
+#define GL_STENCIL_BACK_REF 0x8CA3
+#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4
+#define GL_STENCIL_BACK_WRITEMASK 0x8CA5
+#define GL_PIXEL_PACK_BUFFER 0x88EB
+#define GL_PIXEL_UNPACK_BUFFER 0x88EC
+#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED
+#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
+#define GL_FLOAT_MAT2x3 0x8B65
+#define GL_FLOAT_MAT2x4 0x8B66
+#define GL_FLOAT_MAT3x2 0x8B67
+#define GL_FLOAT_MAT3x4 0x8B68
+#define GL_FLOAT_MAT4x2 0x8B69
+#define GL_FLOAT_MAT4x3 0x8B6A
+#define GL_SRGB 0x8C40
+#define GL_SRGB8 0x8C41
+#define GL_SRGB_ALPHA 0x8C42
+#define GL_SRGB8_ALPHA8 0x8C43
+#define GL_COMPRESSED_SRGB 0x8C48
+#define GL_COMPRESSED_SRGB_ALPHA 0x8C49
+#define GL_COMPARE_REF_TO_TEXTURE 0x884E
+#define GL_CLIP_DISTANCE0 0x3000
+#define GL_CLIP_DISTANCE1 0x3001
+#define GL_CLIP_DISTANCE2 0x3002
+#define GL_CLIP_DISTANCE3 0x3003
+#define GL_CLIP_DISTANCE4 0x3004
+#define GL_CLIP_DISTANCE5 0x3005
+#define GL_CLIP_DISTANCE6 0x3006
+#define GL_CLIP_DISTANCE7 0x3007
+#define GL_MAX_CLIP_DISTANCES 0x0D32
+#define GL_MAJOR_VERSION 0x821B
+#define GL_MINOR_VERSION 0x821C
+#define GL_NUM_EXTENSIONS 0x821D
+#define GL_CONTEXT_FLAGS 0x821E
+#define GL_COMPRESSED_RED 0x8225
+#define GL_COMPRESSED_RG 0x8226
+#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001
+#define GL_RGBA32F 0x8814
+#define GL_RGB32F 0x8815
+#define GL_RGBA16F 0x881A
+#define GL_RGB16F 0x881B
+#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD
+#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF
+#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904
+#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905
+#define GL_CLAMP_READ_COLOR 0x891C
+#define GL_FIXED_ONLY 0x891D
+#define GL_MAX_VARYING_COMPONENTS 0x8B4B
+#define GL_TEXTURE_1D_ARRAY 0x8C18
+#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19
+#define GL_TEXTURE_2D_ARRAY 0x8C1A
+#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B
+#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C
+#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D
+#define GL_R11F_G11F_B10F 0x8C3A
+#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B
+#define GL_RGB9_E5 0x8C3D
+#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E
+#define GL_TEXTURE_SHARED_SIZE 0x8C3F
+#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76
+#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F
+#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80
+#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83
+#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84
+#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85
+#define GL_PRIMITIVES_GENERATED 0x8C87
+#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88
+#define GL_RASTERIZER_DISCARD 0x8C89
+#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A
+#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B
+#define GL_INTERLEAVED_ATTRIBS 0x8C8C
+#define GL_SEPARATE_ATTRIBS 0x8C8D
+#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E
+#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F
+#define GL_RGBA32UI 0x8D70
+#define GL_RGB32UI 0x8D71
+#define GL_RGBA16UI 0x8D76
+#define GL_RGB16UI 0x8D77
+#define GL_RGBA8UI 0x8D7C
+#define GL_RGB8UI 0x8D7D
+#define GL_RGBA32I 0x8D82
+#define GL_RGB32I 0x8D83
+#define GL_RGBA16I 0x8D88
+#define GL_RGB16I 0x8D89
+#define GL_RGBA8I 0x8D8E
+#define GL_RGB8I 0x8D8F
+#define GL_RED_INTEGER 0x8D94
+#define GL_GREEN_INTEGER 0x8D95
+#define GL_BLUE_INTEGER 0x8D96
+#define GL_RGB_INTEGER 0x8D98
+#define GL_RGBA_INTEGER 0x8D99
+#define GL_BGR_INTEGER 0x8D9A
+#define GL_BGRA_INTEGER 0x8D9B
+#define GL_SAMPLER_1D_ARRAY 0x8DC0
+#define GL_SAMPLER_2D_ARRAY 0x8DC1
+#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3
+#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4
+#define GL_SAMPLER_CUBE_SHADOW 0x8DC5
+#define GL_UNSIGNED_INT_VEC2 0x8DC6
+#define GL_UNSIGNED_INT_VEC3 0x8DC7
+#define GL_UNSIGNED_INT_VEC4 0x8DC8
+#define GL_INT_SAMPLER_1D 0x8DC9
+#define GL_INT_SAMPLER_2D 0x8DCA
+#define GL_INT_SAMPLER_3D 0x8DCB
+#define GL_INT_SAMPLER_CUBE 0x8DCC
+#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE
+#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF
+#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1
+#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2
+#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3
+#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4
+#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6
+#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7
+#define GL_QUERY_WAIT 0x8E13
+#define GL_QUERY_NO_WAIT 0x8E14
+#define GL_QUERY_BY_REGION_WAIT 0x8E15
+#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16
+#define GL_BUFFER_ACCESS_FLAGS 0x911F
+#define GL_BUFFER_MAP_LENGTH 0x9120
+#define GL_BUFFER_MAP_OFFSET 0x9121
+#define GL_DEPTH_COMPONENT32F 0x8CAC
+#define GL_DEPTH32F_STENCIL8 0x8CAD
+#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD
+#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506
+#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210
+#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211
+#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212
+#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213
+#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214
+#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215
+#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216
+#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217
+#define GL_FRAMEBUFFER_DEFAULT 0x8218
+#define GL_FRAMEBUFFER_UNDEFINED 0x8219
+#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
+#define GL_MAX_RENDERBUFFER_SIZE 0x84E8
+#define GL_DEPTH_STENCIL 0x84F9
+#define GL_UNSIGNED_INT_24_8 0x84FA
+#define GL_DEPTH24_STENCIL8 0x88F0
+#define GL_TEXTURE_STENCIL_SIZE 0x88F1
+#define GL_TEXTURE_RED_TYPE 0x8C10
+#define GL_TEXTURE_GREEN_TYPE 0x8C11
+#define GL_TEXTURE_BLUE_TYPE 0x8C12
+#define GL_TEXTURE_ALPHA_TYPE 0x8C13
+#define GL_TEXTURE_DEPTH_TYPE 0x8C16
+#define GL_UNSIGNED_NORMALIZED 0x8C17
+#define GL_FRAMEBUFFER_BINDING 0x8CA6
+#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6
+#define GL_RENDERBUFFER_BINDING 0x8CA7
+#define GL_READ_FRAMEBUFFER 0x8CA8
+#define GL_DRAW_FRAMEBUFFER 0x8CA9
+#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA
+#define GL_RENDERBUFFER_SAMPLES 0x8CAB
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4
+#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
+#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB
+#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC
+#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
+#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF
+#define GL_COLOR_ATTACHMENT0 0x8CE0
+#define GL_COLOR_ATTACHMENT1 0x8CE1
+#define GL_COLOR_ATTACHMENT2 0x8CE2
+#define GL_COLOR_ATTACHMENT3 0x8CE3
+#define GL_COLOR_ATTACHMENT4 0x8CE4
+#define GL_COLOR_ATTACHMENT5 0x8CE5
+#define GL_COLOR_ATTACHMENT6 0x8CE6
+#define GL_COLOR_ATTACHMENT7 0x8CE7
+#define GL_COLOR_ATTACHMENT8 0x8CE8
+#define GL_COLOR_ATTACHMENT9 0x8CE9
+#define GL_COLOR_ATTACHMENT10 0x8CEA
+#define GL_COLOR_ATTACHMENT11 0x8CEB
+#define GL_COLOR_ATTACHMENT12 0x8CEC
+#define GL_COLOR_ATTACHMENT13 0x8CED
+#define GL_COLOR_ATTACHMENT14 0x8CEE
+#define GL_COLOR_ATTACHMENT15 0x8CEF
+#define GL_COLOR_ATTACHMENT16 0x8CF0
+#define GL_COLOR_ATTACHMENT17 0x8CF1
+#define GL_COLOR_ATTACHMENT18 0x8CF2
+#define GL_COLOR_ATTACHMENT19 0x8CF3
+#define GL_COLOR_ATTACHMENT20 0x8CF4
+#define GL_COLOR_ATTACHMENT21 0x8CF5
+#define GL_COLOR_ATTACHMENT22 0x8CF6
+#define GL_COLOR_ATTACHMENT23 0x8CF7
+#define GL_COLOR_ATTACHMENT24 0x8CF8
+#define GL_COLOR_ATTACHMENT25 0x8CF9
+#define GL_COLOR_ATTACHMENT26 0x8CFA
+#define GL_COLOR_ATTACHMENT27 0x8CFB
+#define GL_COLOR_ATTACHMENT28 0x8CFC
+#define GL_COLOR_ATTACHMENT29 0x8CFD
+#define GL_COLOR_ATTACHMENT30 0x8CFE
+#define GL_COLOR_ATTACHMENT31 0x8CFF
+#define GL_DEPTH_ATTACHMENT 0x8D00
+#define GL_STENCIL_ATTACHMENT 0x8D20
+#define GL_FRAMEBUFFER 0x8D40
+#define GL_RENDERBUFFER 0x8D41
+#define GL_RENDERBUFFER_WIDTH 0x8D42
+#define GL_RENDERBUFFER_HEIGHT 0x8D43
+#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44
+#define GL_STENCIL_INDEX1 0x8D46
+#define GL_STENCIL_INDEX4 0x8D47
+#define GL_STENCIL_INDEX8 0x8D48
+#define GL_STENCIL_INDEX16 0x8D49
+#define GL_RENDERBUFFER_RED_SIZE 0x8D50
+#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51
+#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52
+#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53
+#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54
+#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55
+#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56
+#define GL_MAX_SAMPLES 0x8D57
+#define GL_INDEX 0x8222
+#define GL_FRAMEBUFFER_SRGB 0x8DB9
+#define GL_HALF_FLOAT 0x140B
+#define GL_MAP_READ_BIT 0x0001
+#define GL_MAP_WRITE_BIT 0x0002
+#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004
+#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008
+#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010
+#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020
+#define GL_COMPRESSED_RED_RGTC1 0x8DBB
+#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC
+#define GL_COMPRESSED_RG_RGTC2 0x8DBD
+#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE
+#define GL_RG 0x8227
+#define GL_RG_INTEGER 0x8228
+#define GL_R8 0x8229
+#define GL_R16 0x822A
+#define GL_RG8 0x822B
+#define GL_RG16 0x822C
+#define GL_R16F 0x822D
+#define GL_R32F 0x822E
+#define GL_RG16F 0x822F
+#define GL_RG32F 0x8230
+#define GL_R8I 0x8231
+#define GL_R8UI 0x8232
+#define GL_R16I 0x8233
+#define GL_R16UI 0x8234
+#define GL_R32I 0x8235
+#define GL_R32UI 0x8236
+#define GL_RG8I 0x8237
+#define GL_RG8UI 0x8238
+#define GL_RG16I 0x8239
+#define GL_RG16UI 0x823A
+#define GL_RG32I 0x823B
+#define GL_RG32UI 0x823C
+#define GL_VERTEX_ARRAY_BINDING 0x85B5
+#define GL_SAMPLER_2D_RECT 0x8B63
+#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64
+#define GL_SAMPLER_BUFFER 0x8DC2
+#define GL_INT_SAMPLER_2D_RECT 0x8DCD
+#define GL_INT_SAMPLER_BUFFER 0x8DD0
+#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5
+#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8
+#define GL_TEXTURE_BUFFER 0x8C2A
+#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B
+#define GL_TEXTURE_BINDING_BUFFER 0x8C2C
+#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D
+#define GL_TEXTURE_RECTANGLE 0x84F5
+#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6
+#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7
+#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8
+#define GL_R8_SNORM 0x8F94
+#define GL_RG8_SNORM 0x8F95
+#define GL_RGB8_SNORM 0x8F96
+#define GL_RGBA8_SNORM 0x8F97
+#define GL_R16_SNORM 0x8F98
+#define GL_RG16_SNORM 0x8F99
+#define GL_RGB16_SNORM 0x8F9A
+#define GL_RGBA16_SNORM 0x8F9B
+#define GL_SIGNED_NORMALIZED 0x8F9C
+#define GL_PRIMITIVE_RESTART 0x8F9D
+#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E
+#define GL_COPY_READ_BUFFER 0x8F36
+#define GL_COPY_WRITE_BUFFER 0x8F37
+#define GL_UNIFORM_BUFFER 0x8A11
+#define GL_UNIFORM_BUFFER_BINDING 0x8A28
+#define GL_UNIFORM_BUFFER_START 0x8A29
+#define GL_UNIFORM_BUFFER_SIZE 0x8A2A
+#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B
+#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C
+#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D
+#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E
+#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F
+#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30
+#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31
+#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32
+#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33
+#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34
+#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35
+#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36
+#define GL_UNIFORM_TYPE 0x8A37
+#define GL_UNIFORM_SIZE 0x8A38
+#define GL_UNIFORM_NAME_LENGTH 0x8A39
+#define GL_UNIFORM_BLOCK_INDEX 0x8A3A
+#define GL_UNIFORM_OFFSET 0x8A3B
+#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C
+#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D
+#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E
+#define GL_UNIFORM_BLOCK_BINDING 0x8A3F
+#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40
+#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41
+#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42
+#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43
+#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44
+#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45
+#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46
+#define GL_INVALID_INDEX 0xFFFFFFFF
+#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
+#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
+#define GL_LINES_ADJACENCY 0x000A
+#define GL_LINE_STRIP_ADJACENCY 0x000B
+#define GL_TRIANGLES_ADJACENCY 0x000C
+#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D
+#define GL_PROGRAM_POINT_SIZE 0x8642
+#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29
+#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7
+#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8
+#define GL_GEOMETRY_SHADER 0x8DD9
+#define GL_GEOMETRY_VERTICES_OUT 0x8916
+#define GL_GEOMETRY_INPUT_TYPE 0x8917
+#define GL_GEOMETRY_OUTPUT_TYPE 0x8918
+#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF
+#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0
+#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1
+#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122
+#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123
+#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124
+#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125
+#define GL_CONTEXT_PROFILE_MASK 0x9126
+#define GL_DEPTH_CLAMP 0x864F
+#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C
+#define GL_FIRST_VERTEX_CONVENTION 0x8E4D
+#define GL_LAST_VERTEX_CONVENTION 0x8E4E
+#define GL_PROVOKING_VERTEX 0x8E4F
+#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F
+#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111
+#define GL_OBJECT_TYPE 0x9112
+#define GL_SYNC_CONDITION 0x9113
+#define GL_SYNC_STATUS 0x9114
+#define GL_SYNC_FLAGS 0x9115
+#define GL_SYNC_FENCE 0x9116
+#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
+#define GL_UNSIGNALED 0x9118
+#define GL_SIGNALED 0x9119
+#define GL_ALREADY_SIGNALED 0x911A
+#define GL_TIMEOUT_EXPIRED 0x911B
+#define GL_CONDITION_SATISFIED 0x911C
+#define GL_WAIT_FAILED 0x911D
+#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF
+#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
+#define GL_SAMPLE_POSITION 0x8E50
+#define GL_SAMPLE_MASK 0x8E51
+#define GL_SAMPLE_MASK_VALUE 0x8E52
+#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59
+#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
+#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101
+#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102
+#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103
+#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104
+#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105
+#define GL_TEXTURE_SAMPLES 0x9106
+#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107
+#define GL_SAMPLER_2D_MULTISAMPLE 0x9108
+#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109
+#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A
+#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B
+#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C
+#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D
+#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E
+#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F
+#define GL_MAX_INTEGER_SAMPLES 0x9110
+#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE
+#define GL_SRC1_COLOR 0x88F9
+#define GL_ONE_MINUS_SRC1_COLOR 0x88FA
+#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB
+#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC
+#define GL_ANY_SAMPLES_PASSED 0x8C2F
+#define GL_SAMPLER_BINDING 0x8919
+#define GL_RGB10_A2UI 0x906F
+#define GL_TEXTURE_SWIZZLE_R 0x8E42
+#define GL_TEXTURE_SWIZZLE_G 0x8E43
+#define GL_TEXTURE_SWIZZLE_B 0x8E44
+#define GL_TEXTURE_SWIZZLE_A 0x8E45
+#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46
+#define GL_TIME_ELAPSED 0x88BF
+#define GL_TIMESTAMP 0x8E28
+#define GL_INT_2_10_10_10_REV 0x8D9F
+#define GL_BLEND_EQUATION 0x8009
+#define GL_BLEND_COLOR 0x8005
+#define GL_FIXED 0x140C
+#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB
+#define GL_MAX_VARYING_VECTORS 0x8DFC
+#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD
+#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A
+#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
+#define GL_SHADER_COMPILER 0x8DFA
+#define GL_SHADER_BINARY_FORMATS 0x8DF8
+#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9
+#define GL_LOW_FLOAT 0x8DF0
+#define GL_MEDIUM_FLOAT 0x8DF1
+#define GL_HIGH_FLOAT 0x8DF2
+#define GL_LOW_INT 0x8DF3
+#define GL_MEDIUM_INT 0x8DF4
+#define GL_HIGH_INT 0x8DF5
+#define GL_RGB565 0x8D62
+#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
+#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
+#define GL_COPY_READ_BUFFER_BINDING 0x8F36
+#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37
+#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A
+#define GL_TRANSFORM_FEEDBACK 0x8E22
+#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23
+#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24
+#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25
+#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257
+#define GL_PROGRAM_BINARY_LENGTH 0x8741
+#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
+#define GL_PROGRAM_BINARY_FORMATS 0x87FF
+#define GL_COMPRESSED_R11_EAC 0x9270
+#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271
+#define GL_COMPRESSED_RG11_EAC 0x9272
+#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273
+#define GL_COMPRESSED_RGB8_ETC2 0x9274
+#define GL_COMPRESSED_SRGB8_ETC2 0x9275
+#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276
+#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277
+#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
+#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279
+#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F
+#define GL_MAX_ELEMENT_INDEX 0x8D6B
+#define GL_NUM_SAMPLE_COUNTS 0x9380
+#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF
+#ifndef GL_VERSION_1_0
+#define GL_VERSION_1_0 1
+GLAPI int GLAD_GL_VERSION_1_0;
+typedef void (APIENTRYP PFNGLCULLFACEPROC)(GLenum);
+GLAPI PFNGLCULLFACEPROC glad_glCullFace;
+#define glCullFace glad_glCullFace
+typedef void (APIENTRYP PFNGLFRONTFACEPROC)(GLenum);
+GLAPI PFNGLFRONTFACEPROC glad_glFrontFace;
+#define glFrontFace glad_glFrontFace
+typedef void (APIENTRYP PFNGLHINTPROC)(GLenum, GLenum);
+GLAPI PFNGLHINTPROC glad_glHint;
+#define glHint glad_glHint
+typedef void (APIENTRYP PFNGLLINEWIDTHPROC)(GLfloat);
+GLAPI PFNGLLINEWIDTHPROC glad_glLineWidth;
+#define glLineWidth glad_glLineWidth
+typedef void (APIENTRYP PFNGLPOINTSIZEPROC)(GLfloat);
+GLAPI PFNGLPOINTSIZEPROC glad_glPointSize;
+#define glPointSize glad_glPointSize
+typedef void (APIENTRYP PFNGLPOLYGONMODEPROC)(GLenum, GLenum);
+GLAPI PFNGLPOLYGONMODEPROC glad_glPolygonMode;
+#define glPolygonMode glad_glPolygonMode
+typedef void (APIENTRYP PFNGLSCISSORPROC)(GLint, GLint, GLsizei, GLsizei);
+GLAPI PFNGLSCISSORPROC glad_glScissor;
+#define glScissor glad_glScissor
+typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC)(GLenum, GLenum, GLfloat);
+GLAPI PFNGLTEXPARAMETERFPROC glad_glTexParameterf;
+#define glTexParameterf glad_glTexParameterf
+typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum, GLenum, const GLfloat*);
+GLAPI PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv;
+#define glTexParameterfv glad_glTexParameterfv
+typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum, GLenum, GLint);
+GLAPI PFNGLTEXPARAMETERIPROC glad_glTexParameteri;
+#define glTexParameteri glad_glTexParameteri
+typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum, GLenum, const GLint*);
+GLAPI PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv;
+#define glTexParameteriv glad_glTexParameteriv
+typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC)(GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum, const void*);
+GLAPI PFNGLTEXIMAGE1DPROC glad_glTexImage1D;
+#define glTexImage1D glad_glTexImage1D
+typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*);
+GLAPI PFNGLTEXIMAGE2DPROC glad_glTexImage2D;
+#define glTexImage2D glad_glTexImage2D
+typedef void (APIENTRYP PFNGLDRAWBUFFERPROC)(GLenum);
+GLAPI PFNGLDRAWBUFFERPROC glad_glDrawBuffer;
+#define glDrawBuffer glad_glDrawBuffer
+typedef void (APIENTRYP PFNGLCLEARPROC)(GLbitfield);
+GLAPI PFNGLCLEARPROC glad_glClear;
+#define glClear glad_glClear
+typedef void (APIENTRYP PFNGLCLEARCOLORPROC)(GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI PFNGLCLEARCOLORPROC glad_glClearColor;
+#define glClearColor glad_glClearColor
+typedef void (APIENTRYP PFNGLCLEARSTENCILPROC)(GLint);
+GLAPI PFNGLCLEARSTENCILPROC glad_glClearStencil;
+#define glClearStencil glad_glClearStencil
+typedef void (APIENTRYP PFNGLCLEARDEPTHPROC)(GLdouble);
+GLAPI PFNGLCLEARDEPTHPROC glad_glClearDepth;
+#define glClearDepth glad_glClearDepth
+typedef void (APIENTRYP PFNGLSTENCILMASKPROC)(GLuint);
+GLAPI PFNGLSTENCILMASKPROC glad_glStencilMask;
+#define glStencilMask glad_glStencilMask
+typedef void (APIENTRYP PFNGLCOLORMASKPROC)(GLboolean, GLboolean, GLboolean, GLboolean);
+GLAPI PFNGLCOLORMASKPROC glad_glColorMask;
+#define glColorMask glad_glColorMask
+typedef void (APIENTRYP PFNGLDEPTHMASKPROC)(GLboolean);
+GLAPI PFNGLDEPTHMASKPROC glad_glDepthMask;
+#define glDepthMask glad_glDepthMask
+typedef void (APIENTRYP PFNGLDISABLEPROC)(GLenum);
+GLAPI PFNGLDISABLEPROC glad_glDisable;
+#define glDisable glad_glDisable
+typedef void (APIENTRYP PFNGLENABLEPROC)(GLenum);
+GLAPI PFNGLENABLEPROC glad_glEnable;
+#define glEnable glad_glEnable
+typedef void (APIENTRYP PFNGLFINISHPROC)();
+GLAPI PFNGLFINISHPROC glad_glFinish;
+#define glFinish glad_glFinish
+typedef void (APIENTRYP PFNGLFLUSHPROC)();
+GLAPI PFNGLFLUSHPROC glad_glFlush;
+#define glFlush glad_glFlush
+typedef void (APIENTRYP PFNGLBLENDFUNCPROC)(GLenum, GLenum);
+GLAPI PFNGLBLENDFUNCPROC glad_glBlendFunc;
+#define glBlendFunc glad_glBlendFunc
+typedef void (APIENTRYP PFNGLLOGICOPPROC)(GLenum);
+GLAPI PFNGLLOGICOPPROC glad_glLogicOp;
+#define glLogicOp glad_glLogicOp
+typedef void (APIENTRYP PFNGLSTENCILFUNCPROC)(GLenum, GLint, GLuint);
+GLAPI PFNGLSTENCILFUNCPROC glad_glStencilFunc;
+#define glStencilFunc glad_glStencilFunc
+typedef void (APIENTRYP PFNGLSTENCILOPPROC)(GLenum, GLenum, GLenum);
+GLAPI PFNGLSTENCILOPPROC glad_glStencilOp;
+#define glStencilOp glad_glStencilOp
+typedef void (APIENTRYP PFNGLDEPTHFUNCPROC)(GLenum);
+GLAPI PFNGLDEPTHFUNCPROC glad_glDepthFunc;
+#define glDepthFunc glad_glDepthFunc
+typedef void (APIENTRYP PFNGLPIXELSTOREFPROC)(GLenum, GLfloat);
+GLAPI PFNGLPIXELSTOREFPROC glad_glPixelStoref;
+#define glPixelStoref glad_glPixelStoref
+typedef void (APIENTRYP PFNGLPIXELSTOREIPROC)(GLenum, GLint);
+GLAPI PFNGLPIXELSTOREIPROC glad_glPixelStorei;
+#define glPixelStorei glad_glPixelStorei
+typedef void (APIENTRYP PFNGLREADBUFFERPROC)(GLenum);
+GLAPI PFNGLREADBUFFERPROC glad_glReadBuffer;
+#define glReadBuffer glad_glReadBuffer
+typedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, void*);
+GLAPI PFNGLREADPIXELSPROC glad_glReadPixels;
+#define glReadPixels glad_glReadPixels
+typedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum, GLboolean*);
+GLAPI PFNGLGETBOOLEANVPROC glad_glGetBooleanv;
+#define glGetBooleanv glad_glGetBooleanv
+typedef void (APIENTRYP PFNGLGETDOUBLEVPROC)(GLenum, GLdouble*);
+GLAPI PFNGLGETDOUBLEVPROC glad_glGetDoublev;
+#define glGetDoublev glad_glGetDoublev
+typedef GLenum (APIENTRYP PFNGLGETERRORPROC)();
+GLAPI PFNGLGETERRORPROC glad_glGetError;
+#define glGetError glad_glGetError
+typedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum, GLfloat*);
+GLAPI PFNGLGETFLOATVPROC glad_glGetFloatv;
+#define glGetFloatv glad_glGetFloatv
+typedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum, GLint*);
+GLAPI PFNGLGETINTEGERVPROC glad_glGetIntegerv;
+#define glGetIntegerv glad_glGetIntegerv
+typedef const GLubyte* (APIENTRYP PFNGLGETSTRINGPROC)(GLenum);
+GLAPI PFNGLGETSTRINGPROC glad_glGetString;
+#define glGetString glad_glGetString
+typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC)(GLenum, GLint, GLenum, GLenum, void*);
+GLAPI PFNGLGETTEXIMAGEPROC glad_glGetTexImage;
+#define glGetTexImage glad_glGetTexImage
+typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum, GLenum, GLfloat*);
+GLAPI PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv;
+#define glGetTexParameterfv glad_glGetTexParameterfv
+typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum, GLenum, GLint*);
+GLAPI PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv;
+#define glGetTexParameteriv glad_glGetTexParameteriv
+typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum, GLint, GLenum, GLfloat*);
+GLAPI PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv;
+#define glGetTexLevelParameterfv glad_glGetTexLevelParameterfv
+typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum, GLint, GLenum, GLint*);
+GLAPI PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv;
+#define glGetTexLevelParameteriv glad_glGetTexLevelParameteriv
+typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum);
+GLAPI PFNGLISENABLEDPROC glad_glIsEnabled;
+#define glIsEnabled glad_glIsEnabled
+typedef void (APIENTRYP PFNGLDEPTHRANGEPROC)(GLdouble, GLdouble);
+GLAPI PFNGLDEPTHRANGEPROC glad_glDepthRange;
+#define glDepthRange glad_glDepthRange
+typedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint, GLint, GLsizei, GLsizei);
+GLAPI PFNGLVIEWPORTPROC glad_glViewport;
+#define glViewport glad_glViewport
+#endif
+#ifndef GL_VERSION_1_1
+#define GL_VERSION_1_1 1
+GLAPI int GLAD_GL_VERSION_1_1;
+typedef void (APIENTRYP PFNGLDRAWARRAYSPROC)(GLenum, GLint, GLsizei);
+GLAPI PFNGLDRAWARRAYSPROC glad_glDrawArrays;
+#define glDrawArrays glad_glDrawArrays
+typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum, GLsizei, GLenum, const void*);
+GLAPI PFNGLDRAWELEMENTSPROC glad_glDrawElements;
+#define glDrawElements glad_glDrawElements
+typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat, GLfloat);
+GLAPI PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset;
+#define glPolygonOffset glad_glPolygonOffset
+typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC)(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLint);
+GLAPI PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D;
+#define glCopyTexImage1D glad_glCopyTexImage1D
+typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC)(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint);
+GLAPI PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D;
+#define glCopyTexImage2D glad_glCopyTexImage2D
+typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum, GLint, GLint, GLint, GLint, GLsizei);
+GLAPI PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D;
+#define glCopyTexSubImage1D glad_glCopyTexSubImage1D
+typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);
+GLAPI PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D;
+#define glCopyTexSubImage2D glad_glCopyTexSubImage2D
+typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC)(GLenum, GLint, GLint, GLsizei, GLenum, GLenum, const void*);
+GLAPI PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D;
+#define glTexSubImage1D glad_glTexSubImage1D
+typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void*);
+GLAPI PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D;
+#define glTexSubImage2D glad_glTexSubImage2D
+typedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum, GLuint);
+GLAPI PFNGLBINDTEXTUREPROC glad_glBindTexture;
+#define glBindTexture glad_glBindTexture
+typedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei, const GLuint*);
+GLAPI PFNGLDELETETEXTURESPROC glad_glDeleteTextures;
+#define glDeleteTextures glad_glDeleteTextures
+typedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei, GLuint*);
+GLAPI PFNGLGENTEXTURESPROC glad_glGenTextures;
+#define glGenTextures glad_glGenTextures
+typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint);
+GLAPI PFNGLISTEXTUREPROC glad_glIsTexture;
+#define glIsTexture glad_glIsTexture
+#endif
+#ifndef GL_VERSION_1_2
+#define GL_VERSION_1_2 1
+GLAPI int GLAD_GL_VERSION_1_2;
+typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC)(GLenum, GLuint, GLuint, GLsizei, GLenum, const void*);
+GLAPI PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements;
+#define glDrawRangeElements glad_glDrawRangeElements
+typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC)(GLenum, GLint, GLint, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*);
+GLAPI PFNGLTEXIMAGE3DPROC glad_glTexImage3D;
+#define glTexImage3D glad_glTexImage3D
+typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const void*);
+GLAPI PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D;
+#define glTexSubImage3D glad_glTexSubImage3D
+typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum, GLint, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);
+GLAPI PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D;
+#define glCopyTexSubImage3D glad_glCopyTexSubImage3D
+#endif
+#ifndef GL_VERSION_1_3
+#define GL_VERSION_1_3 1
+GLAPI int GLAD_GL_VERSION_1_3;
+typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum);
+GLAPI PFNGLACTIVETEXTUREPROC glad_glActiveTexture;
+#define glActiveTexture glad_glActiveTexture
+typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat, GLboolean);
+GLAPI PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage;
+#define glSampleCoverage glad_glSampleCoverage
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const void*);
+GLAPI PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D;
+#define glCompressedTexImage3D glad_glCompressedTexImage3D
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const void*);
+GLAPI PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D;
+#define glCompressedTexImage2D glad_glCompressedTexImage2D
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const void*);
+GLAPI PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D;
+#define glCompressedTexImage1D glad_glCompressedTexImage1D
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const void*);
+GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D;
+#define glCompressedTexSubImage3D glad_glCompressedTexSubImage3D
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const void*);
+GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D;
+#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const void*);
+GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D;
+#define glCompressedTexSubImage1D glad_glCompressedTexSubImage1D
+typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum, GLint, void*);
+GLAPI PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage;
+#define glGetCompressedTexImage glad_glGetCompressedTexImage
+#endif
+#ifndef GL_VERSION_1_4
+#define GL_VERSION_1_4 1
+GLAPI int GLAD_GL_VERSION_1_4;
+typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum, GLenum, GLenum, GLenum);
+GLAPI PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate;
+#define glBlendFuncSeparate glad_glBlendFuncSeparate
+typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC)(GLenum, const GLint*, const GLsizei*, GLsizei);
+GLAPI PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays;
+#define glMultiDrawArrays glad_glMultiDrawArrays
+typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC)(GLenum, const GLsizei*, GLenum, const void**, GLsizei);
+GLAPI PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements;
+#define glMultiDrawElements glad_glMultiDrawElements
+typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC)(GLenum, GLfloat);
+GLAPI PFNGLPOINTPARAMETERFPROC glad_glPointParameterf;
+#define glPointParameterf glad_glPointParameterf
+typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC)(GLenum, const GLfloat*);
+GLAPI PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv;
+#define glPointParameterfv glad_glPointParameterfv
+typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC)(GLenum, GLint);
+GLAPI PFNGLPOINTPARAMETERIPROC glad_glPointParameteri;
+#define glPointParameteri glad_glPointParameteri
+typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC)(GLenum, const GLint*);
+GLAPI PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv;
+#define glPointParameteriv glad_glPointParameteriv
+typedef void (APIENTRYP PFNGLBLENDCOLORPROC)(GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI PFNGLBLENDCOLORPROC glad_glBlendColor;
+#define glBlendColor glad_glBlendColor
+typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC)(GLenum);
+GLAPI PFNGLBLENDEQUATIONPROC glad_glBlendEquation;
+#define glBlendEquation glad_glBlendEquation
+#endif
+#ifndef GL_VERSION_1_5
+#define GL_VERSION_1_5 1
+GLAPI int GLAD_GL_VERSION_1_5;
+typedef void (APIENTRYP PFNGLGENQUERIESPROC)(GLsizei, GLuint*);
+GLAPI PFNGLGENQUERIESPROC glad_glGenQueries;
+#define glGenQueries glad_glGenQueries
+typedef void (APIENTRYP PFNGLDELETEQUERIESPROC)(GLsizei, const GLuint*);
+GLAPI PFNGLDELETEQUERIESPROC glad_glDeleteQueries;
+#define glDeleteQueries glad_glDeleteQueries
+typedef GLboolean (APIENTRYP PFNGLISQUERYPROC)(GLuint);
+GLAPI PFNGLISQUERYPROC glad_glIsQuery;
+#define glIsQuery glad_glIsQuery
+typedef void (APIENTRYP PFNGLBEGINQUERYPROC)(GLenum, GLuint);
+GLAPI PFNGLBEGINQUERYPROC glad_glBeginQuery;
+#define glBeginQuery glad_glBeginQuery
+typedef void (APIENTRYP PFNGLENDQUERYPROC)(GLenum);
+GLAPI PFNGLENDQUERYPROC glad_glEndQuery;
+#define glEndQuery glad_glEndQuery
+typedef void (APIENTRYP PFNGLGETQUERYIVPROC)(GLenum, GLenum, GLint*);
+GLAPI PFNGLGETQUERYIVPROC glad_glGetQueryiv;
+#define glGetQueryiv glad_glGetQueryiv
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC)(GLuint, GLenum, GLint*);
+GLAPI PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv;
+#define glGetQueryObjectiv glad_glGetQueryObjectiv
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC)(GLuint, GLenum, GLuint*);
+GLAPI PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv;
+#define glGetQueryObjectuiv glad_glGetQueryObjectuiv
+typedef void (APIENTRYP PFNGLBINDBUFFERPROC)(GLenum, GLuint);
+GLAPI PFNGLBINDBUFFERPROC glad_glBindBuffer;
+#define glBindBuffer glad_glBindBuffer
+typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei, const GLuint*);
+GLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers;
+#define glDeleteBuffers glad_glDeleteBuffers
+typedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei, GLuint*);
+GLAPI PFNGLGENBUFFERSPROC glad_glGenBuffers;
+#define glGenBuffers glad_glGenBuffers
+typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint);
+GLAPI PFNGLISBUFFERPROC glad_glIsBuffer;
+#define glIsBuffer glad_glIsBuffer
+typedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum, GLsizeiptr, const void*, GLenum);
+GLAPI PFNGLBUFFERDATAPROC glad_glBufferData;
+#define glBufferData glad_glBufferData
+typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum, GLintptr, GLsizeiptr, const void*);
+GLAPI PFNGLBUFFERSUBDATAPROC glad_glBufferSubData;
+#define glBufferSubData glad_glBufferSubData
+typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC)(GLenum, GLintptr, GLsizeiptr, void*);
+GLAPI PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData;
+#define glGetBufferSubData glad_glGetBufferSubData
+typedef void* (APIENTRYP PFNGLMAPBUFFERPROC)(GLenum, GLenum);
+GLAPI PFNGLMAPBUFFERPROC glad_glMapBuffer;
+#define glMapBuffer glad_glMapBuffer
+typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC)(GLenum);
+GLAPI PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer;
+#define glUnmapBuffer glad_glUnmapBuffer
+typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum, GLenum, GLint*);
+GLAPI PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv;
+#define glGetBufferParameteriv glad_glGetBufferParameteriv
+typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum, GLenum, void**);
+GLAPI PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv;
+#define glGetBufferPointerv glad_glGetBufferPointerv
+#endif
+#ifndef GL_VERSION_2_0
+#define GL_VERSION_2_0 1
+GLAPI int GLAD_GL_VERSION_2_0;
+typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum, GLenum);
+GLAPI PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate;
+#define glBlendEquationSeparate glad_glBlendEquationSeparate
+typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC)(GLsizei, const GLenum*);
+GLAPI PFNGLDRAWBUFFERSPROC glad_glDrawBuffers;
+#define glDrawBuffers glad_glDrawBuffers
+typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum, GLenum, GLenum, GLenum);
+GLAPI PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate;
+#define glStencilOpSeparate glad_glStencilOpSeparate
+typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC)(GLenum, GLenum, GLint, GLuint);
+GLAPI PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate;
+#define glStencilFuncSeparate glad_glStencilFuncSeparate
+typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC)(GLenum, GLuint);
+GLAPI PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate;
+#define glStencilMaskSeparate glad_glStencilMaskSeparate
+typedef void (APIENTRYP PFNGLATTACHSHADERPROC)(GLuint, GLuint);
+GLAPI PFNGLATTACHSHADERPROC glad_glAttachShader;
+#define glAttachShader glad_glAttachShader
+typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint, GLuint, const GLchar*);
+GLAPI PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation;
+#define glBindAttribLocation glad_glBindAttribLocation
+typedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint);
+GLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader;
+#define glCompileShader glad_glCompileShader
+typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)();
+GLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram;
+#define glCreateProgram glad_glCreateProgram
+typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC)(GLenum);
+GLAPI PFNGLCREATESHADERPROC glad_glCreateShader;
+#define glCreateShader glad_glCreateShader
+typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint);
+GLAPI PFNGLDELETEPROGRAMPROC glad_glDeleteProgram;
+#define glDeleteProgram glad_glDeleteProgram
+typedef void (APIENTRYP PFNGLDELETESHADERPROC)(GLuint);
+GLAPI PFNGLDELETESHADERPROC glad_glDeleteShader;
+#define glDeleteShader glad_glDeleteShader
+typedef void (APIENTRYP PFNGLDETACHSHADERPROC)(GLuint, GLuint);
+GLAPI PFNGLDETACHSHADERPROC glad_glDetachShader;
+#define glDetachShader glad_glDetachShader
+typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint);
+GLAPI PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray;
+#define glDisableVertexAttribArray glad_glDisableVertexAttribArray
+typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint);
+GLAPI PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray;
+#define glEnableVertexAttribArray glad_glEnableVertexAttribArray
+typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLint*, GLenum*, GLchar*);
+GLAPI PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib;
+#define glGetActiveAttrib glad_glGetActiveAttrib
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLint*, GLenum*, GLchar*);
+GLAPI PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform;
+#define glGetActiveUniform glad_glGetActiveUniform
+typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint, GLsizei, GLsizei*, GLuint*);
+GLAPI PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders;
+#define glGetAttachedShaders glad_glGetAttachedShaders
+typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint, const GLchar*);
+GLAPI PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation;
+#define glGetAttribLocation glad_glGetAttribLocation
+typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint, GLenum, GLint*);
+GLAPI PFNGLGETPROGRAMIVPROC glad_glGetProgramiv;
+#define glGetProgramiv glad_glGetProgramiv
+typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint, GLsizei, GLsizei*, GLchar*);
+GLAPI PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog;
+#define glGetProgramInfoLog glad_glGetProgramInfoLog
+typedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint, GLenum, GLint*);
+GLAPI PFNGLGETSHADERIVPROC glad_glGetShaderiv;
+#define glGetShaderiv glad_glGetShaderiv
+typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint, GLsizei, GLsizei*, GLchar*);
+GLAPI PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog;
+#define glGetShaderInfoLog glad_glGetShaderInfoLog
+typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint, GLsizei, GLsizei*, GLchar*);
+GLAPI PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource;
+#define glGetShaderSource glad_glGetShaderSource
+typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint, const GLchar*);
+GLAPI PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation;
+#define glGetUniformLocation glad_glGetUniformLocation
+typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint, GLint, GLfloat*);
+GLAPI PFNGLGETUNIFORMFVPROC glad_glGetUniformfv;
+#define glGetUniformfv glad_glGetUniformfv
+typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint, GLint, GLint*);
+GLAPI PFNGLGETUNIFORMIVPROC glad_glGetUniformiv;
+#define glGetUniformiv glad_glGetUniformiv
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC)(GLuint, GLenum, GLdouble*);
+GLAPI PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv;
+#define glGetVertexAttribdv glad_glGetVertexAttribdv
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint, GLenum, GLfloat*);
+GLAPI PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv;
+#define glGetVertexAttribfv glad_glGetVertexAttribfv
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint, GLenum, GLint*);
+GLAPI PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv;
+#define glGetVertexAttribiv glad_glGetVertexAttribiv
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint, GLenum, void**);
+GLAPI PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv;
+#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv
+typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint);
+GLAPI PFNGLISPROGRAMPROC glad_glIsProgram;
+#define glIsProgram glad_glIsProgram
+typedef GLboolean (APIENTRYP PFNGLISSHADERPROC)(GLuint);
+GLAPI PFNGLISSHADERPROC glad_glIsShader;
+#define glIsShader glad_glIsShader
+typedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint);
+GLAPI PFNGLLINKPROGRAMPROC glad_glLinkProgram;
+#define glLinkProgram glad_glLinkProgram
+typedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint, GLsizei, const GLchar**, const GLint*);
+GLAPI PFNGLSHADERSOURCEPROC glad_glShaderSource;
+#define glShaderSource glad_glShaderSource
+typedef void (APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint);
+GLAPI PFNGLUSEPROGRAMPROC glad_glUseProgram;
+#define glUseProgram glad_glUseProgram
+typedef void (APIENTRYP PFNGLUNIFORM1FPROC)(GLint, GLfloat);
+GLAPI PFNGLUNIFORM1FPROC glad_glUniform1f;
+#define glUniform1f glad_glUniform1f
+typedef void (APIENTRYP PFNGLUNIFORM2FPROC)(GLint, GLfloat, GLfloat);
+GLAPI PFNGLUNIFORM2FPROC glad_glUniform2f;
+#define glUniform2f glad_glUniform2f
+typedef void (APIENTRYP PFNGLUNIFORM3FPROC)(GLint, GLfloat, GLfloat, GLfloat);
+GLAPI PFNGLUNIFORM3FPROC glad_glUniform3f;
+#define glUniform3f glad_glUniform3f
+typedef void (APIENTRYP PFNGLUNIFORM4FPROC)(GLint, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI PFNGLUNIFORM4FPROC glad_glUniform4f;
+#define glUniform4f glad_glUniform4f
+typedef void (APIENTRYP PFNGLUNIFORM1IPROC)(GLint, GLint);
+GLAPI PFNGLUNIFORM1IPROC glad_glUniform1i;
+#define glUniform1i glad_glUniform1i
+typedef void (APIENTRYP PFNGLUNIFORM2IPROC)(GLint, GLint, GLint);
+GLAPI PFNGLUNIFORM2IPROC glad_glUniform2i;
+#define glUniform2i glad_glUniform2i
+typedef void (APIENTRYP PFNGLUNIFORM3IPROC)(GLint, GLint, GLint, GLint);
+GLAPI PFNGLUNIFORM3IPROC glad_glUniform3i;
+#define glUniform3i glad_glUniform3i
+typedef void (APIENTRYP PFNGLUNIFORM4IPROC)(GLint, GLint, GLint, GLint, GLint);
+GLAPI PFNGLUNIFORM4IPROC glad_glUniform4i;
+#define glUniform4i glad_glUniform4i
+typedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint, GLsizei, const GLfloat*);
+GLAPI PFNGLUNIFORM1FVPROC glad_glUniform1fv;
+#define glUniform1fv glad_glUniform1fv
+typedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint, GLsizei, const GLfloat*);
+GLAPI PFNGLUNIFORM2FVPROC glad_glUniform2fv;
+#define glUniform2fv glad_glUniform2fv
+typedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint, GLsizei, const GLfloat*);
+GLAPI PFNGLUNIFORM3FVPROC glad_glUniform3fv;
+#define glUniform3fv glad_glUniform3fv
+typedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint, GLsizei, const GLfloat*);
+GLAPI PFNGLUNIFORM4FVPROC glad_glUniform4fv;
+#define glUniform4fv glad_glUniform4fv
+typedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint, GLsizei, const GLint*);
+GLAPI PFNGLUNIFORM1IVPROC glad_glUniform1iv;
+#define glUniform1iv glad_glUniform1iv
+typedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint, GLsizei, const GLint*);
+GLAPI PFNGLUNIFORM2IVPROC glad_glUniform2iv;
+#define glUniform2iv glad_glUniform2iv
+typedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint, GLsizei, const GLint*);
+GLAPI PFNGLUNIFORM3IVPROC glad_glUniform3iv;
+#define glUniform3iv glad_glUniform3iv
+typedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint, GLsizei, const GLint*);
+GLAPI PFNGLUNIFORM4IVPROC glad_glUniform4iv;
+#define glUniform4iv glad_glUniform4iv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);
+GLAPI PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv;
+#define glUniformMatrix2fv glad_glUniformMatrix2fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);
+GLAPI PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv;
+#define glUniformMatrix3fv glad_glUniformMatrix3fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);
+GLAPI PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv;
+#define glUniformMatrix4fv glad_glUniformMatrix4fv
+typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint);
+GLAPI PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram;
+#define glValidateProgram glad_glValidateProgram
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC)(GLuint, GLdouble);
+GLAPI PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d;
+#define glVertexAttrib1d glad_glVertexAttrib1d
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC)(GLuint, const GLdouble*);
+GLAPI PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv;
+#define glVertexAttrib1dv glad_glVertexAttrib1dv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC)(GLuint, GLfloat);
+GLAPI PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f;
+#define glVertexAttrib1f glad_glVertexAttrib1f
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint, const GLfloat*);
+GLAPI PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv;
+#define glVertexAttrib1fv glad_glVertexAttrib1fv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC)(GLuint, GLshort);
+GLAPI PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s;
+#define glVertexAttrib1s glad_glVertexAttrib1s
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC)(GLuint, const GLshort*);
+GLAPI PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv;
+#define glVertexAttrib1sv glad_glVertexAttrib1sv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC)(GLuint, GLdouble, GLdouble);
+GLAPI PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d;
+#define glVertexAttrib2d glad_glVertexAttrib2d
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC)(GLuint, const GLdouble*);
+GLAPI PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv;
+#define glVertexAttrib2dv glad_glVertexAttrib2dv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC)(GLuint, GLfloat, GLfloat);
+GLAPI PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f;
+#define glVertexAttrib2f glad_glVertexAttrib2f
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint, const GLfloat*);
+GLAPI PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv;
+#define glVertexAttrib2fv glad_glVertexAttrib2fv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC)(GLuint, GLshort, GLshort);
+GLAPI PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s;
+#define glVertexAttrib2s glad_glVertexAttrib2s
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC)(GLuint, const GLshort*);
+GLAPI PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv;
+#define glVertexAttrib2sv glad_glVertexAttrib2sv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC)(GLuint, GLdouble, GLdouble, GLdouble);
+GLAPI PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d;
+#define glVertexAttrib3d glad_glVertexAttrib3d
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC)(GLuint, const GLdouble*);
+GLAPI PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv;
+#define glVertexAttrib3dv glad_glVertexAttrib3dv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC)(GLuint, GLfloat, GLfloat, GLfloat);
+GLAPI PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f;
+#define glVertexAttrib3f glad_glVertexAttrib3f
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint, const GLfloat*);
+GLAPI PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv;
+#define glVertexAttrib3fv glad_glVertexAttrib3fv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC)(GLuint, GLshort, GLshort, GLshort);
+GLAPI PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s;
+#define glVertexAttrib3s glad_glVertexAttrib3s
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC)(GLuint, const GLshort*);
+GLAPI PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv;
+#define glVertexAttrib3sv glad_glVertexAttrib3sv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC)(GLuint, const GLbyte*);
+GLAPI PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv;
+#define glVertexAttrib4Nbv glad_glVertexAttrib4Nbv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC)(GLuint, const GLint*);
+GLAPI PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv;
+#define glVertexAttrib4Niv glad_glVertexAttrib4Niv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC)(GLuint, const GLshort*);
+GLAPI PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv;
+#define glVertexAttrib4Nsv glad_glVertexAttrib4Nsv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC)(GLuint, GLubyte, GLubyte, GLubyte, GLubyte);
+GLAPI PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub;
+#define glVertexAttrib4Nub glad_glVertexAttrib4Nub
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC)(GLuint, const GLubyte*);
+GLAPI PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv;
+#define glVertexAttrib4Nubv glad_glVertexAttrib4Nubv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC)(GLuint, const GLuint*);
+GLAPI PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv;
+#define glVertexAttrib4Nuiv glad_glVertexAttrib4Nuiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC)(GLuint, const GLushort*);
+GLAPI PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv;
+#define glVertexAttrib4Nusv glad_glVertexAttrib4Nusv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC)(GLuint, const GLbyte*);
+GLAPI PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv;
+#define glVertexAttrib4bv glad_glVertexAttrib4bv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC)(GLuint, GLdouble, GLdouble, GLdouble, GLdouble);
+GLAPI PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d;
+#define glVertexAttrib4d glad_glVertexAttrib4d
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC)(GLuint, const GLdouble*);
+GLAPI PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv;
+#define glVertexAttrib4dv glad_glVertexAttrib4dv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC)(GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
+GLAPI PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f;
+#define glVertexAttrib4f glad_glVertexAttrib4f
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint, const GLfloat*);
+GLAPI PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv;
+#define glVertexAttrib4fv glad_glVertexAttrib4fv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC)(GLuint, const GLint*);
+GLAPI PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv;
+#define glVertexAttrib4iv glad_glVertexAttrib4iv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC)(GLuint, GLshort, GLshort, GLshort, GLshort);
+GLAPI PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s;
+#define glVertexAttrib4s glad_glVertexAttrib4s
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC)(GLuint, const GLshort*);
+GLAPI PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv;
+#define glVertexAttrib4sv glad_glVertexAttrib4sv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC)(GLuint, const GLubyte*);
+GLAPI PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv;
+#define glVertexAttrib4ubv glad_glVertexAttrib4ubv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC)(GLuint, const GLuint*);
+GLAPI PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv;
+#define glVertexAttrib4uiv glad_glVertexAttrib4uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC)(GLuint, const GLushort*);
+GLAPI PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv;
+#define glVertexAttrib4usv glad_glVertexAttrib4usv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint, GLint, GLenum, GLboolean, GLsizei, const void*);
+GLAPI PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer;
+#define glVertexAttribPointer glad_glVertexAttribPointer
+#endif
+#ifndef GL_VERSION_2_1
+#define GL_VERSION_2_1 1
+GLAPI int GLAD_GL_VERSION_2_1;
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);
+GLAPI PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv;
+#define glUniformMatrix2x3fv glad_glUniformMatrix2x3fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);
+GLAPI PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv;
+#define glUniformMatrix3x2fv glad_glUniformMatrix3x2fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);
+GLAPI PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv;
+#define glUniformMatrix2x4fv glad_glUniformMatrix2x4fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);
+GLAPI PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv;
+#define glUniformMatrix4x2fv glad_glUniformMatrix4x2fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);
+GLAPI PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv;
+#define glUniformMatrix3x4fv glad_glUniformMatrix3x4fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);
+GLAPI PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv;
+#define glUniformMatrix4x3fv glad_glUniformMatrix4x3fv
+#endif
+#ifndef GL_VERSION_3_0
+#define GL_VERSION_3_0 1
+GLAPI int GLAD_GL_VERSION_3_0;
+typedef void (APIENTRYP PFNGLCOLORMASKIPROC)(GLuint, GLboolean, GLboolean, GLboolean, GLboolean);
+GLAPI PFNGLCOLORMASKIPROC glad_glColorMaski;
+#define glColorMaski glad_glColorMaski
+typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC)(GLenum, GLuint, GLboolean*);
+GLAPI PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v;
+#define glGetBooleani_v glad_glGetBooleani_v
+typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC)(GLenum, GLuint, GLint*);
+GLAPI PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v;
+#define glGetIntegeri_v glad_glGetIntegeri_v
+typedef void (APIENTRYP PFNGLENABLEIPROC)(GLenum, GLuint);
+GLAPI PFNGLENABLEIPROC glad_glEnablei;
+#define glEnablei glad_glEnablei
+typedef void (APIENTRYP PFNGLDISABLEIPROC)(GLenum, GLuint);
+GLAPI PFNGLDISABLEIPROC glad_glDisablei;
+#define glDisablei glad_glDisablei
+typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC)(GLenum, GLuint);
+GLAPI PFNGLISENABLEDIPROC glad_glIsEnabledi;
+#define glIsEnabledi glad_glIsEnabledi
+typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum);
+GLAPI PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback;
+#define glBeginTransformFeedback glad_glBeginTransformFeedback
+typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)();
+GLAPI PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback;
+#define glEndTransformFeedback glad_glEndTransformFeedback
+typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC)(GLenum, GLuint, GLuint, GLintptr, GLsizeiptr);
+GLAPI PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange;
+#define glBindBufferRange glad_glBindBufferRange
+typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC)(GLenum, GLuint, GLuint);
+GLAPI PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase;
+#define glBindBufferBase glad_glBindBufferBase
+typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint, GLsizei, const GLchar**, GLenum);
+GLAPI PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings;
+#define glTransformFeedbackVaryings glad_glTransformFeedbackVaryings
+typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLsizei*, GLenum*, GLchar*);
+GLAPI PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying;
+#define glGetTransformFeedbackVarying glad_glGetTransformFeedbackVarying
+typedef void (APIENTRYP PFNGLCLAMPCOLORPROC)(GLenum, GLenum);
+GLAPI PFNGLCLAMPCOLORPROC glad_glClampColor;
+#define glClampColor glad_glClampColor
+typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC)(GLuint, GLenum);
+GLAPI PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender;
+#define glBeginConditionalRender glad_glBeginConditionalRender
+typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)();
+GLAPI PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender;
+#define glEndConditionalRender glad_glEndConditionalRender
+typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint, GLint, GLenum, GLsizei, const void*);
+GLAPI PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer;
+#define glVertexAttribIPointer glad_glVertexAttribIPointer
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC)(GLuint, GLenum, GLint*);
+GLAPI PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv;
+#define glGetVertexAttribIiv glad_glGetVertexAttribIiv
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint, GLenum, GLuint*);
+GLAPI PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv;
+#define glGetVertexAttribIuiv glad_glGetVertexAttribIuiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC)(GLuint, GLint);
+GLAPI PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i;
+#define glVertexAttribI1i glad_glVertexAttribI1i
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC)(GLuint, GLint, GLint);
+GLAPI PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i;
+#define glVertexAttribI2i glad_glVertexAttribI2i
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC)(GLuint, GLint, GLint, GLint);
+GLAPI PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i;
+#define glVertexAttribI3i glad_glVertexAttribI3i
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC)(GLuint, GLint, GLint, GLint, GLint);
+GLAPI PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i;
+#define glVertexAttribI4i glad_glVertexAttribI4i
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC)(GLuint, GLuint);
+GLAPI PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui;
+#define glVertexAttribI1ui glad_glVertexAttribI1ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC)(GLuint, GLuint, GLuint);
+GLAPI PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui;
+#define glVertexAttribI2ui glad_glVertexAttribI2ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC)(GLuint, GLuint, GLuint, GLuint);
+GLAPI PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui;
+#define glVertexAttribI3ui glad_glVertexAttribI3ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC)(GLuint, GLuint, GLuint, GLuint, GLuint);
+GLAPI PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui;
+#define glVertexAttribI4ui glad_glVertexAttribI4ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC)(GLuint, const GLint*);
+GLAPI PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv;
+#define glVertexAttribI1iv glad_glVertexAttribI1iv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC)(GLuint, const GLint*);
+GLAPI PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv;
+#define glVertexAttribI2iv glad_glVertexAttribI2iv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC)(GLuint, const GLint*);
+GLAPI PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv;
+#define glVertexAttribI3iv glad_glVertexAttribI3iv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC)(GLuint, const GLint*);
+GLAPI PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv;
+#define glVertexAttribI4iv glad_glVertexAttribI4iv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC)(GLuint, const GLuint*);
+GLAPI PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv;
+#define glVertexAttribI1uiv glad_glVertexAttribI1uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC)(GLuint, const GLuint*);
+GLAPI PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv;
+#define glVertexAttribI2uiv glad_glVertexAttribI2uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC)(GLuint, const GLuint*);
+GLAPI PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv;
+#define glVertexAttribI3uiv glad_glVertexAttribI3uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC)(GLuint, const GLuint*);
+GLAPI PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv;
+#define glVertexAttribI4uiv glad_glVertexAttribI4uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC)(GLuint, const GLbyte*);
+GLAPI PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv;
+#define glVertexAttribI4bv glad_glVertexAttribI4bv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC)(GLuint, const GLshort*);
+GLAPI PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv;
+#define glVertexAttribI4sv glad_glVertexAttribI4sv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC)(GLuint, const GLubyte*);
+GLAPI PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv;
+#define glVertexAttribI4ubv glad_glVertexAttribI4ubv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC)(GLuint, const GLushort*);
+GLAPI PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv;
+#define glVertexAttribI4usv glad_glVertexAttribI4usv
+typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC)(GLuint, GLint, GLuint*);
+GLAPI PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv;
+#define glGetUniformuiv glad_glGetUniformuiv
+typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC)(GLuint, GLuint, const GLchar*);
+GLAPI PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation;
+#define glBindFragDataLocation glad_glBindFragDataLocation
+typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC)(GLuint, const GLchar*);
+GLAPI PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation;
+#define glGetFragDataLocation glad_glGetFragDataLocation
+typedef void (APIENTRYP PFNGLUNIFORM1UIPROC)(GLint, GLuint);
+GLAPI PFNGLUNIFORM1UIPROC glad_glUniform1ui;
+#define glUniform1ui glad_glUniform1ui
+typedef void (APIENTRYP PFNGLUNIFORM2UIPROC)(GLint, GLuint, GLuint);
+GLAPI PFNGLUNIFORM2UIPROC glad_glUniform2ui;
+#define glUniform2ui glad_glUniform2ui
+typedef void (APIENTRYP PFNGLUNIFORM3UIPROC)(GLint, GLuint, GLuint, GLuint);
+GLAPI PFNGLUNIFORM3UIPROC glad_glUniform3ui;
+#define glUniform3ui glad_glUniform3ui
+typedef void (APIENTRYP PFNGLUNIFORM4UIPROC)(GLint, GLuint, GLuint, GLuint, GLuint);
+GLAPI PFNGLUNIFORM4UIPROC glad_glUniform4ui;
+#define glUniform4ui glad_glUniform4ui
+typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC)(GLint, GLsizei, const GLuint*);
+GLAPI PFNGLUNIFORM1UIVPROC glad_glUniform1uiv;
+#define glUniform1uiv glad_glUniform1uiv
+typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC)(GLint, GLsizei, const GLuint*);
+GLAPI PFNGLUNIFORM2UIVPROC glad_glUniform2uiv;
+#define glUniform2uiv glad_glUniform2uiv
+typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC)(GLint, GLsizei, const GLuint*);
+GLAPI PFNGLUNIFORM3UIVPROC glad_glUniform3uiv;
+#define glUniform3uiv glad_glUniform3uiv
+typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC)(GLint, GLsizei, const GLuint*);
+GLAPI PFNGLUNIFORM4UIVPROC glad_glUniform4uiv;
+#define glUniform4uiv glad_glUniform4uiv
+typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC)(GLenum, GLenum, const GLint*);
+GLAPI PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv;
+#define glTexParameterIiv glad_glTexParameterIiv
+typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC)(GLenum, GLenum, const GLuint*);
+GLAPI PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv;
+#define glTexParameterIuiv glad_glTexParameterIuiv
+typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC)(GLenum, GLenum, GLint*);
+GLAPI PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv;
+#define glGetTexParameterIiv glad_glGetTexParameterIiv
+typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC)(GLenum, GLenum, GLuint*);
+GLAPI PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv;
+#define glGetTexParameterIuiv glad_glGetTexParameterIuiv
+typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC)(GLenum, GLint, const GLint*);
+GLAPI PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv;
+#define glClearBufferiv glad_glClearBufferiv
+typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC)(GLenum, GLint, const GLuint*);
+GLAPI PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv;
+#define glClearBufferuiv glad_glClearBufferuiv
+typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC)(GLenum, GLint, const GLfloat*);
+GLAPI PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv;
+#define glClearBufferfv glad_glClearBufferfv
+typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC)(GLenum, GLint, GLfloat, GLint);
+GLAPI PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi;
+#define glClearBufferfi glad_glClearBufferfi
+typedef const GLubyte* (APIENTRYP PFNGLGETSTRINGIPROC)(GLenum, GLuint);
+GLAPI PFNGLGETSTRINGIPROC glad_glGetStringi;
+#define glGetStringi glad_glGetStringi
+typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint);
+GLAPI PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer;
+#define glIsRenderbuffer glad_glIsRenderbuffer
+typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum, GLuint);
+GLAPI PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer;
+#define glBindRenderbuffer glad_glBindRenderbuffer
+typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei, const GLuint*);
+GLAPI PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers;
+#define glDeleteRenderbuffers glad_glDeleteRenderbuffers
+typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei, GLuint*);
+GLAPI PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers;
+#define glGenRenderbuffers glad_glGenRenderbuffers
+typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum, GLenum, GLsizei, GLsizei);
+GLAPI PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage;
+#define glRenderbufferStorage glad_glRenderbufferStorage
+typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum, GLenum, GLint*);
+GLAPI PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv;
+#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv
+typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint);
+GLAPI PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer;
+#define glIsFramebuffer glad_glIsFramebuffer
+typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum, GLuint);
+GLAPI PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer;
+#define glBindFramebuffer glad_glBindFramebuffer
+typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei, const GLuint*);
+GLAPI PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers;
+#define glDeleteFramebuffers glad_glDeleteFramebuffers
+typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei, GLuint*);
+GLAPI PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers;
+#define glGenFramebuffers glad_glGenFramebuffers
+typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum);
+GLAPI PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus;
+#define glCheckFramebufferStatus glad_glCheckFramebufferStatus
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum, GLenum, GLenum, GLuint, GLint);
+GLAPI PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D;
+#define glFramebufferTexture1D glad_glFramebufferTexture1D
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum, GLenum, GLenum, GLuint, GLint);
+GLAPI PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D;
+#define glFramebufferTexture2D glad_glFramebufferTexture2D
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum, GLenum, GLenum, GLuint, GLint, GLint);
+GLAPI PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D;
+#define glFramebufferTexture3D glad_glFramebufferTexture3D
+typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum, GLenum, GLenum, GLuint);
+GLAPI PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer;
+#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer
+typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum, GLenum, GLenum, GLint*);
+GLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv;
+#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv
+typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum);
+GLAPI PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap;
+#define glGenerateMipmap glad_glGenerateMipmap
+typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC)(GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum);
+GLAPI PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer;
+#define glBlitFramebuffer glad_glBlitFramebuffer
+typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei);
+GLAPI PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample;
+#define glRenderbufferStorageMultisample glad_glRenderbufferStorageMultisample
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum, GLenum, GLuint, GLint, GLint);
+GLAPI PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer;
+#define glFramebufferTextureLayer glad_glFramebufferTextureLayer
+typedef void* (APIENTRYP PFNGLMAPBUFFERRANGEPROC)(GLenum, GLintptr, GLsizeiptr, GLbitfield);
+GLAPI PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange;
+#define glMapBufferRange glad_glMapBufferRange
+typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum, GLintptr, GLsizeiptr);
+GLAPI PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange;
+#define glFlushMappedBufferRange glad_glFlushMappedBufferRange
+typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC)(GLuint);
+GLAPI PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray;
+#define glBindVertexArray glad_glBindVertexArray
+typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC)(GLsizei, const GLuint*);
+GLAPI PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays;
+#define glDeleteVertexArrays glad_glDeleteVertexArrays
+typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei, GLuint*);
+GLAPI PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays;
+#define glGenVertexArrays glad_glGenVertexArrays
+typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC)(GLuint);
+GLAPI PFNGLISVERTEXARRAYPROC glad_glIsVertexArray;
+#define glIsVertexArray glad_glIsVertexArray
+#endif
+#ifndef GL_VERSION_3_1
+#define GL_VERSION_3_1 1
+GLAPI int GLAD_GL_VERSION_3_1;
+typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum, GLint, GLsizei, GLsizei);
+GLAPI PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced;
+#define glDrawArraysInstanced glad_glDrawArraysInstanced
+typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum, GLsizei, GLenum, const void*, GLsizei);
+GLAPI PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced;
+#define glDrawElementsInstanced glad_glDrawElementsInstanced
+typedef void (APIENTRYP PFNGLTEXBUFFERPROC)(GLenum, GLenum, GLuint);
+GLAPI PFNGLTEXBUFFERPROC glad_glTexBuffer;
+#define glTexBuffer glad_glTexBuffer
+typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint);
+GLAPI PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex;
+#define glPrimitiveRestartIndex glad_glPrimitiveRestartIndex
+typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC)(GLenum, GLenum, GLintptr, GLintptr, GLsizeiptr);
+GLAPI PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData;
+#define glCopyBufferSubData glad_glCopyBufferSubData
+typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC)(GLuint, GLsizei, const GLchar**, GLuint*);
+GLAPI PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices;
+#define glGetUniformIndices glad_glGetUniformIndices
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint, GLsizei, const GLuint*, GLenum, GLint*);
+GLAPI PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv;
+#define glGetActiveUniformsiv glad_glGetActiveUniformsiv
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLchar*);
+GLAPI PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName;
+#define glGetActiveUniformName glad_glGetActiveUniformName
+typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint, const GLchar*);
+GLAPI PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex;
+#define glGetUniformBlockIndex glad_glGetUniformBlockIndex
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint, GLuint, GLenum, GLint*);
+GLAPI PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv;
+#define glGetActiveUniformBlockiv glad_glGetActiveUniformBlockiv
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLchar*);
+GLAPI PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName;
+#define glGetActiveUniformBlockName glad_glGetActiveUniformBlockName
+typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint, GLuint, GLuint);
+GLAPI PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding;
+#define glUniformBlockBinding glad_glUniformBlockBinding
+#endif
+#ifndef GL_VERSION_3_2
+#define GL_VERSION_3_2 1
+GLAPI int GLAD_GL_VERSION_3_2;
+typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum, GLsizei, GLenum, const void*, GLint);
+GLAPI PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex;
+#define glDrawElementsBaseVertex glad_glDrawElementsBaseVertex
+typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum, GLuint, GLuint, GLsizei, GLenum, const void*, GLint);
+GLAPI PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex;
+#define glDrawRangeElementsBaseVertex glad_glDrawRangeElementsBaseVertex
+typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum, GLsizei, GLenum, const void*, GLsizei, GLint);
+GLAPI PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex;
+#define glDrawElementsInstancedBaseVertex glad_glDrawElementsInstancedBaseVertex
+typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum, const GLsizei*, GLenum, const void**, GLsizei, const GLint*);
+GLAPI PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex;
+#define glMultiDrawElementsBaseVertex glad_glMultiDrawElementsBaseVertex
+typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC)(GLenum);
+GLAPI PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex;
+#define glProvokingVertex glad_glProvokingVertex
+typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC)(GLenum, GLbitfield);
+GLAPI PFNGLFENCESYNCPROC glad_glFenceSync;
+#define glFenceSync glad_glFenceSync
+typedef GLboolean (APIENTRYP PFNGLISSYNCPROC)(GLsync);
+GLAPI PFNGLISSYNCPROC glad_glIsSync;
+#define glIsSync glad_glIsSync
+typedef void (APIENTRYP PFNGLDELETESYNCPROC)(GLsync);
+GLAPI PFNGLDELETESYNCPROC glad_glDeleteSync;
+#define glDeleteSync glad_glDeleteSync
+typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC)(GLsync, GLbitfield, GLuint64);
+GLAPI PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync;
+#define glClientWaitSync glad_glClientWaitSync
+typedef void (APIENTRYP PFNGLWAITSYNCPROC)(GLsync, GLbitfield, GLuint64);
+GLAPI PFNGLWAITSYNCPROC glad_glWaitSync;
+#define glWaitSync glad_glWaitSync
+typedef void (APIENTRYP PFNGLGETINTEGER64VPROC)(GLenum, GLint64*);
+GLAPI PFNGLGETINTEGER64VPROC glad_glGetInteger64v;
+#define glGetInteger64v glad_glGetInteger64v
+typedef void (APIENTRYP PFNGLGETSYNCIVPROC)(GLsync, GLenum, GLsizei, GLsizei*, GLint*);
+GLAPI PFNGLGETSYNCIVPROC glad_glGetSynciv;
+#define glGetSynciv glad_glGetSynciv
+typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC)(GLenum, GLuint, GLint64*);
+GLAPI PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v;
+#define glGetInteger64i_v glad_glGetInteger64i_v
+typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum, GLenum, GLint64*);
+GLAPI PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v;
+#define glGetBufferParameteri64v glad_glGetBufferParameteri64v
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum, GLenum, GLuint, GLint);
+GLAPI PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture;
+#define glFramebufferTexture glad_glFramebufferTexture
+typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLboolean);
+GLAPI PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample;
+#define glTexImage2DMultisample glad_glTexImage2DMultisample
+typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei, GLboolean);
+GLAPI PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample;
+#define glTexImage3DMultisample glad_glTexImage3DMultisample
+typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC)(GLenum, GLuint, GLfloat*);
+GLAPI PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv;
+#define glGetMultisamplefv glad_glGetMultisamplefv
+typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC)(GLuint, GLbitfield);
+GLAPI PFNGLSAMPLEMASKIPROC glad_glSampleMaski;
+#define glSampleMaski glad_glSampleMaski
+#endif
+#ifndef GL_VERSION_3_3
+#define GL_VERSION_3_3 1
+GLAPI int GLAD_GL_VERSION_3_3;
+typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint, GLuint, GLuint, const GLchar*);
+GLAPI PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed;
+#define glBindFragDataLocationIndexed glad_glBindFragDataLocationIndexed
+typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC)(GLuint, const GLchar*);
+GLAPI PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex;
+#define glGetFragDataIndex glad_glGetFragDataIndex
+typedef void (APIENTRYP PFNGLGENSAMPLERSPROC)(GLsizei, GLuint*);
+GLAPI PFNGLGENSAMPLERSPROC glad_glGenSamplers;
+#define glGenSamplers glad_glGenSamplers
+typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC)(GLsizei, const GLuint*);
+GLAPI PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers;
+#define glDeleteSamplers glad_glDeleteSamplers
+typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC)(GLuint);
+GLAPI PFNGLISSAMPLERPROC glad_glIsSampler;
+#define glIsSampler glad_glIsSampler
+typedef void (APIENTRYP PFNGLBINDSAMPLERPROC)(GLuint, GLuint);
+GLAPI PFNGLBINDSAMPLERPROC glad_glBindSampler;
+#define glBindSampler glad_glBindSampler
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC)(GLuint, GLenum, GLint);
+GLAPI PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri;
+#define glSamplerParameteri glad_glSamplerParameteri
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC)(GLuint, GLenum, const GLint*);
+GLAPI PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv;
+#define glSamplerParameteriv glad_glSamplerParameteriv
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC)(GLuint, GLenum, GLfloat);
+GLAPI PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf;
+#define glSamplerParameterf glad_glSamplerParameterf
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC)(GLuint, GLenum, const GLfloat*);
+GLAPI PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv;
+#define glSamplerParameterfv glad_glSamplerParameterfv
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC)(GLuint, GLenum, const GLint*);
+GLAPI PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv;
+#define glSamplerParameterIiv glad_glSamplerParameterIiv
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint, GLenum, const GLuint*);
+GLAPI PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv;
+#define glSamplerParameterIuiv glad_glSamplerParameterIuiv
+typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint, GLenum, GLint*);
+GLAPI PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv;
+#define glGetSamplerParameteriv glad_glGetSamplerParameteriv
+typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint, GLenum, GLint*);
+GLAPI PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv;
+#define glGetSamplerParameterIiv glad_glGetSamplerParameterIiv
+typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint, GLenum, GLfloat*);
+GLAPI PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv;
+#define glGetSamplerParameterfv glad_glGetSamplerParameterfv
+typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint, GLenum, GLuint*);
+GLAPI PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv;
+#define glGetSamplerParameterIuiv glad_glGetSamplerParameterIuiv
+typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC)(GLuint, GLenum);
+GLAPI PFNGLQUERYCOUNTERPROC glad_glQueryCounter;
+#define glQueryCounter glad_glQueryCounter
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC)(GLuint, GLenum, GLint64*);
+GLAPI PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v;
+#define glGetQueryObjecti64v glad_glGetQueryObjecti64v
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC)(GLuint, GLenum, GLuint64*);
+GLAPI PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v;
+#define glGetQueryObjectui64v glad_glGetQueryObjectui64v
+typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC)(GLuint, GLuint);
+GLAPI PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor;
+#define glVertexAttribDivisor glad_glVertexAttribDivisor
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC)(GLuint, GLenum, GLboolean, GLuint);
+GLAPI PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui;
+#define glVertexAttribP1ui glad_glVertexAttribP1ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC)(GLuint, GLenum, GLboolean, const GLuint*);
+GLAPI PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv;
+#define glVertexAttribP1uiv glad_glVertexAttribP1uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC)(GLuint, GLenum, GLboolean, GLuint);
+GLAPI PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui;
+#define glVertexAttribP2ui glad_glVertexAttribP2ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC)(GLuint, GLenum, GLboolean, const GLuint*);
+GLAPI PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv;
+#define glVertexAttribP2uiv glad_glVertexAttribP2uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC)(GLuint, GLenum, GLboolean, GLuint);
+GLAPI PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui;
+#define glVertexAttribP3ui glad_glVertexAttribP3ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC)(GLuint, GLenum, GLboolean, const GLuint*);
+GLAPI PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv;
+#define glVertexAttribP3uiv glad_glVertexAttribP3uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC)(GLuint, GLenum, GLboolean, GLuint);
+GLAPI PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui;
+#define glVertexAttribP4ui glad_glVertexAttribP4ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC)(GLuint, GLenum, GLboolean, const GLuint*);
+GLAPI PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv;
+#define glVertexAttribP4uiv glad_glVertexAttribP4uiv
+typedef void (APIENTRYP PFNGLVERTEXP2UIPROC)(GLenum, GLuint);
+GLAPI PFNGLVERTEXP2UIPROC glad_glVertexP2ui;
+#define glVertexP2ui glad_glVertexP2ui
+typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC)(GLenum, const GLuint*);
+GLAPI PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv;
+#define glVertexP2uiv glad_glVertexP2uiv
+typedef void (APIENTRYP PFNGLVERTEXP3UIPROC)(GLenum, GLuint);
+GLAPI PFNGLVERTEXP3UIPROC glad_glVertexP3ui;
+#define glVertexP3ui glad_glVertexP3ui
+typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC)(GLenum, const GLuint*);
+GLAPI PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv;
+#define glVertexP3uiv glad_glVertexP3uiv
+typedef void (APIENTRYP PFNGLVERTEXP4UIPROC)(GLenum, GLuint);
+GLAPI PFNGLVERTEXP4UIPROC glad_glVertexP4ui;
+#define glVertexP4ui glad_glVertexP4ui
+typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC)(GLenum, const GLuint*);
+GLAPI PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv;
+#define glVertexP4uiv glad_glVertexP4uiv
+typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC)(GLenum, GLuint);
+GLAPI PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui;
+#define glTexCoordP1ui glad_glTexCoordP1ui
+typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC)(GLenum, const GLuint*);
+GLAPI PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv;
+#define glTexCoordP1uiv glad_glTexCoordP1uiv
+typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC)(GLenum, GLuint);
+GLAPI PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui;
+#define glTexCoordP2ui glad_glTexCoordP2ui
+typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC)(GLenum, const GLuint*);
+GLAPI PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv;
+#define glTexCoordP2uiv glad_glTexCoordP2uiv
+typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC)(GLenum, GLuint);
+GLAPI PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui;
+#define glTexCoordP3ui glad_glTexCoordP3ui
+typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC)(GLenum, const GLuint*);
+GLAPI PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv;
+#define glTexCoordP3uiv glad_glTexCoordP3uiv
+typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC)(GLenum, GLuint);
+GLAPI PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui;
+#define glTexCoordP4ui glad_glTexCoordP4ui
+typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC)(GLenum, const GLuint*);
+GLAPI PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv;
+#define glTexCoordP4uiv glad_glTexCoordP4uiv
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC)(GLenum, GLenum, GLuint);
+GLAPI PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui;
+#define glMultiTexCoordP1ui glad_glMultiTexCoordP1ui
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC)(GLenum, GLenum, const GLuint*);
+GLAPI PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv;
+#define glMultiTexCoordP1uiv glad_glMultiTexCoordP1uiv
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC)(GLenum, GLenum, GLuint);
+GLAPI PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui;
+#define glMultiTexCoordP2ui glad_glMultiTexCoordP2ui
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC)(GLenum, GLenum, const GLuint*);
+GLAPI PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv;
+#define glMultiTexCoordP2uiv glad_glMultiTexCoordP2uiv
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC)(GLenum, GLenum, GLuint);
+GLAPI PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui;
+#define glMultiTexCoordP3ui glad_glMultiTexCoordP3ui
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC)(GLenum, GLenum, const GLuint*);
+GLAPI PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv;
+#define glMultiTexCoordP3uiv glad_glMultiTexCoordP3uiv
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC)(GLenum, GLenum, GLuint);
+GLAPI PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui;
+#define glMultiTexCoordP4ui glad_glMultiTexCoordP4ui
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC)(GLenum, GLenum, const GLuint*);
+GLAPI PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv;
+#define glMultiTexCoordP4uiv glad_glMultiTexCoordP4uiv
+typedef void (APIENTRYP PFNGLNORMALP3UIPROC)(GLenum, GLuint);
+GLAPI PFNGLNORMALP3UIPROC glad_glNormalP3ui;
+#define glNormalP3ui glad_glNormalP3ui
+typedef void (APIENTRYP PFNGLNORMALP3UIVPROC)(GLenum, const GLuint*);
+GLAPI PFNGLNORMALP3UIVPROC glad_glNormalP3uiv;
+#define glNormalP3uiv glad_glNormalP3uiv
+typedef void (APIENTRYP PFNGLCOLORP3UIPROC)(GLenum, GLuint);
+GLAPI PFNGLCOLORP3UIPROC glad_glColorP3ui;
+#define glColorP3ui glad_glColorP3ui
+typedef void (APIENTRYP PFNGLCOLORP3UIVPROC)(GLenum, const GLuint*);
+GLAPI PFNGLCOLORP3UIVPROC glad_glColorP3uiv;
+#define glColorP3uiv glad_glColorP3uiv
+typedef void (APIENTRYP PFNGLCOLORP4UIPROC)(GLenum, GLuint);
+GLAPI PFNGLCOLORP4UIPROC glad_glColorP4ui;
+#define glColorP4ui glad_glColorP4ui
+typedef void (APIENTRYP PFNGLCOLORP4UIVPROC)(GLenum, const GLuint*);
+GLAPI PFNGLCOLORP4UIVPROC glad_glColorP4uiv;
+#define glColorP4uiv glad_glColorP4uiv
+typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC)(GLenum, GLuint);
+GLAPI PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui;
+#define glSecondaryColorP3ui glad_glSecondaryColorP3ui
+typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC)(GLenum, const GLuint*);
+GLAPI PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv;
+#define glSecondaryColorP3uiv glad_glSecondaryColorP3uiv
+#endif
+#ifndef GL_ES_VERSION_2_0
+#define GL_ES_VERSION_2_0 1
+GLAPI int GLAD_GL_ES_VERSION_2_0;
+typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC)(GLfloat);
+GLAPI PFNGLCLEARDEPTHFPROC glad_glClearDepthf;
+#define glClearDepthf glad_glClearDepthf
+typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC)(GLfloat, GLfloat);
+GLAPI PFNGLDEPTHRANGEFPROC glad_glDepthRangef;
+#define glDepthRangef glad_glDepthRangef
+typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum, GLenum, GLint*, GLint*);
+GLAPI PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat;
+#define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat
+typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC)();
+GLAPI PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler;
+#define glReleaseShaderCompiler glad_glReleaseShaderCompiler
+typedef void (APIENTRYP PFNGLSHADERBINARYPROC)(GLsizei, const GLuint*, GLenum, const void*, GLsizei);
+GLAPI PFNGLSHADERBINARYPROC glad_glShaderBinary;
+#define glShaderBinary glad_glShaderBinary
+#endif
+#ifndef GL_ES_VERSION_3_0
+#define GL_ES_VERSION_3_0 1
+GLAPI int GLAD_GL_ES_VERSION_3_0;
+typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC)(GLenum, GLuint);
+GLAPI PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback;
+#define glBindTransformFeedback glad_glBindTransformFeedback
+typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC)(GLsizei, const GLuint*);
+GLAPI PFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks;
+#define glDeleteTransformFeedbacks glad_glDeleteTransformFeedbacks
+typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC)(GLsizei, GLuint*);
+GLAPI PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks;
+#define glGenTransformFeedbacks glad_glGenTransformFeedbacks
+typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC)(GLuint);
+GLAPI PFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback;
+#define glIsTransformFeedback glad_glIsTransformFeedback
+typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC)();
+GLAPI PFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback;
+#define glPauseTransformFeedback glad_glPauseTransformFeedback
+typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC)();
+GLAPI PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback;
+#define glResumeTransformFeedback glad_glResumeTransformFeedback
+typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC)(GLuint, GLsizei, GLsizei*, GLenum*, void*);
+GLAPI PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary;
+#define glGetProgramBinary glad_glGetProgramBinary
+typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC)(GLuint, GLenum, const void*, GLsizei);
+GLAPI PFNGLPROGRAMBINARYPROC glad_glProgramBinary;
+#define glProgramBinary glad_glProgramBinary
+typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC)(GLuint, GLenum, GLint);
+GLAPI PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri;
+#define glProgramParameteri glad_glProgramParameteri
+typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC)(GLenum, GLsizei, const GLenum*);
+GLAPI PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer;
+#define glInvalidateFramebuffer glad_glInvalidateFramebuffer
+typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC)(GLenum, GLsizei, const GLenum*, GLint, GLint, GLsizei, GLsizei);
+GLAPI PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer;
+#define glInvalidateSubFramebuffer glad_glInvalidateSubFramebuffer
+typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei);
+GLAPI PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D;
+#define glTexStorage2D glad_glTexStorage2D
+typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei);
+GLAPI PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D;
+#define glTexStorage3D glad_glTexStorage3D
+typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC)(GLenum, GLenum, GLenum, GLsizei, GLint*);
+GLAPI PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ;
+#define glGetInternalformativ glad_glGetInternalformativ
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <citraimport\glad\include\glad/glad.h>
+
+static void* get_proc(const char *namez);
+
+#ifdef _WIN32
+#include <windows.h>
+static HMODULE libGL;
+
+typedef void* (APIENTRYP PFNWGLGETPROCADDRESSPROC_PRIVATE)(const char*);
+PFNWGLGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr;
+
+static
+int open_gl(void) {
+ libGL = LoadLibraryA("opengl32.dll");
+ if(libGL != NULL) {
+ gladGetProcAddressPtr = (PFNWGLGETPROCADDRESSPROC_PRIVATE)GetProcAddress(
+ libGL, "wglGetProcAddress");
+ return gladGetProcAddressPtr != NULL;
+ }
+
+ return 0;
+}
+
+static
+void close_gl(void) {
+ if(libGL != NULL) {
+ FreeLibrary(libGL);
+ libGL = NULL;
+ }
+}
+#else
+#include <dlfcn.h>
+static void* libGL;
+
+#ifndef __APPLE__
+typedef void* (APIENTRYP PFNGLXGETPROCADDRESSPROC_PRIVATE)(const char*);
+PFNGLXGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr;
+#endif
+
+static
+int open_gl(void) {
+#ifdef __APPLE__
+ static const char *NAMES[] = {
+ "../Frameworks/OpenGL.framework/OpenGL",
+ "/Library/Frameworks/OpenGL.framework/OpenGL",
+ "/System/Library/Frameworks/OpenGL.framework/OpenGL",
+ "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL"
+ };
+#else
+ static const char *NAMES[] = {"libGL.so.1", "libGL.so"};
+#endif
+
+ unsigned int index = 0;
+ for(index = 0; index < (sizeof(NAMES) / sizeof(NAMES[0])); index++) {
+ libGL = dlopen(NAMES[index], RTLD_NOW | RTLD_GLOBAL);
+
+ if(libGL != NULL) {
+#ifdef __APPLE__
+ return 1;
+#else
+ gladGetProcAddressPtr = (PFNGLXGETPROCADDRESSPROC_PRIVATE)dlsym(libGL,
+ "glXGetProcAddressARB");
+ return gladGetProcAddressPtr != NULL;
+#endif
+ }
+ }
+
+ return 0;
+}
+
+static
+void close_gl() {
+ if(libGL != NULL) {
+ dlclose(libGL);
+ libGL = NULL;
+ }
+}
+#endif
+
+static
+void* get_proc(const char *namez) {
+ void* result = NULL;
+ if(libGL == NULL) return NULL;
+
+#ifndef __APPLE__
+ if(gladGetProcAddressPtr != NULL) {
+ result = gladGetProcAddressPtr(namez);
+ }
+#endif
+ if(result == NULL) {
+#ifdef _WIN32
+ result = (void*)GetProcAddress(libGL, namez);
+#else
+ result = dlsym(libGL, namez);
+#endif
+ }
+
+ return result;
+}
+
+int gladLoadGL(void) {
+ int status = 0;
+
+ if(open_gl()) {
+ status = gladLoadGLLoader(&get_proc);
+ close_gl();
+ }
+
+ return status;
+}
+
+struct gladGLversionStruct GLVersion;
+
+#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0)
+#define _GLAD_IS_SOME_NEW_VERSION 1
+#endif
+
+static int max_loaded_major;
+static int max_loaded_minor;
+
+static const char *exts = NULL;
+static int num_exts_i = 0;
+static const char **exts_i = NULL;
+
+static void get_exts(void) {
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+ if(max_loaded_major < 3) {
+#endif
+ exts = (const char *)glGetString(GL_EXTENSIONS);
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+ } else {
+ int index;
+
+ num_exts_i = 0;
+ glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts_i);
+ if (num_exts_i > 0) {
+ exts_i = (const char **)realloc((void *)exts_i, num_exts_i * sizeof *exts_i);
+ }
+
+ for(index = 0; index < num_exts_i; index++) {
+ exts_i[index] = (const char*)glGetStringi(GL_EXTENSIONS, index);
+ }
+ }
+#endif
+}
+
+static int has_ext(const char *ext) {
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+ if(max_loaded_major < 3) {
+#endif
+ const char *extensions;
+ const char *loc;
+ const char *terminator;
+ extensions = exts;
+ if(extensions == NULL || ext == NULL) {
+ return 0;
+ }
+
+ while(1) {
+ loc = strstr(extensions, ext);
+ if(loc == NULL) {
+ return 0;
+ }
+
+ terminator = loc + strlen(ext);
+ if((loc == extensions || *(loc - 1) == ' ') &&
+ (*terminator == ' ' || *terminator == '\0')) {
+ return 1;
+ }
+ extensions = terminator;
+ }
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+ } else {
+ int index;
+
+ for(index = 0; index < num_exts_i; index++) {
+ const char *e = exts_i[index];
+
+ if(strcmp(e, ext) == 0) {
+ return 1;
+ }
+ }
+ }
+#endif
+
+ return 0;
+}
+int GLAD_GL_VERSION_1_0;
+int GLAD_GL_VERSION_1_1;
+int GLAD_GL_VERSION_1_2;
+int GLAD_GL_VERSION_1_3;
+int GLAD_GL_VERSION_1_4;
+int GLAD_GL_VERSION_1_5;
+int GLAD_GL_VERSION_2_0;
+int GLAD_GL_VERSION_2_1;
+int GLAD_GL_VERSION_3_0;
+int GLAD_GL_VERSION_3_1;
+int GLAD_GL_VERSION_3_2;
+int GLAD_GL_VERSION_3_3;
+int GLAD_GL_ES_VERSION_2_0;
+int GLAD_GL_ES_VERSION_3_0;
+PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D;
+PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui;
+PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate;
+PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer;
+PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D;
+PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv;
+PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv;
+PFNGLBINDSAMPLERPROC glad_glBindSampler;
+PFNGLLINEWIDTHPROC glad_glLineWidth;
+PFNGLCOLORP3UIVPROC glad_glColorP3uiv;
+PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v;
+PFNGLCOMPILESHADERPROC glad_glCompileShader;
+PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying;
+PFNGLDEPTHRANGEFPROC glad_glDepthRangef;
+PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer;
+PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui;
+PFNGLVERTEXP4UIPROC glad_glVertexP4ui;
+PFNGLENABLEIPROC glad_glEnablei;
+PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui;
+PFNGLCREATESHADERPROC glad_glCreateShader;
+PFNGLISBUFFERPROC glad_glIsBuffer;
+PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv;
+PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers;
+PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D;
+PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D;
+PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f;
+PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate;
+PFNGLHINTPROC glad_glHint;
+PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s;
+PFNGLSAMPLEMASKIPROC glad_glSampleMaski;
+PFNGLVERTEXP2UIPROC glad_glVertexP2ui;
+PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv;
+PFNGLPOINTSIZEPROC glad_glPointSize;
+PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv;
+PFNGLDELETEPROGRAMPROC glad_glDeleteProgram;
+PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv;
+PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage;
+PFNGLWAITSYNCPROC glad_glWaitSync;
+PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv;
+PFNGLUNIFORM3IPROC glad_glUniform3i;
+PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv;
+PFNGLUNIFORM3FPROC glad_glUniform3f;
+PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv;
+PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv;
+PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui;
+PFNGLCOLORMASKIPROC glad_glColorMaski;
+PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi;
+PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays;
+PFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback;
+PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui;
+PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv;
+PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex;
+PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D;
+PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv;
+PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv;
+PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback;
+PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui;
+PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers;
+PFNGLDRAWARRAYSPROC glad_glDrawArrays;
+PFNGLUNIFORM1UIPROC glad_glUniform1ui;
+PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i;
+PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui;
+PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d;
+PFNGLCLEARPROC glad_glClear;
+PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri;
+PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName;
+PFNGLISENABLEDPROC glad_glIsEnabled;
+PFNGLSTENCILOPPROC glad_glStencilOp;
+PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D;
+PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv;
+PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub;
+PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation;
+PFNGLTEXIMAGE1DPROC glad_glTexImage1D;
+PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv;
+PFNGLGETTEXIMAGEPROC glad_glGetTexImage;
+PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v;
+PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers;
+PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders;
+PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer;
+PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays;
+PFNGLISVERTEXARRAYPROC glad_glIsVertexArray;
+PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray;
+PFNGLGETQUERYIVPROC glad_glGetQueryiv;
+PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv;
+PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices;
+PFNGLISSHADERPROC glad_glIsShader;
+PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv;
+PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv;
+PFNGLENABLEPROC glad_glEnable;
+PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv;
+PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation;
+PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv;
+PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv;
+PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui;
+PFNGLGETUNIFORMFVPROC glad_glGetUniformfv;
+PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv;
+PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv;
+PFNGLDRAWBUFFERPROC glad_glDrawBuffer;
+PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv;
+PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced;
+PFNGLFLUSHPROC glad_glFlush;
+PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv;
+PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv;
+PFNGLFENCESYNCPROC glad_glFenceSync;
+PFNGLCOLORP3UIPROC glad_glColorP3ui;
+PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv;
+PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender;
+PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv;
+PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv;
+PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate;
+PFNGLGENSAMPLERSPROC glad_glGenSamplers;
+PFNGLCLAMPCOLORPROC glad_glClampColor;
+PFNGLUNIFORM4IVPROC glad_glUniform4iv;
+PFNGLCLEARSTENCILPROC glad_glClearStencil;
+PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv;
+PFNGLGENTEXTURESPROC glad_glGenTextures;
+PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv;
+PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv;
+PFNGLISSYNCPROC glad_glIsSync;
+PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName;
+PFNGLUNIFORM2IPROC glad_glUniform2i;
+PFNGLUNIFORM2FPROC glad_glUniform2f;
+PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui;
+PFNGLGETPROGRAMIVPROC glad_glGetProgramiv;
+PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer;
+PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer;
+PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange;
+PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D;
+PFNGLGENQUERIESPROC glad_glGenQueries;
+PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui;
+PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D;
+PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v;
+PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers;
+PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D;
+PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer;
+PFNGLISENABLEDIPROC glad_glIsEnabledi;
+PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui;
+PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed;
+PFNGLUNIFORM2IVPROC glad_glUniform2iv;
+PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv;
+PFNGLUNIFORM4UIVPROC glad_glUniform4uiv;
+PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D;
+PFNGLGETSHADERIVPROC glad_glGetShaderiv;
+PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer;
+PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation;
+PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset;
+PFNGLGETDOUBLEVPROC glad_glGetDoublev;
+PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d;
+PFNGLGETUNIFORMIVPROC glad_glGetUniformiv;
+PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv;
+PFNGLUNIFORM3FVPROC glad_glUniform3fv;
+PFNGLDEPTHRANGEPROC glad_glDepthRange;
+PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer;
+PFNGLMAPBUFFERPROC glad_glMapBuffer;
+PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D;
+PFNGLDELETESYNCPROC glad_glDeleteSync;
+PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D;
+PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv;
+PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements;
+PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv;
+PFNGLUNIFORM3IVPROC glad_glUniform3iv;
+PFNGLPOLYGONMODEPROC glad_glPolygonMode;
+PFNGLDRAWBUFFERSPROC glad_glDrawBuffers;
+PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv;
+PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary;
+PFNGLUSEPROGRAMPROC glad_glUseProgram;
+PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog;
+PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback;
+PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray;
+PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers;
+PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv;
+PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex;
+PFNGLUNIFORM2UIVPROC glad_glUniform2uiv;
+PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D;
+PFNGLFINISHPROC glad_glFinish;
+PFNGLDELETESHADERPROC glad_glDeleteShader;
+PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv;
+PFNGLVIEWPORTPROC glad_glViewport;
+PFNGLUNIFORM1UIVPROC glad_glUniform1uiv;
+PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings;
+PFNGLUNIFORM2UIPROC glad_glUniform2ui;
+PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i;
+PFNGLCLEARDEPTHPROC glad_glClearDepth;
+PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv;
+PFNGLTEXPARAMETERFPROC glad_glTexParameterf;
+PFNGLTEXPARAMETERIPROC glad_glTexParameteri;
+PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource;
+PFNGLTEXBUFFERPROC glad_glTexBuffer;
+PFNGLPIXELSTOREIPROC glad_glPixelStorei;
+PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram;
+PFNGLPIXELSTOREFPROC glad_glPixelStoref;
+PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v;
+PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv;
+PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ;
+PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv;
+PFNGLLINKPROGRAMPROC glad_glLinkProgram;
+PFNGLBINDTEXTUREPROC glad_glBindTexture;
+PFNGLGETSTRINGPROC glad_glGetString;
+PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv;
+PFNGLDETACHSHADERPROC glad_glDetachShader;
+PFNGLENDQUERYPROC glad_glEndQuery;
+PFNGLNORMALP3UIPROC glad_glNormalP3ui;
+PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui;
+PFNGLDELETETEXTURESPROC glad_glDeleteTextures;
+PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate;
+PFNGLDELETEQUERIESPROC glad_glDeleteQueries;
+PFNGLNORMALP3UIVPROC glad_glNormalP3uiv;
+PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f;
+PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d;
+PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv;
+PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s;
+PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex;
+PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage;
+PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri;
+PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf;
+PFNGLUNIFORM1FPROC glad_glUniform1f;
+PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv;
+PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage;
+PFNGLUNIFORM1IPROC glad_glUniform1i;
+PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib;
+PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D;
+PFNGLDISABLEPROC glad_glDisable;
+PFNGLLOGICOPPROC glad_glLogicOp;
+PFNGLUNIFORM4UIPROC glad_glUniform4ui;
+PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer;
+PFNGLCULLFACEPROC glad_glCullFace;
+PFNGLGETSTRINGIPROC glad_glGetStringi;
+PFNGLATTACHSHADERPROC glad_glAttachShader;
+PFNGLQUERYCOUNTERPROC glad_glQueryCounter;
+PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex;
+PFNGLSHADERBINARYPROC glad_glShaderBinary;
+PFNGLDRAWELEMENTSPROC glad_glDrawElements;
+PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv;
+PFNGLUNIFORM1IVPROC glad_glUniform1iv;
+PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv;
+PFNGLREADBUFFERPROC glad_glReadBuffer;
+PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv;
+PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced;
+PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap;
+PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv;
+PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f;
+PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv;
+PFNGLPOINTPARAMETERIPROC glad_glPointParameteri;
+PFNGLBLENDCOLORPROC glad_glBlendColor;
+PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv;
+PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer;
+PFNGLPOINTPARAMETERFPROC glad_glPointParameterf;
+PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s;
+PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer;
+PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv;
+PFNGLISPROGRAMPROC glad_glIsProgram;
+PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv;
+PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv;
+PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler;
+PFNGLUNIFORM4IPROC glad_glUniform4i;
+PFNGLACTIVETEXTUREPROC glad_glActiveTexture;
+PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray;
+PFNGLREADPIXELSPROC glad_glReadPixels;
+PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv;
+PFNGLUNIFORM4FPROC glad_glUniform4f;
+PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample;
+PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv;
+PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex;
+PFNGLSTENCILFUNCPROC glad_glStencilFunc;
+PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding;
+PFNGLCOLORP4UIPROC glad_glColorP4ui;
+PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv;
+PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog;
+PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i;
+PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData;
+PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate;
+PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui;
+PFNGLGENBUFFERSPROC glad_glGenBuffers;
+PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv;
+PFNGLBLENDFUNCPROC glad_glBlendFunc;
+PFNGLCREATEPROGRAMPROC glad_glCreateProgram;
+PFNGLTEXIMAGE3DPROC glad_glTexImage3D;
+PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer;
+PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex;
+PFNGLGETINTEGER64VPROC glad_glGetInteger64v;
+PFNGLSCISSORPROC glad_glScissor;
+PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv;
+PFNGLGETBOOLEANVPROC glad_glGetBooleanv;
+PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv;
+PFNGLUNIFORM3UIVPROC glad_glUniform3uiv;
+PFNGLCLEARCOLORPROC glad_glClearColor;
+PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv;
+PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv;
+PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v;
+PFNGLCOLORP4UIVPROC glad_glColorP4uiv;
+PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv;
+PFNGLUNIFORM3UIPROC glad_glUniform3ui;
+PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv;
+PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv;
+PFNGLUNIFORM2FVPROC glad_glUniform2fv;
+PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv;
+PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange;
+PFNGLCLEARDEPTHFPROC glad_glClearDepthf;
+PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv;
+PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks;
+PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv;
+PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv;
+PFNGLDEPTHFUNCPROC glad_glDepthFunc;
+PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D;
+PFNGLPROGRAMBINARYPROC glad_glProgramBinary;
+PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv;
+PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv;
+PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui;
+PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync;
+PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui;
+PFNGLCOLORMASKPROC glad_glColorMask;
+PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv;
+PFNGLBLENDEQUATIONPROC glad_glBlendEquation;
+PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation;
+PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback;
+PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv;
+PFNGLUNIFORM4FVPROC glad_glUniform4fv;
+PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback;
+PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv;
+PFNGLISSAMPLERPROC glad_glIsSampler;
+PFNGLVERTEXP3UIPROC glad_glVertexP3ui;
+PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor;
+PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D;
+PFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks;
+PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D;
+PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex;
+PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus;
+PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender;
+PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv;
+PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation;
+PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv;
+PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv;
+PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements;
+PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv;
+PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase;
+PFNGLBUFFERSUBDATAPROC glad_glBufferSubData;
+PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv;
+PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange;
+PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture;
+PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays;
+PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv;
+PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv;
+PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat;
+PFNGLDISABLEIPROC glad_glDisablei;
+PFNGLSHADERSOURCEPROC glad_glShaderSource;
+PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers;
+PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv;
+PFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback;
+PFNGLGETSYNCIVPROC glad_glGetSynciv;
+PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv;
+PFNGLBEGINQUERYPROC glad_glBeginQuery;
+PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv;
+PFNGLBINDBUFFERPROC glad_glBindBuffer;
+PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv;
+PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv;
+PFNGLBUFFERDATAPROC glad_glBufferData;
+PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv;
+PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui;
+PFNGLGETERRORPROC glad_glGetError;
+PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui;
+PFNGLGETFLOATVPROC glad_glGetFloatv;
+PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D;
+PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv;
+PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv;
+PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i;
+PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv;
+PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv;
+PFNGLGETINTEGERVPROC glad_glGetIntegerv;
+PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv;
+PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D;
+PFNGLISQUERYPROC glad_glIsQuery;
+PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv;
+PFNGLTEXIMAGE2DPROC glad_glTexImage2D;
+PFNGLSTENCILMASKPROC glad_glStencilMask;
+PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv;
+PFNGLISTEXTUREPROC glad_glIsTexture;
+PFNGLUNIFORM1FVPROC glad_glUniform1fv;
+PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv;
+PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv;
+PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv;
+PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData;
+PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv;
+PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d;
+PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f;
+PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv;
+PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v;
+PFNGLDEPTHMASKPROC glad_glDepthMask;
+PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s;
+PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample;
+PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex;
+PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample;
+PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform;
+PFNGLFRONTFACEPROC glad_glFrontFace;
+static void load_GL_VERSION_1_0(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_0) return;
+ glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace");
+ glad_glFrontFace = (PFNGLFRONTFACEPROC)load("glFrontFace");
+ glad_glHint = (PFNGLHINTPROC)load("glHint");
+ glad_glLineWidth = (PFNGLLINEWIDTHPROC)load("glLineWidth");
+ glad_glPointSize = (PFNGLPOINTSIZEPROC)load("glPointSize");
+ glad_glPolygonMode = (PFNGLPOLYGONMODEPROC)load("glPolygonMode");
+ glad_glScissor = (PFNGLSCISSORPROC)load("glScissor");
+ glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC)load("glTexParameterf");
+ glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)load("glTexParameterfv");
+ glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC)load("glTexParameteri");
+ glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)load("glTexParameteriv");
+ glad_glTexImage1D = (PFNGLTEXIMAGE1DPROC)load("glTexImage1D");
+ glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC)load("glTexImage2D");
+ glad_glDrawBuffer = (PFNGLDRAWBUFFERPROC)load("glDrawBuffer");
+ glad_glClear = (PFNGLCLEARPROC)load("glClear");
+ glad_glClearColor = (PFNGLCLEARCOLORPROC)load("glClearColor");
+ glad_glClearStencil = (PFNGLCLEARSTENCILPROC)load("glClearStencil");
+ glad_glClearDepth = (PFNGLCLEARDEPTHPROC)load("glClearDepth");
+ glad_glStencilMask = (PFNGLSTENCILMASKPROC)load("glStencilMask");
+ glad_glColorMask = (PFNGLCOLORMASKPROC)load("glColorMask");
+ glad_glDepthMask = (PFNGLDEPTHMASKPROC)load("glDepthMask");
+ glad_glDisable = (PFNGLDISABLEPROC)load("glDisable");
+ glad_glEnable = (PFNGLENABLEPROC)load("glEnable");
+ glad_glFinish = (PFNGLFINISHPROC)load("glFinish");
+ glad_glFlush = (PFNGLFLUSHPROC)load("glFlush");
+ glad_glBlendFunc = (PFNGLBLENDFUNCPROC)load("glBlendFunc");
+ glad_glLogicOp = (PFNGLLOGICOPPROC)load("glLogicOp");
+ glad_glStencilFunc = (PFNGLSTENCILFUNCPROC)load("glStencilFunc");
+ glad_glStencilOp = (PFNGLSTENCILOPPROC)load("glStencilOp");
+ glad_glDepthFunc = (PFNGLDEPTHFUNCPROC)load("glDepthFunc");
+ glad_glPixelStoref = (PFNGLPIXELSTOREFPROC)load("glPixelStoref");
+ glad_glPixelStorei = (PFNGLPIXELSTOREIPROC)load("glPixelStorei");
+ glad_glReadBuffer = (PFNGLREADBUFFERPROC)load("glReadBuffer");
+ glad_glReadPixels = (PFNGLREADPIXELSPROC)load("glReadPixels");
+ glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC)load("glGetBooleanv");
+ glad_glGetDoublev = (PFNGLGETDOUBLEVPROC)load("glGetDoublev");
+ glad_glGetError = (PFNGLGETERRORPROC)load("glGetError");
+ glad_glGetFloatv = (PFNGLGETFLOATVPROC)load("glGetFloatv");
+ glad_glGetIntegerv = (PFNGLGETINTEGERVPROC)load("glGetIntegerv");
+ glad_glGetString = (PFNGLGETSTRINGPROC)load("glGetString");
+ glad_glGetTexImage = (PFNGLGETTEXIMAGEPROC)load("glGetTexImage");
+ glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC)load("glGetTexParameterfv");
+ glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC)load("glGetTexParameteriv");
+ glad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC)load("glGetTexLevelParameterfv");
+ glad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC)load("glGetTexLevelParameteriv");
+ glad_glIsEnabled = (PFNGLISENABLEDPROC)load("glIsEnabled");
+ glad_glDepthRange = (PFNGLDEPTHRANGEPROC)load("glDepthRange");
+ glad_glViewport = (PFNGLVIEWPORTPROC)load("glViewport");
+}
+static void load_GL_VERSION_1_1(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_1) return;
+ glad_glDrawArrays = (PFNGLDRAWARRAYSPROC)load("glDrawArrays");
+ glad_glDrawElements = (PFNGLDRAWELEMENTSPROC)load("glDrawElements");
+ glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC)load("glPolygonOffset");
+ glad_glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC)load("glCopyTexImage1D");
+ glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC)load("glCopyTexImage2D");
+ glad_glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC)load("glCopyTexSubImage1D");
+ glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC)load("glCopyTexSubImage2D");
+ glad_glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC)load("glTexSubImage1D");
+ glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC)load("glTexSubImage2D");
+ glad_glBindTexture = (PFNGLBINDTEXTUREPROC)load("glBindTexture");
+ glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC)load("glDeleteTextures");
+ glad_glGenTextures = (PFNGLGENTEXTURESPROC)load("glGenTextures");
+ glad_glIsTexture = (PFNGLISTEXTUREPROC)load("glIsTexture");
+}
+static void load_GL_VERSION_1_2(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_2) return;
+ glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)load("glDrawRangeElements");
+ glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC)load("glTexImage3D");
+ glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)load("glTexSubImage3D");
+ glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)load("glCopyTexSubImage3D");
+}
+static void load_GL_VERSION_1_3(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_3) return;
+ glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC)load("glActiveTexture");
+ glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)load("glSampleCoverage");
+ glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)load("glCompressedTexImage3D");
+ glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)load("glCompressedTexImage2D");
+ glad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)load("glCompressedTexImage1D");
+ glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)load("glCompressedTexSubImage3D");
+ glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)load("glCompressedTexSubImage2D");
+ glad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)load("glCompressedTexSubImage1D");
+ glad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)load("glGetCompressedTexImage");
+}
+static void load_GL_VERSION_1_4(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_4) return;
+ glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)load("glBlendFuncSeparate");
+ glad_glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC)load("glMultiDrawArrays");
+ glad_glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC)load("glMultiDrawElements");
+ glad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC)load("glPointParameterf");
+ glad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC)load("glPointParameterfv");
+ glad_glPointParameteri = (PFNGLPOINTPARAMETERIPROC)load("glPointParameteri");
+ glad_glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC)load("glPointParameteriv");
+ glad_glBlendColor = (PFNGLBLENDCOLORPROC)load("glBlendColor");
+ glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC)load("glBlendEquation");
+}
+static void load_GL_VERSION_1_5(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_5) return;
+ glad_glGenQueries = (PFNGLGENQUERIESPROC)load("glGenQueries");
+ glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC)load("glDeleteQueries");
+ glad_glIsQuery = (PFNGLISQUERYPROC)load("glIsQuery");
+ glad_glBeginQuery = (PFNGLBEGINQUERYPROC)load("glBeginQuery");
+ glad_glEndQuery = (PFNGLENDQUERYPROC)load("glEndQuery");
+ glad_glGetQueryiv = (PFNGLGETQUERYIVPROC)load("glGetQueryiv");
+ glad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC)load("glGetQueryObjectiv");
+ glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)load("glGetQueryObjectuiv");
+ glad_glBindBuffer = (PFNGLBINDBUFFERPROC)load("glBindBuffer");
+ glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)load("glDeleteBuffers");
+ glad_glGenBuffers = (PFNGLGENBUFFERSPROC)load("glGenBuffers");
+ glad_glIsBuffer = (PFNGLISBUFFERPROC)load("glIsBuffer");
+ glad_glBufferData = (PFNGLBUFFERDATAPROC)load("glBufferData");
+ glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC)load("glBufferSubData");
+ glad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)load("glGetBufferSubData");
+ glad_glMapBuffer = (PFNGLMAPBUFFERPROC)load("glMapBuffer");
+ glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)load("glUnmapBuffer");
+ glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv");
+ glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load("glGetBufferPointerv");
+}
+static void load_GL_VERSION_2_0(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_2_0) return;
+ glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)load("glBlendEquationSeparate");
+ glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC)load("glDrawBuffers");
+ glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)load("glStencilOpSeparate");
+ glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)load("glStencilFuncSeparate");
+ glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)load("glStencilMaskSeparate");
+ glad_glAttachShader = (PFNGLATTACHSHADERPROC)load("glAttachShader");
+ glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)load("glBindAttribLocation");
+ glad_glCompileShader = (PFNGLCOMPILESHADERPROC)load("glCompileShader");
+ glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC)load("glCreateProgram");
+ glad_glCreateShader = (PFNGLCREATESHADERPROC)load("glCreateShader");
+ glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC)load("glDeleteProgram");
+ glad_glDeleteShader = (PFNGLDELETESHADERPROC)load("glDeleteShader");
+ glad_glDetachShader = (PFNGLDETACHSHADERPROC)load("glDetachShader");
+ glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load("glDisableVertexAttribArray");
+ glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load("glEnableVertexAttribArray");
+ glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)load("glGetActiveAttrib");
+ glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)load("glGetActiveUniform");
+ glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)load("glGetAttachedShaders");
+ glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)load("glGetAttribLocation");
+ glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC)load("glGetProgramiv");
+ glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)load("glGetProgramInfoLog");
+ glad_glGetShaderiv = (PFNGLGETSHADERIVPROC)load("glGetShaderiv");
+ glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)load("glGetShaderInfoLog");
+ glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)load("glGetShaderSource");
+ glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)load("glGetUniformLocation");
+ glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC)load("glGetUniformfv");
+ glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC)load("glGetUniformiv");
+ glad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC)load("glGetVertexAttribdv");
+ glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)load("glGetVertexAttribfv");
+ glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)load("glGetVertexAttribiv");
+ glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)load("glGetVertexAttribPointerv");
+ glad_glIsProgram = (PFNGLISPROGRAMPROC)load("glIsProgram");
+ glad_glIsShader = (PFNGLISSHADERPROC)load("glIsShader");
+ glad_glLinkProgram = (PFNGLLINKPROGRAMPROC)load("glLinkProgram");
+ glad_glShaderSource = (PFNGLSHADERSOURCEPROC)load("glShaderSource");
+ glad_glUseProgram = (PFNGLUSEPROGRAMPROC)load("glUseProgram");
+ glad_glUniform1f = (PFNGLUNIFORM1FPROC)load("glUniform1f");
+ glad_glUniform2f = (PFNGLUNIFORM2FPROC)load("glUniform2f");
+ glad_glUniform3f = (PFNGLUNIFORM3FPROC)load("glUniform3f");
+ glad_glUniform4f = (PFNGLUNIFORM4FPROC)load("glUniform4f");
+ glad_glUniform1i = (PFNGLUNIFORM1IPROC)load("glUniform1i");
+ glad_glUniform2i = (PFNGLUNIFORM2IPROC)load("glUniform2i");
+ glad_glUniform3i = (PFNGLUNIFORM3IPROC)load("glUniform3i");
+ glad_glUniform4i = (PFNGLUNIFORM4IPROC)load("glUniform4i");
+ glad_glUniform1fv = (PFNGLUNIFORM1FVPROC)load("glUniform1fv");
+ glad_glUniform2fv = (PFNGLUNIFORM2FVPROC)load("glUniform2fv");
+ glad_glUniform3fv = (PFNGLUNIFORM3FVPROC)load("glUniform3fv");
+ glad_glUniform4fv = (PFNGLUNIFORM4FVPROC)load("glUniform4fv");
+ glad_glUniform1iv = (PFNGLUNIFORM1IVPROC)load("glUniform1iv");
+ glad_glUniform2iv = (PFNGLUNIFORM2IVPROC)load("glUniform2iv");
+ glad_glUniform3iv = (PFNGLUNIFORM3IVPROC)load("glUniform3iv");
+ glad_glUniform4iv = (PFNGLUNIFORM4IVPROC)load("glUniform4iv");
+ glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)load("glUniformMatrix2fv");
+ glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)load("glUniformMatrix3fv");
+ glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)load("glUniformMatrix4fv");
+ glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)load("glValidateProgram");
+ glad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC)load("glVertexAttrib1d");
+ glad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC)load("glVertexAttrib1dv");
+ glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)load("glVertexAttrib1f");
+ glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)load("glVertexAttrib1fv");
+ glad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC)load("glVertexAttrib1s");
+ glad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC)load("glVertexAttrib1sv");
+ glad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC)load("glVertexAttrib2d");
+ glad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC)load("glVertexAttrib2dv");
+ glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)load("glVertexAttrib2f");
+ glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)load("glVertexAttrib2fv");
+ glad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC)load("glVertexAttrib2s");
+ glad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC)load("glVertexAttrib2sv");
+ glad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC)load("glVertexAttrib3d");
+ glad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC)load("glVertexAttrib3dv");
+ glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)load("glVertexAttrib3f");
+ glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)load("glVertexAttrib3fv");
+ glad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC)load("glVertexAttrib3s");
+ glad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC)load("glVertexAttrib3sv");
+ glad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC)load("glVertexAttrib4Nbv");
+ glad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC)load("glVertexAttrib4Niv");
+ glad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC)load("glVertexAttrib4Nsv");
+ glad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC)load("glVertexAttrib4Nub");
+ glad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC)load("glVertexAttrib4Nubv");
+ glad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC)load("glVertexAttrib4Nuiv");
+ glad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC)load("glVertexAttrib4Nusv");
+ glad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC)load("glVertexAttrib4bv");
+ glad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC)load("glVertexAttrib4d");
+ glad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC)load("glVertexAttrib4dv");
+ glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)load("glVertexAttrib4f");
+ glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)load("glVertexAttrib4fv");
+ glad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC)load("glVertexAttrib4iv");
+ glad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC)load("glVertexAttrib4s");
+ glad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC)load("glVertexAttrib4sv");
+ glad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC)load("glVertexAttrib4ubv");
+ glad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC)load("glVertexAttrib4uiv");
+ glad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC)load("glVertexAttrib4usv");
+ glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load("glVertexAttribPointer");
+}
+static void load_GL_VERSION_2_1(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_2_1) return;
+ glad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)load("glUniformMatrix2x3fv");
+ glad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)load("glUniformMatrix3x2fv");
+ glad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)load("glUniformMatrix2x4fv");
+ glad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)load("glUniformMatrix4x2fv");
+ glad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)load("glUniformMatrix3x4fv");
+ glad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)load("glUniformMatrix4x3fv");
+}
+static void load_GL_VERSION_3_0(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_3_0) return;
+ glad_glColorMaski = (PFNGLCOLORMASKIPROC)load("glColorMaski");
+ glad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC)load("glGetBooleani_v");
+ glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v");
+ glad_glEnablei = (PFNGLENABLEIPROC)load("glEnablei");
+ glad_glDisablei = (PFNGLDISABLEIPROC)load("glDisablei");
+ glad_glIsEnabledi = (PFNGLISENABLEDIPROC)load("glIsEnabledi");
+ glad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)load("glBeginTransformFeedback");
+ glad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)load("glEndTransformFeedback");
+ glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load("glBindBufferRange");
+ glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load("glBindBufferBase");
+ glad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)load("glTransformFeedbackVaryings");
+ glad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)load("glGetTransformFeedbackVarying");
+ glad_glClampColor = (PFNGLCLAMPCOLORPROC)load("glClampColor");
+ glad_glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC)load("glBeginConditionalRender");
+ glad_glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC)load("glEndConditionalRender");
+ glad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)load("glVertexAttribIPointer");
+ glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)load("glGetVertexAttribIiv");
+ glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)load("glGetVertexAttribIuiv");
+ glad_glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC)load("glVertexAttribI1i");
+ glad_glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC)load("glVertexAttribI2i");
+ glad_glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC)load("glVertexAttribI3i");
+ glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)load("glVertexAttribI4i");
+ glad_glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC)load("glVertexAttribI1ui");
+ glad_glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC)load("glVertexAttribI2ui");
+ glad_glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC)load("glVertexAttribI3ui");
+ glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)load("glVertexAttribI4ui");
+ glad_glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC)load("glVertexAttribI1iv");
+ glad_glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC)load("glVertexAttribI2iv");
+ glad_glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC)load("glVertexAttribI3iv");
+ glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)load("glVertexAttribI4iv");
+ glad_glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC)load("glVertexAttribI1uiv");
+ glad_glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC)load("glVertexAttribI2uiv");
+ glad_glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC)load("glVertexAttribI3uiv");
+ glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)load("glVertexAttribI4uiv");
+ glad_glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC)load("glVertexAttribI4bv");
+ glad_glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC)load("glVertexAttribI4sv");
+ glad_glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC)load("glVertexAttribI4ubv");
+ glad_glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC)load("glVertexAttribI4usv");
+ glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)load("glGetUniformuiv");
+ glad_glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)load("glBindFragDataLocation");
+ glad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC)load("glGetFragDataLocation");
+ glad_glUniform1ui = (PFNGLUNIFORM1UIPROC)load("glUniform1ui");
+ glad_glUniform2ui = (PFNGLUNIFORM2UIPROC)load("glUniform2ui");
+ glad_glUniform3ui = (PFNGLUNIFORM3UIPROC)load("glUniform3ui");
+ glad_glUniform4ui = (PFNGLUNIFORM4UIPROC)load("glUniform4ui");
+ glad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC)load("glUniform1uiv");
+ glad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC)load("glUniform2uiv");
+ glad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC)load("glUniform3uiv");
+ glad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC)load("glUniform4uiv");
+ glad_glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC)load("glTexParameterIiv");
+ glad_glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC)load("glTexParameterIuiv");
+ glad_glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC)load("glGetTexParameterIiv");
+ glad_glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC)load("glGetTexParameterIuiv");
+ glad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)load("glClearBufferiv");
+ glad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC)load("glClearBufferuiv");
+ glad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)load("glClearBufferfv");
+ glad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)load("glClearBufferfi");
+ glad_glGetStringi = (PFNGLGETSTRINGIPROC)load("glGetStringi");
+ glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load("glIsRenderbuffer");
+ glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load("glBindRenderbuffer");
+ glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load("glDeleteRenderbuffers");
+ glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load("glGenRenderbuffers");
+ glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load("glRenderbufferStorage");
+ glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load("glGetRenderbufferParameteriv");
+ glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load("glIsFramebuffer");
+ glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load("glBindFramebuffer");
+ glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load("glDeleteFramebuffers");
+ glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load("glGenFramebuffers");
+ glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load("glCheckFramebufferStatus");
+ glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)load("glFramebufferTexture1D");
+ glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load("glFramebufferTexture2D");
+ glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)load("glFramebufferTexture3D");
+ glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load("glFramebufferRenderbuffer");
+ glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load("glGetFramebufferAttachmentParameteriv");
+ glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load("glGenerateMipmap");
+ glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)load("glBlitFramebuffer");
+ glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)load("glRenderbufferStorageMultisample");
+ glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)load("glFramebufferTextureLayer");
+ glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)load("glMapBufferRange");
+ glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)load("glFlushMappedBufferRange");
+ glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)load("glBindVertexArray");
+ glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)load("glDeleteVertexArrays");
+ glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)load("glGenVertexArrays");
+ glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC)load("glIsVertexArray");
+}
+static void load_GL_VERSION_3_1(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_3_1) return;
+ glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)load("glDrawArraysInstanced");
+ glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)load("glDrawElementsInstanced");
+ glad_glTexBuffer = (PFNGLTEXBUFFERPROC)load("glTexBuffer");
+ glad_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)load("glPrimitiveRestartIndex");
+ glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)load("glCopyBufferSubData");
+ glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)load("glGetUniformIndices");
+ glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)load("glGetActiveUniformsiv");
+ glad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)load("glGetActiveUniformName");
+ glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)load("glGetUniformBlockIndex");
+ glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load("glGetActiveUniformBlockiv");
+ glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load("glGetActiveUniformBlockName");
+ glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load("glUniformBlockBinding");
+ glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load("glBindBufferRange");
+ glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load("glBindBufferBase");
+ glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v");
+}
+static void load_GL_VERSION_3_2(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_3_2) return;
+ glad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)load("glDrawElementsBaseVertex");
+ glad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)load("glDrawRangeElementsBaseVertex");
+ glad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)load("glDrawElementsInstancedBaseVertex");
+ glad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)load("glMultiDrawElementsBaseVertex");
+ glad_glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC)load("glProvokingVertex");
+ glad_glFenceSync = (PFNGLFENCESYNCPROC)load("glFenceSync");
+ glad_glIsSync = (PFNGLISSYNCPROC)load("glIsSync");
+ glad_glDeleteSync = (PFNGLDELETESYNCPROC)load("glDeleteSync");
+ glad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)load("glClientWaitSync");
+ glad_glWaitSync = (PFNGLWAITSYNCPROC)load("glWaitSync");
+ glad_glGetInteger64v = (PFNGLGETINTEGER64VPROC)load("glGetInteger64v");
+ glad_glGetSynciv = (PFNGLGETSYNCIVPROC)load("glGetSynciv");
+ glad_glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC)load("glGetInteger64i_v");
+ glad_glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC)load("glGetBufferParameteri64v");
+ glad_glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC)load("glFramebufferTexture");
+ glad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)load("glTexImage2DMultisample");
+ glad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC)load("glTexImage3DMultisample");
+ glad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC)load("glGetMultisamplefv");
+ glad_glSampleMaski = (PFNGLSAMPLEMASKIPROC)load("glSampleMaski");
+}
+static void load_GL_VERSION_3_3(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_3_3) return;
+ glad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)load("glBindFragDataLocationIndexed");
+ glad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC)load("glGetFragDataIndex");
+ glad_glGenSamplers = (PFNGLGENSAMPLERSPROC)load("glGenSamplers");
+ glad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)load("glDeleteSamplers");
+ glad_glIsSampler = (PFNGLISSAMPLERPROC)load("glIsSampler");
+ glad_glBindSampler = (PFNGLBINDSAMPLERPROC)load("glBindSampler");
+ glad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)load("glSamplerParameteri");
+ glad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)load("glSamplerParameteriv");
+ glad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)load("glSamplerParameterf");
+ glad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)load("glSamplerParameterfv");
+ glad_glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC)load("glSamplerParameterIiv");
+ glad_glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC)load("glSamplerParameterIuiv");
+ glad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)load("glGetSamplerParameteriv");
+ glad_glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC)load("glGetSamplerParameterIiv");
+ glad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)load("glGetSamplerParameterfv");
+ glad_glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC)load("glGetSamplerParameterIuiv");
+ glad_glQueryCounter = (PFNGLQUERYCOUNTERPROC)load("glQueryCounter");
+ glad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC)load("glGetQueryObjecti64v");
+ glad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC)load("glGetQueryObjectui64v");
+ glad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)load("glVertexAttribDivisor");
+ glad_glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC)load("glVertexAttribP1ui");
+ glad_glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC)load("glVertexAttribP1uiv");
+ glad_glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC)load("glVertexAttribP2ui");
+ glad_glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC)load("glVertexAttribP2uiv");
+ glad_glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC)load("glVertexAttribP3ui");
+ glad_glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC)load("glVertexAttribP3uiv");
+ glad_glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC)load("glVertexAttribP4ui");
+ glad_glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC)load("glVertexAttribP4uiv");
+ glad_glVertexP2ui = (PFNGLVERTEXP2UIPROC)load("glVertexP2ui");
+ glad_glVertexP2uiv = (PFNGLVERTEXP2UIVPROC)load("glVertexP2uiv");
+ glad_glVertexP3ui = (PFNGLVERTEXP3UIPROC)load("glVertexP3ui");
+ glad_glVertexP3uiv = (PFNGLVERTEXP3UIVPROC)load("glVertexP3uiv");
+ glad_glVertexP4ui = (PFNGLVERTEXP4UIPROC)load("glVertexP4ui");
+ glad_glVertexP4uiv = (PFNGLVERTEXP4UIVPROC)load("glVertexP4uiv");
+ glad_glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC)load("glTexCoordP1ui");
+ glad_glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC)load("glTexCoordP1uiv");
+ glad_glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC)load("glTexCoordP2ui");
+ glad_glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC)load("glTexCoordP2uiv");
+ glad_glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC)load("glTexCoordP3ui");
+ glad_glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC)load("glTexCoordP3uiv");
+ glad_glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC)load("glTexCoordP4ui");
+ glad_glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC)load("glTexCoordP4uiv");
+ glad_glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC)load("glMultiTexCoordP1ui");
+ glad_glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC)load("glMultiTexCoordP1uiv");
+ glad_glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC)load("glMultiTexCoordP2ui");
+ glad_glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC)load("glMultiTexCoordP2uiv");
+ glad_glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC)load("glMultiTexCoordP3ui");
+ glad_glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC)load("glMultiTexCoordP3uiv");
+ glad_glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC)load("glMultiTexCoordP4ui");
+ glad_glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC)load("glMultiTexCoordP4uiv");
+ glad_glNormalP3ui = (PFNGLNORMALP3UIPROC)load("glNormalP3ui");
+ glad_glNormalP3uiv = (PFNGLNORMALP3UIVPROC)load("glNormalP3uiv");
+ glad_glColorP3ui = (PFNGLCOLORP3UIPROC)load("glColorP3ui");
+ glad_glColorP3uiv = (PFNGLCOLORP3UIVPROC)load("glColorP3uiv");
+ glad_glColorP4ui = (PFNGLCOLORP4UIPROC)load("glColorP4ui");
+ glad_glColorP4uiv = (PFNGLCOLORP4UIVPROC)load("glColorP4uiv");
+ glad_glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC)load("glSecondaryColorP3ui");
+ glad_glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC)load("glSecondaryColorP3uiv");
+}
+static void find_extensionsGL(void) {
+ get_exts();
+}
+
+static void find_coreGL(void) {
+
+ /* Thank you @elmindreda
+ * https://github.com/elmindreda/greg/blob/master/templates/greg.c.in#L176
+ * https://github.com/glfw/glfw/blob/master/src/context.c#L36
+ */
+ int i, major, minor;
+
+ const char* version;
+ const char* prefixes[] = {
+ "OpenGL ES-CM ",
+ "OpenGL ES-CL ",
+ "OpenGL ES ",
+ NULL
+ };
+
+ version = (const char*) glGetString(GL_VERSION);
+ if (!version) return;
+
+ for (i = 0; prefixes[i]; i++) {
+ const size_t length = strlen(prefixes[i]);
+ if (strncmp(version, prefixes[i], length) == 0) {
+ version += length;
+ break;
+ }
+ }
+
+/* PR #18 */
+#ifdef _MSC_VER
+ sscanf_s(version, "%d.%d", &major, &minor);
+#else
+ sscanf(version, "%d.%d", &major, &minor);
+#endif
+
+ GLVersion.major = major; GLVersion.minor = minor;
+ max_loaded_major = major; max_loaded_minor = minor;
+ GLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1;
+ GLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1;
+ GLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1;
+ GLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1;
+ GLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1;
+ GLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1;
+ GLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2;
+ GLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2;
+ GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3;
+ GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3;
+ GLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3;
+ GLAD_GL_VERSION_3_3 = (major == 3 && minor >= 3) || major > 3;
+ if (GLVersion.major > 3 || (GLVersion.major >= 3 && GLVersion.minor >= 3)) {
+ max_loaded_major = 3;
+ max_loaded_minor = 3;
+ }
+}
+
+int gladLoadGLLoader(GLADloadproc load) {
+ GLVersion.major = 0; GLVersion.minor = 0;
+ glGetString = (PFNGLGETSTRINGPROC)load("glGetString");
+ if(glGetString == NULL) return 0;
+ if(glGetString(GL_VERSION) == NULL) return 0;
+ find_coreGL();
+ load_GL_VERSION_1_0(load);
+ load_GL_VERSION_1_1(load);
+ load_GL_VERSION_1_2(load);
+ load_GL_VERSION_1_3(load);
+ load_GL_VERSION_1_4(load);
+ load_GL_VERSION_1_5(load);
+ load_GL_VERSION_2_0(load);
+ load_GL_VERSION_2_1(load);
+ load_GL_VERSION_3_0(load);
+ load_GL_VERSION_3_1(load);
+ load_GL_VERSION_3_2(load);
+ load_GL_VERSION_3_3(load);
+
+ find_extensionsGL();
+ return GLVersion.major != 0 || GLVersion.minor != 0;
+}
+
+static void load_GL_ES_VERSION_2_0(GLADloadproc load) {
+ if(!GLAD_GL_ES_VERSION_2_0) return;
+ glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC)load("glActiveTexture");
+ glad_glAttachShader = (PFNGLATTACHSHADERPROC)load("glAttachShader");
+ glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)load("glBindAttribLocation");
+ glad_glBindBuffer = (PFNGLBINDBUFFERPROC)load("glBindBuffer");
+ glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load("glBindFramebuffer");
+ glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load("glBindRenderbuffer");
+ glad_glBindTexture = (PFNGLBINDTEXTUREPROC)load("glBindTexture");
+ glad_glBlendColor = (PFNGLBLENDCOLORPROC)load("glBlendColor");
+ glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC)load("glBlendEquation");
+ glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)load("glBlendEquationSeparate");
+ glad_glBlendFunc = (PFNGLBLENDFUNCPROC)load("glBlendFunc");
+ glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)load("glBlendFuncSeparate");
+ glad_glBufferData = (PFNGLBUFFERDATAPROC)load("glBufferData");
+ glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC)load("glBufferSubData");
+ glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load("glCheckFramebufferStatus");
+ glad_glClear = (PFNGLCLEARPROC)load("glClear");
+ glad_glClearColor = (PFNGLCLEARCOLORPROC)load("glClearColor");
+ glad_glClearDepthf = (PFNGLCLEARDEPTHFPROC)load("glClearDepthf");
+ glad_glClearStencil = (PFNGLCLEARSTENCILPROC)load("glClearStencil");
+ glad_glColorMask = (PFNGLCOLORMASKPROC)load("glColorMask");
+ glad_glCompileShader = (PFNGLCOMPILESHADERPROC)load("glCompileShader");
+ glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)load("glCompressedTexImage2D");
+ glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)load("glCompressedTexSubImage2D");
+ glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC)load("glCopyTexImage2D");
+ glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC)load("glCopyTexSubImage2D");
+ glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC)load("glCreateProgram");
+ glad_glCreateShader = (PFNGLCREATESHADERPROC)load("glCreateShader");
+ glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace");
+ glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)load("glDeleteBuffers");
+ glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load("glDeleteFramebuffers");
+ glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC)load("glDeleteProgram");
+ glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load("glDeleteRenderbuffers");
+ glad_glDeleteShader = (PFNGLDELETESHADERPROC)load("glDeleteShader");
+ glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC)load("glDeleteTextures");
+ glad_glDepthFunc = (PFNGLDEPTHFUNCPROC)load("glDepthFunc");
+ glad_glDepthMask = (PFNGLDEPTHMASKPROC)load("glDepthMask");
+ glad_glDepthRangef = (PFNGLDEPTHRANGEFPROC)load("glDepthRangef");
+ glad_glDetachShader = (PFNGLDETACHSHADERPROC)load("glDetachShader");
+ glad_glDisable = (PFNGLDISABLEPROC)load("glDisable");
+ glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load("glDisableVertexAttribArray");
+ glad_glDrawArrays = (PFNGLDRAWARRAYSPROC)load("glDrawArrays");
+ glad_glDrawElements = (PFNGLDRAWELEMENTSPROC)load("glDrawElements");
+ glad_glEnable = (PFNGLENABLEPROC)load("glEnable");
+ glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load("glEnableVertexAttribArray");
+ glad_glFinish = (PFNGLFINISHPROC)load("glFinish");
+ glad_glFlush = (PFNGLFLUSHPROC)load("glFlush");
+ glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load("glFramebufferRenderbuffer");
+ glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load("glFramebufferTexture2D");
+ glad_glFrontFace = (PFNGLFRONTFACEPROC)load("glFrontFace");
+ glad_glGenBuffers = (PFNGLGENBUFFERSPROC)load("glGenBuffers");
+ glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load("glGenerateMipmap");
+ glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load("glGenFramebuffers");
+ glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load("glGenRenderbuffers");
+ glad_glGenTextures = (PFNGLGENTEXTURESPROC)load("glGenTextures");
+ glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)load("glGetActiveAttrib");
+ glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)load("glGetActiveUniform");
+ glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)load("glGetAttachedShaders");
+ glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)load("glGetAttribLocation");
+ glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC)load("glGetBooleanv");
+ glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv");
+ glad_glGetError = (PFNGLGETERRORPROC)load("glGetError");
+ glad_glGetFloatv = (PFNGLGETFLOATVPROC)load("glGetFloatv");
+ glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load("glGetFramebufferAttachmentParameteriv");
+ glad_glGetIntegerv = (PFNGLGETINTEGERVPROC)load("glGetIntegerv");
+ glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC)load("glGetProgramiv");
+ glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)load("glGetProgramInfoLog");
+ glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load("glGetRenderbufferParameteriv");
+ glad_glGetShaderiv = (PFNGLGETSHADERIVPROC)load("glGetShaderiv");
+ glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)load("glGetShaderInfoLog");
+ glad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC)load("glGetShaderPrecisionFormat");
+ glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)load("glGetShaderSource");
+ glad_glGetString = (PFNGLGETSTRINGPROC)load("glGetString");
+ glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC)load("glGetTexParameterfv");
+ glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC)load("glGetTexParameteriv");
+ glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC)load("glGetUniformfv");
+ glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC)load("glGetUniformiv");
+ glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)load("glGetUniformLocation");
+ glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)load("glGetVertexAttribfv");
+ glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)load("glGetVertexAttribiv");
+ glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)load("glGetVertexAttribPointerv");
+ glad_glHint = (PFNGLHINTPROC)load("glHint");
+ glad_glIsBuffer = (PFNGLISBUFFERPROC)load("glIsBuffer");
+ glad_glIsEnabled = (PFNGLISENABLEDPROC)load("glIsEnabled");
+ glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load("glIsFramebuffer");
+ glad_glIsProgram = (PFNGLISPROGRAMPROC)load("glIsProgram");
+ glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load("glIsRenderbuffer");
+ glad_glIsShader = (PFNGLISSHADERPROC)load("glIsShader");
+ glad_glIsTexture = (PFNGLISTEXTUREPROC)load("glIsTexture");
+ glad_glLineWidth = (PFNGLLINEWIDTHPROC)load("glLineWidth");
+ glad_glLinkProgram = (PFNGLLINKPROGRAMPROC)load("glLinkProgram");
+ glad_glPixelStorei = (PFNGLPIXELSTOREIPROC)load("glPixelStorei");
+ glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC)load("glPolygonOffset");
+ glad_glReadPixels = (PFNGLREADPIXELSPROC)load("glReadPixels");
+ glad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC)load("glReleaseShaderCompiler");
+ glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load("glRenderbufferStorage");
+ glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)load("glSampleCoverage");
+ glad_glScissor = (PFNGLSCISSORPROC)load("glScissor");
+ glad_glShaderBinary = (PFNGLSHADERBINARYPROC)load("glShaderBinary");
+ glad_glShaderSource = (PFNGLSHADERSOURCEPROC)load("glShaderSource");
+ glad_glStencilFunc = (PFNGLSTENCILFUNCPROC)load("glStencilFunc");
+ glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)load("glStencilFuncSeparate");
+ glad_glStencilMask = (PFNGLSTENCILMASKPROC)load("glStencilMask");
+ glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)load("glStencilMaskSeparate");
+ glad_glStencilOp = (PFNGLSTENCILOPPROC)load("glStencilOp");
+ glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)load("glStencilOpSeparate");
+ glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC)load("glTexImage2D");
+ glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC)load("glTexParameterf");
+ glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)load("glTexParameterfv");
+ glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC)load("glTexParameteri");
+ glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)load("glTexParameteriv");
+ glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC)load("glTexSubImage2D");
+ glad_glUniform1f = (PFNGLUNIFORM1FPROC)load("glUniform1f");
+ glad_glUniform1fv = (PFNGLUNIFORM1FVPROC)load("glUniform1fv");
+ glad_glUniform1i = (PFNGLUNIFORM1IPROC)load("glUniform1i");
+ glad_glUniform1iv = (PFNGLUNIFORM1IVPROC)load("glUniform1iv");
+ glad_glUniform2f = (PFNGLUNIFORM2FPROC)load("glUniform2f");
+ glad_glUniform2fv = (PFNGLUNIFORM2FVPROC)load("glUniform2fv");
+ glad_glUniform2i = (PFNGLUNIFORM2IPROC)load("glUniform2i");
+ glad_glUniform2iv = (PFNGLUNIFORM2IVPROC)load("glUniform2iv");
+ glad_glUniform3f = (PFNGLUNIFORM3FPROC)load("glUniform3f");
+ glad_glUniform3fv = (PFNGLUNIFORM3FVPROC)load("glUniform3fv");
+ glad_glUniform3i = (PFNGLUNIFORM3IPROC)load("glUniform3i");
+ glad_glUniform3iv = (PFNGLUNIFORM3IVPROC)load("glUniform3iv");
+ glad_glUniform4f = (PFNGLUNIFORM4FPROC)load("glUniform4f");
+ glad_glUniform4fv = (PFNGLUNIFORM4FVPROC)load("glUniform4fv");
+ glad_glUniform4i = (PFNGLUNIFORM4IPROC)load("glUniform4i");
+ glad_glUniform4iv = (PFNGLUNIFORM4IVPROC)load("glUniform4iv");
+ glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)load("glUniformMatrix2fv");
+ glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)load("glUniformMatrix3fv");
+ glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)load("glUniformMatrix4fv");
+ glad_glUseProgram = (PFNGLUSEPROGRAMPROC)load("glUseProgram");
+ glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)load("glValidateProgram");
+ glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)load("glVertexAttrib1f");
+ glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)load("glVertexAttrib1fv");
+ glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)load("glVertexAttrib2f");
+ glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)load("glVertexAttrib2fv");
+ glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)load("glVertexAttrib3f");
+ glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)load("glVertexAttrib3fv");
+ glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)load("glVertexAttrib4f");
+ glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)load("glVertexAttrib4fv");
+ glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load("glVertexAttribPointer");
+ glad_glViewport = (PFNGLVIEWPORTPROC)load("glViewport");
+}
+static void load_GL_ES_VERSION_3_0(GLADloadproc load) {
+ if(!GLAD_GL_ES_VERSION_3_0) return;
+ glad_glReadBuffer = (PFNGLREADBUFFERPROC)load("glReadBuffer");
+ glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)load("glDrawRangeElements");
+ glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC)load("glTexImage3D");
+ glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)load("glTexSubImage3D");
+ glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)load("glCopyTexSubImage3D");
+ glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)load("glCompressedTexImage3D");
+ glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)load("glCompressedTexSubImage3D");
+ glad_glGenQueries = (PFNGLGENQUERIESPROC)load("glGenQueries");
+ glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC)load("glDeleteQueries");
+ glad_glIsQuery = (PFNGLISQUERYPROC)load("glIsQuery");
+ glad_glBeginQuery = (PFNGLBEGINQUERYPROC)load("glBeginQuery");
+ glad_glEndQuery = (PFNGLENDQUERYPROC)load("glEndQuery");
+ glad_glGetQueryiv = (PFNGLGETQUERYIVPROC)load("glGetQueryiv");
+ glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)load("glGetQueryObjectuiv");
+ glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)load("glUnmapBuffer");
+ glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load("glGetBufferPointerv");
+ glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC)load("glDrawBuffers");
+ glad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)load("glUniformMatrix2x3fv");
+ glad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)load("glUniformMatrix3x2fv");
+ glad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)load("glUniformMatrix2x4fv");
+ glad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)load("glUniformMatrix4x2fv");
+ glad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)load("glUniformMatrix3x4fv");
+ glad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)load("glUniformMatrix4x3fv");
+ glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)load("glBlitFramebuffer");
+ glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)load("glRenderbufferStorageMultisample");
+ glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)load("glFramebufferTextureLayer");
+ glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)load("glMapBufferRange");
+ glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)load("glFlushMappedBufferRange");
+ glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)load("glBindVertexArray");
+ glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)load("glDeleteVertexArrays");
+ glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)load("glGenVertexArrays");
+ glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC)load("glIsVertexArray");
+ glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v");
+ glad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)load("glBeginTransformFeedback");
+ glad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)load("glEndTransformFeedback");
+ glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load("glBindBufferRange");
+ glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load("glBindBufferBase");
+ glad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)load("glTransformFeedbackVaryings");
+ glad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)load("glGetTransformFeedbackVarying");
+ glad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)load("glVertexAttribIPointer");
+ glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)load("glGetVertexAttribIiv");
+ glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)load("glGetVertexAttribIuiv");
+ glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)load("glVertexAttribI4i");
+ glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)load("glVertexAttribI4ui");
+ glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)load("glVertexAttribI4iv");
+ glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)load("glVertexAttribI4uiv");
+ glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)load("glGetUniformuiv");
+ glad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC)load("glGetFragDataLocation");
+ glad_glUniform1ui = (PFNGLUNIFORM1UIPROC)load("glUniform1ui");
+ glad_glUniform2ui = (PFNGLUNIFORM2UIPROC)load("glUniform2ui");
+ glad_glUniform3ui = (PFNGLUNIFORM3UIPROC)load("glUniform3ui");
+ glad_glUniform4ui = (PFNGLUNIFORM4UIPROC)load("glUniform4ui");
+ glad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC)load("glUniform1uiv");
+ glad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC)load("glUniform2uiv");
+ glad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC)load("glUniform3uiv");
+ glad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC)load("glUniform4uiv");
+ glad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)load("glClearBufferiv");
+ glad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC)load("glClearBufferuiv");
+ glad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)load("glClearBufferfv");
+ glad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)load("glClearBufferfi");
+ glad_glGetStringi = (PFNGLGETSTRINGIPROC)load("glGetStringi");
+ glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)load("glCopyBufferSubData");
+ glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)load("glGetUniformIndices");
+ glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)load("glGetActiveUniformsiv");
+ glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)load("glGetUniformBlockIndex");
+ glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load("glGetActiveUniformBlockiv");
+ glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load("glGetActiveUniformBlockName");
+ glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load("glUniformBlockBinding");
+ glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)load("glDrawArraysInstanced");
+ glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)load("glDrawElementsInstanced");
+ glad_glFenceSync = (PFNGLFENCESYNCPROC)load("glFenceSync");
+ glad_glIsSync = (PFNGLISSYNCPROC)load("glIsSync");
+ glad_glDeleteSync = (PFNGLDELETESYNCPROC)load("glDeleteSync");
+ glad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)load("glClientWaitSync");
+ glad_glWaitSync = (PFNGLWAITSYNCPROC)load("glWaitSync");
+ glad_glGetInteger64v = (PFNGLGETINTEGER64VPROC)load("glGetInteger64v");
+ glad_glGetSynciv = (PFNGLGETSYNCIVPROC)load("glGetSynciv");
+ glad_glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC)load("glGetInteger64i_v");
+ glad_glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC)load("glGetBufferParameteri64v");
+ glad_glGenSamplers = (PFNGLGENSAMPLERSPROC)load("glGenSamplers");
+ glad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)load("glDeleteSamplers");
+ glad_glIsSampler = (PFNGLISSAMPLERPROC)load("glIsSampler");
+ glad_glBindSampler = (PFNGLBINDSAMPLERPROC)load("glBindSampler");
+ glad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)load("glSamplerParameteri");
+ glad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)load("glSamplerParameteriv");
+ glad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)load("glSamplerParameterf");
+ glad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)load("glSamplerParameterfv");
+ glad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)load("glGetSamplerParameteriv");
+ glad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)load("glGetSamplerParameterfv");
+ glad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)load("glVertexAttribDivisor");
+ glad_glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC)load("glBindTransformFeedback");
+ glad_glDeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC)load("glDeleteTransformFeedbacks");
+ glad_glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC)load("glGenTransformFeedbacks");
+ glad_glIsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC)load("glIsTransformFeedback");
+ glad_glPauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC)load("glPauseTransformFeedback");
+ glad_glResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC)load("glResumeTransformFeedback");
+ glad_glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)load("glGetProgramBinary");
+ glad_glProgramBinary = (PFNGLPROGRAMBINARYPROC)load("glProgramBinary");
+ glad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)load("glProgramParameteri");
+ glad_glInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC)load("glInvalidateFramebuffer");
+ glad_glInvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC)load("glInvalidateSubFramebuffer");
+ glad_glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)load("glTexStorage2D");
+ glad_glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)load("glTexStorage3D");
+ glad_glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC)load("glGetInternalformativ");
+}
+static void find_extensionsGLES2(void) {
+ get_exts();
+}
+
+static void find_coreGLES2(void) {
+
+ /* Thank you @elmindreda
+ * https://github.com/elmindreda/greg/blob/master/templates/greg.c.in#L176
+ * https://github.com/glfw/glfw/blob/master/src/context.c#L36
+ */
+ int i, major, minor;
+
+ const char* version;
+ const char* prefixes[] = {
+ "OpenGL ES-CM ",
+ "OpenGL ES-CL ",
+ "OpenGL ES ",
+ NULL
+ };
+
+ version = (const char*) glGetString(GL_VERSION);
+ if (!version) return;
+
+ for (i = 0; prefixes[i]; i++) {
+ const size_t length = strlen(prefixes[i]);
+ if (strncmp(version, prefixes[i], length) == 0) {
+ version += length;
+ break;
+ }
+ }
+
+/* PR #18 */
+#ifdef _MSC_VER
+ sscanf_s(version, "%d.%d", &major, &minor);
+#else
+ sscanf(version, "%d.%d", &major, &minor);
+#endif
+
+ GLVersion.major = major; GLVersion.minor = minor;
+ max_loaded_major = major; max_loaded_minor = minor;
+ GLAD_GL_ES_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2;
+ GLAD_GL_ES_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3;
+ if (GLVersion.major > 3 || (GLVersion.major >= 3 && GLVersion.minor >= 0)) {
+ max_loaded_major = 3;
+ max_loaded_minor = 0;
+ }
+}
+
+int gladLoadGLES2Loader(GLADloadproc load) {
+ GLVersion.major = 0; GLVersion.minor = 0;
+ glGetString = (PFNGLGETSTRINGPROC)load("glGetString");
+ if(glGetString == NULL) return 0;
+ if(glGetString(GL_VERSION) == NULL) return 0;
+ find_coreGLES2();
+ load_GL_ES_VERSION_2_0(load);
+ load_GL_ES_VERSION_3_0(load);
+
+ find_extensionsGLES2();
+ return GLVersion.major != 0 || GLVersion.minor != 0;
+}
+
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+/* hid todo
+#include "key_map.h"
+#include <map>
+
+namespace KeyMap {
+
+static std::map<HostDeviceKey, Service::HID::PadState> key_map;
+static int next_device_id = 0;
+
+int NewDeviceId() {
+ return next_device_id++;
+}
+
+void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState) {
+ key_map[key].hex = padState.hex;
+}
+
+Service::HID::PadState GetPadKey(HostDeviceKey key) {
+ return key_map[key];
+}
+
+}
+*/
\ No newline at end of file
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/hid/hid.h"
+
+namespace KeyMap {
+
+/**
+ * Represents a key for a specific host device.
+ */
+struct HostDeviceKey {
+ int key_code;
+ int device_id; ///< Uniquely identifies a host device
+
+ bool operator < (const HostDeviceKey &other) const {
+ if (device_id == other.device_id) {
+ return key_code < other.key_code;
+ }
+ return device_id < other.device_id;
+ }
+
+ bool operator == (const HostDeviceKey &other) const {
+ return device_id == other.device_id && key_code == other.key_code;
+ }
+};
+
+/**
+ * Generates a new device id, which uniquely identifies a host device within KeyMap.
+ */
+int NewDeviceId();
+
+/**
+ * Maps a device-specific key to a PadState.
+ */
+void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState);
+
+/**
+ * Gets the PadState that's mapped to the provided device-specific key.
+ */
+Service::HID::PadState GetPadKey(HostDeviceKey key);
+
+}
--- /dev/null
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <cstdlib>
+#include <type_traits>
+
+
+namespace MathUtil
+{
+
+inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1, unsigned length1) {
+ return (std::max(start0, start1) < std::min(start0 + length0, start1 + length1));
+}
+
+template<typename T>
+inline T Clamp(const T val, const T& min, const T& max)
+{
+ return std::max(min, std::min(max, val));
+}
+
+template<class T>
+struct Rectangle
+{
+ T left;
+ T top;
+ T right;
+ T bottom;
+
+ Rectangle() {}
+
+ Rectangle(T left, T top, T right, T bottom) : left(left), top(top), right(right), bottom(bottom) {}
+
+ T GetWidth() const { return std::abs(static_cast<typename std::make_signed<T>::type>(right - left)); }
+ T GetHeight() const { return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); }
+};
+
+} // namespace MathUtil
--- /dev/null
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+#pragma once
+
+#include <limits>
+#include <type_traits>
+
+#ifndef __forceinline
+#ifndef _WIN32
+#define __forceinline inline __attribute__((always_inline))
+#endif
+#endif
+
+namespace nihstro {
+
+/*
+ * Abstract bitfield class
+ *
+ * Allows endianness-independent access to individual bitfields within some raw
+ * integer value. The assembly generated by this class is identical to the
+ * usage of raw bitfields, so it's a perfectly fine replacement.
+ *
+ * For BitField<X,Y,Z>, X is the distance of the bitfield to the LSB of the
+ * raw value, Y is the length in bits of the bitfield. Z is an integer type
+ * which determines the sign of the bitfield. Z must have the same size as the
+ * raw integer.
+ *
+ *
+ * General usage:
+ *
+ * Create a new union with the raw integer value as a member.
+ * Then for each bitfield you want to expose, add a BitField member
+ * in the union. The template parameters are the bit offset and the number
+ * of desired bits.
+ *
+ * Changes in the bitfield members will then get reflected in the raw integer
+ * value and vice-versa.
+ *
+ *
+ * Sample usage:
+ *
+ * union SomeRegister
+ * {
+ * u32 hex;
+ *
+ * BitField<0,7,u32> first_seven_bits; // unsigned
+ * BitField<7,8,u32> next_eight_bits; // unsigned
+ * BitField<3,15,s32> some_signed_fields; // signed
+ * };
+ *
+ * This is equivalent to the little-endian specific code:
+ *
+ * union SomeRegister
+ * {
+ * u32 hex;
+ *
+ * struct
+ * {
+ * u32 first_seven_bits : 7;
+ * u32 next_eight_bits : 8;
+ * };
+ * struct
+ * {
+ * u32 : 3; // padding
+ * s32 some_signed_fields : 15;
+ * };
+ * };
+ *
+ *
+ * Caveats:
+ *
+ * 1)
+ * BitField provides automatic casting from and to the storage type where
+ * appropriate. However, when using non-typesafe functions like printf, an
+ * explicit cast must be performed on the BitField object to make sure it gets
+ * passed correctly, e.g.:
+ * printf("Value: %d", (s32)some_register.some_signed_fields);
+ *
+ * 2)
+ * Not really a caveat, but potentially irritating: This class is used in some
+ * packed structures that do not guarantee proper alignment. Therefore we have
+ * to use #pragma pack here not to pack the members of the class, but instead
+ * to break GCC's assumption that the members of the class are aligned on
+ * sizeof(StorageType).
+ * TODO(neobrain): Confirm that this is a proper fix and not just masking
+ * symptoms.
+ */
+#pragma pack(1)
+template<std::size_t position, std::size_t bits, typename T>
+struct BitField
+{
+private:
+ // This constructor might be considered ambiguous:
+ // Would it initialize the storage or just the bitfield?
+ // Hence, delete it. Use the assignment operator to set bitfield values!
+ BitField(T val) = delete;
+
+public:
+ // Force default constructor to be created
+ // so that we can use this within unions
+ BitField() = default;
+
+#ifndef _WIN32
+ // We explicitly delete the copy assigment operator here, because the
+ // default copy assignment would copy the full storage value, rather than
+ // just the bits relevant to this particular bit field.
+ // Ideally, we would just implement the copy assignment to copy only the
+ // relevant bits, but this requires compiler support for unrestricted
+ // unions.
+ // MSVC 2013 has no support for this, hence we disable this code on
+ // Windows (so that the default copy assignment operator will be used).
+ // For any C++11 conformant compiler we delete the operator to make sure
+ // we never use this inappropriate operator to begin with.
+ // TODO: Implement this operator properly once all target compilers
+ // support unrestricted unions.
+ // TODO: Actually, deleting and overriding this operator both cause more
+ // harm than anything. Instead, it's suggested to never use the copy
+ // constructor directly but instead invoke Assign() explicitly.
+ // BitField& operator=(const BitField&) = delete;
+#endif
+
+ __forceinline BitField& operator=(T val)
+ {
+ Assign(val);
+ return *this;
+ }
+
+ __forceinline operator typename std::add_const<T>::type() const
+ {
+ return Value();
+ }
+
+ __forceinline void Assign(const T& value) {
+ storage = (storage & ~GetMask()) | ((((StorageType)value) << position) & GetMask());
+ }
+
+ __forceinline typename std::add_const<T>::type Value() const
+ {
+ if (std::numeric_limits<T>::is_signed)
+ {
+ std::size_t shift = 8 * sizeof(T)-bits;
+ return (T)(((storage & GetMask()) << (shift - position)) >> shift);
+ }
+ else
+ {
+ return (T)((storage & GetMask()) >> position);
+ }
+ }
+
+ static size_t NumBits() {
+ return bits;
+ }
+
+private:
+ // StorageType is T for non-enum types and the underlying type of T if
+ // T is an enumeration. Note that T is wrapped within an enable_if in the
+ // former case to workaround compile errors which arise when using
+ // std::underlying_type<T>::type directly.
+ typedef typename std::conditional < std::is_enum<T>::value,
+ std::underlying_type<T>,
+ std::enable_if < true, T >> ::type::type StorageType;
+
+ // Unsigned version of StorageType
+ typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
+
+ __forceinline StorageType GetMask() const
+ {
+ return ((~(StorageTypeU)0) >> (8 * sizeof(T)-bits)) << position;
+ }
+
+ StorageType storage;
+
+ static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
+
+ // And, you know, just in case people specify something stupid like bits=position=0x80000000
+ static_assert(position < 8 * sizeof(T), "Invalid position");
+ static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
+ static_assert(bits > 0, "Invalid number of bits");
+ static_assert(std::is_standard_layout<T>::value, "Invalid base type");
+};
+
+/**
+ * Abstract bit flag class. This is basically a specialization of BitField for single-bit fields.
+ * Instead of being cast to the underlying type, it acts like a boolean.
+ */
+template<std::size_t position, typename T>
+struct BitFlag : protected BitField<position, 1, T>
+{
+private:
+ BitFlag(T val) = delete;
+
+ typedef BitField<position, 1, T> ParentType;
+
+public:
+ BitFlag() = default;
+
+#ifndef _WIN32
+ BitFlag& operator=(const BitFlag&) = delete;
+#endif
+
+ __forceinline BitFlag& operator=(bool val)
+ {
+ Assign(val);
+ return *this;
+ }
+
+ __forceinline operator bool() const
+ {
+ return Value();
+ }
+
+ __forceinline void Assign(bool value) {
+ ParentType::Assign(value);
+ }
+
+ __forceinline bool Value() const
+ {
+ return ParentType::Value() != 0;
+ }
+};
+#pragma pack()
+
+} // namespace
--- /dev/null
+#pragma once
+
+#include <cstdint>
+#include <limits>
+
+#include "bit_field.h"
+
+namespace nihstro {
+
+inline uint32_t to_float24(float val) {
+ static_assert(std::numeric_limits<float>::is_iec559, "Compiler does not adhere to IEEE 754");
+
+ union Float32 {
+ BitField< 0, 23, uint32_t> mant;
+ BitField<23, 8, uint32_t> biased_exp;
+ BitField<31, 1, uint32_t> sign;
+
+ static int ExponentBias() {
+ return 127;
+ }
+ } f32 = reinterpret_cast<Float32&>(val);
+
+ union Float24 {
+ uint32_t hex;
+
+ BitField< 0, 16, uint32_t> mant;
+ BitField<16, 7, uint32_t> biased_exp;
+ BitField<23, 1, uint32_t> sign;
+
+ static int ExponentBias() {
+ return 63;
+ }
+ } f24 = { 0 };
+
+ int biased_exp = (int)f32.biased_exp - Float32::ExponentBias() + Float24::ExponentBias();
+ unsigned mant = (biased_exp >= 0) ? (f32.mant >> (f32.mant.NumBits() - f24.mant.NumBits())) : 0;
+ if (biased_exp >= (1 << f24.biased_exp.NumBits())) {
+ // TODO: Return +inf or -inf
+ }
+
+ f24.biased_exp = std::max(0, biased_exp);
+ f24.mant = mant;
+ f24.sign = f32.sign.Value();
+
+ return f24.hex;
+}
+
+} // namespace
--- /dev/null
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+#include <initializer_list>
+#include <vector>
+
+#include "bit_field.h"
+#include "float24.h"
+#include "shader_binary.h"
+#include "shader_bytecode.h"
+
+namespace nihstro {
+
+struct ShaderBinary {
+ std::vector<Instruction> program;
+ std::vector<SwizzlePattern> swizzle_table;
+ std::vector<OutputRegisterInfo> output_table;
+ std::vector<ConstantInfo> constant_table;
+ std::vector<char> symbol_table;
+ std::vector<UniformInfo> uniform_table;
+};
+
+struct DestRegisterOrTemporary : public DestRegister {
+ DestRegisterOrTemporary(const DestRegister& oth) : DestRegister(oth) {}
+ DestRegisterOrTemporary(const SourceRegister& oth) : DestRegister(DestRegister::FromTypeAndIndex(oth.GetRegisterType(), oth.GetIndex())) {
+ if (oth.GetRegisterType() != RegisterType::Temporary)
+ throw "Invalid source register used as output";
+ }
+};
+
+struct InlineAsm {
+ enum RelativeAddress {
+ None,
+ A1,
+ A2,
+ AL
+ };
+
+ struct DestMask {
+ DestMask(const std::string& mask) {
+ static const std::map<std::string,uint32_t> valid_masks {
+ { "x", 8 }, { "y", 4 }, { "z", 2 }, { "w", 1 },
+ { "xy", 12 }, { "xz", 10 }, { "xw", 9 },
+ { "yz", 6 }, { "yw", 5 }, { "zw", 3 },
+ { "xyz", 14 }, { "xyw", 13 }, { "xzw", 11 }, { "yzw", 7 },
+ { "xyzw", 15 }, { "", 15 }
+ };
+
+ dest_mask = valid_masks.at(mask);
+ }
+
+ DestMask(const char* mask) : DestMask(std::string(mask)) {}
+
+ DestMask(const DestMask&) = default;
+
+ uint32_t dest_mask;
+ };
+
+ struct SwizzleMask {
+ SwizzleMask(const std::string& swizzle) : negate(false) {
+ selectors[0] = SwizzlePattern::Selector::x;
+ selectors[1] = SwizzlePattern::Selector::y;
+ selectors[2] = SwizzlePattern::Selector::z;
+ selectors[3] = SwizzlePattern::Selector::w;
+
+ if (swizzle.length() == 0)
+ return;
+
+ if (swizzle.length() > 5) {
+ throw "Invalid swizzle mask";
+ }
+
+ int index = 0;
+
+ if (swizzle[index] == '-') {
+ negate = true;
+ } else if (swizzle[index] == '+') {
+ index++;
+ }
+
+ for (int i = 0; i < 4; ++i) {
+ if (swizzle.length() <= index + i)
+ return;
+
+ switch (swizzle[index + i]) {
+ case 'x': selectors[i] = SwizzlePattern::Selector::x; break;
+ case 'y': selectors[i] = SwizzlePattern::Selector::y; break;
+ case 'z': selectors[i] = SwizzlePattern::Selector::z; break;
+ case 'w': selectors[i] = SwizzlePattern::Selector::w; break;
+ default:
+ throw "Invalid swizzle mask";
+ }
+ }
+ }
+
+ SwizzleMask(const char* swizzle) : SwizzleMask(std::string(swizzle)) {}
+
+ SwizzleMask(const SwizzleMask&) = default;
+
+ SwizzlePattern::Selector selectors[4];
+ bool negate;
+ };
+
+ enum Type {
+ Regular,
+ Output,
+ Constant,
+ Uniform,
+ Else,
+ EndIf,
+ EndLoop,
+ Label
+ } type;
+
+ InlineAsm(Type type) : type(type) {
+ }
+
+ InlineAsm(OpCode opcode) : type(Regular) {
+ if (opcode.GetInfo().type != OpCode::Type::Trivial) {
+ throw "Invalid opcode used with zero arguments";
+ }
+ full_instruction.instr.opcode = opcode;
+ }
+
+ InlineAsm(OpCode opcode, int src) : type(Regular) {
+ switch (opcode.EffectiveOpCode()) {
+ case OpCode::Id::LOOP:
+// if (src.GetRegisterType() != RegisterType::IntUniform)
+// throw "LOOP argument must be an integer register!";
+
+ reg_id = src;
+ full_instruction.instr.hex = 0;
+ full_instruction.instr.opcode = opcode;
+ break;
+
+ default:
+ throw "Unknown opcode argument";
+ }
+ }
+
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
+ SourceRegister src1, const SwizzleMask swizzle_src1 = SwizzleMask{""}) : type(Regular) {
+ Instruction& instr = full_instruction.instr;
+ instr.hex = 0;
+ instr.opcode = opcode;
+ SwizzlePattern& swizzle = full_instruction.swizzle;
+ swizzle.hex = 0;
+
+ switch(opcode.GetInfo().type) {
+ case OpCode::Type::Arithmetic:
+ // TODO: Assert valid inputs, considering the field width!
+ instr.common.dest = dest;
+ instr.common.src1 = src1;
+ swizzle.negate_src1 = swizzle_src1.negate;
+
+ swizzle.dest_mask = dest_mask.dest_mask;
+ for (int i = 0; i < 4; ++i) {
+ swizzle.SetSelectorSrc1(i, swizzle_src1.selectors[i]);
+ }
+ break;
+
+ default:
+ throw "Unknown inline assmembler command";
+ }
+ }
+
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
+ SourceRegister src1, const SwizzleMask& swizzle_src1,
+ SourceRegister src2, const SwizzleMask& swizzle_src2 = "", RelativeAddress addr = None) : type(Regular) {
+ Instruction& instr = full_instruction.instr;
+ instr.hex = 0;
+ instr.opcode = opcode;
+ SwizzlePattern& swizzle = full_instruction.swizzle;
+ swizzle.hex = 0;
+
+ switch(opcode.GetInfo().type) {
+ case OpCode::Type::Arithmetic:
+ // TODO: Assert valid inputs, considering the field width!
+ instr.common.dest = dest;
+ instr.common.src1 = src1;
+ instr.common.src2 = src2;
+ instr.common.address_register_index = addr;
+ swizzle.negate_src1 = swizzle_src1.negate;
+ swizzle.negate_src2 = swizzle_src2.negate;
+
+ swizzle.dest_mask = dest_mask.dest_mask;
+ for (int i = 0; i < 4; ++i) {
+ swizzle.SetSelectorSrc1(i, swizzle_src1.selectors[i]);
+ swizzle.SetSelectorSrc2(i, swizzle_src2.selectors[i]);
+ }
+ break;
+
+ default:
+ throw "Unknown inline assembler command";
+ }
+ }
+
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
+ SourceRegister src1, const SwizzleMask& swizzle_src1,
+ SourceRegister src2, const SwizzleMask& swizzle_src2,
+ SourceRegister src3, const SwizzleMask& swizzle_src3 = {""}) : type(Regular) {
+ Instruction& instr = full_instruction.instr;
+ instr.hex = 0;
+ instr.opcode = opcode;
+ SwizzlePattern& swizzle = full_instruction.swizzle;
+ swizzle.hex = 0;
+
+ switch(opcode.GetInfo().type) {
+ case OpCode::Type::MultiplyAdd:
+ // TODO: Assert valid inputs, considering the field width!
+ instr.mad.dest = dest;
+ instr.mad.src1 = src1;
+ instr.mad.src2 = src2;
+ instr.mad.src3 = src3;
+ full_instruction.swizzle.negate_src1 = swizzle_src1.negate;
+ full_instruction.swizzle.negate_src2 = swizzle_src2.negate;
+ full_instruction.swizzle.negate_src3 = swizzle_src3.negate;
+
+ swizzle.dest_mask = dest_mask.dest_mask;
+ for (int i = 0; i < 4; ++i) {
+ full_instruction.swizzle.SetSelectorSrc1(i, swizzle_src1.selectors[i]);
+ full_instruction.swizzle.SetSelectorSrc2(i, swizzle_src2.selectors[i]);
+ }
+ break;
+
+ default:
+ throw "Unknown inline assembler command";
+ }
+ }
+
+ // Convenience constructors with implicit swizzle mask
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
+ SourceRegister src1, const SwizzleMask swizzle_src1 = SwizzleMask{ "" }) : InlineAsm(opcode, dest, "", src1, swizzle_src1) {}
+
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
+ SourceRegister src1, const SwizzleMask& swizzle_src1,
+ SourceRegister src2, const SwizzleMask& swizzle_src2 = "") : InlineAsm(opcode, dest, "", src1, swizzle_src1, src2, swizzle_src2) {}
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
+ SourceRegister src1, SourceRegister src2,
+ const SwizzleMask& swizzle_src2 = "") : InlineAsm(opcode, dest, dest_mask, src1, "", src2, swizzle_src2) {}
+
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
+ SourceRegister src1, const SwizzleMask& swizzle_src1,
+ SourceRegister src2, const SwizzleMask& swizzle_src2,
+ SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, "", src1, swizzle_src1, src2, swizzle_src2, src3, swizzle_src3) {}
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
+ SourceRegister src1,
+ SourceRegister src2, const SwizzleMask& swizzle_src2,
+ SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, dest_mask, src1, "", src2, swizzle_src2, src3, swizzle_src3) {}
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
+ SourceRegister src1, const SwizzleMask& swizzle_src1,
+ SourceRegister src2,
+ SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, dest_mask, src1, swizzle_src1, src2, "", src3, swizzle_src3) {}
+
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
+ SourceRegister src1,
+ SourceRegister src2, const SwizzleMask& swizzle_src2,
+ SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, "", src1, "", src2, swizzle_src2, src3, swizzle_src3) {}
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
+ SourceRegister src1, const SwizzleMask& swizzle_src1,
+ SourceRegister src2,
+ SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, "", src1, swizzle_src1, src2, "", src3, swizzle_src3) {}
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
+ SourceRegister src1,
+ SourceRegister src2,
+ SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, dest_mask, src1, "", src2, "", src3, swizzle_src3) {}
+ InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
+ SourceRegister src1,
+ SourceRegister src2,
+ SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, "", src1, "", src2, "", src3, swizzle_src3) {}
+
+ static InlineAsm DeclareOutput(DestRegister reg, OutputRegisterInfo::Type semantic) {
+ if (reg.GetRegisterType() != RegisterType::Output)
+ throw "Invalid register used to declare output";
+
+ InlineAsm ret(Output);
+ ret.output_semantic = semantic;
+ ret.reg_id = reg.GetIndex();
+ return ret;
+ }
+
+ static InlineAsm DeclareConstant(SourceRegister reg, float x, float y, float z, float w) {
+ if (reg.GetRegisterType() != RegisterType::FloatUniform)
+ throw "Invalid source register used to declare shader constant";
+
+ InlineAsm ret(Constant);
+ ret.value[0] = x;
+ ret.value[1] = y;
+ ret.value[2] = z;
+ ret.value[3] = w;
+ ret.constant_type = ConstantInfo::Float;
+ ret.reg_id = reg.GetIndex();
+ return ret;
+ }
+
+ static InlineAsm DeclareUniform(SourceRegister reg_first, SourceRegister reg_last, const std::string& name) {
+ InlineAsm ret(Uniform);
+ ret.reg_id = reg_first.GetIndex();
+ ret.reg_id_last = reg_last.GetIndex();
+ ret.name = name;
+ return ret;
+ }
+
+ // TODO: Group this into a union once MSVC supports unrestricted unions!
+ struct {
+ Instruction instr;
+ SwizzlePattern swizzle;
+ } full_instruction;
+
+ std::string name;
+
+ unsigned reg_id;
+ unsigned reg_id_last;
+
+ OutputRegisterInfo::Type output_semantic;
+
+ ConstantInfo::Type constant_type;
+ float value[4];
+
+ static size_t FindSwizzlePattern(const SwizzlePattern& pattern, std::vector<SwizzlePattern>& swizzle_table) {
+ auto it = std::find_if(swizzle_table.begin(), swizzle_table.end(), [&](const SwizzlePattern& candidate) { return candidate.hex == pattern.hex; });
+ size_t ret = std::distance(swizzle_table.begin(), it);
+
+ if (it == swizzle_table.end())
+ swizzle_table.push_back(pattern);
+
+ return ret;
+ }
+
+ static const ShaderBinary CompileToRawBinary(std::initializer_list<InlineAsm> code_) {
+ ShaderBinary binary;
+ std::vector<InlineAsm> code(code_);
+ for (int i = 0; i < code.size(); ++i) {
+ auto command = code[i];
+
+ switch (command.type) {
+ case Regular:
+ {
+ auto& instr = command.full_instruction.instr;
+
+ switch (instr.opcode.Value().GetInfo().type) {
+ case OpCode::Type::Trivial:
+ break;
+
+ case OpCode::Type::Arithmetic:
+ instr.common.operand_desc_id = FindSwizzlePattern(command.full_instruction.swizzle, binary.swizzle_table);
+ break;
+
+ case OpCode::Type::UniformFlowControl:
+ if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::LOOP) {
+ instr.flow_control.int_uniform_id = command.reg_id;
+ instr.flow_control.dest_offset = binary.program.size();
+
+// std::cout << "Started at "<< binary.program.size() << " <-> "<<i <<std::endl;
+ // TODO: Handle nested LOOPs
+ for (int i2 = i + 1; i2 < code.size(); ++i2) {
+ if (code[i2].type == Regular) {
+ // std::cout << "went at "<< i2 << std::endl;
+ instr.flow_control.dest_offset = instr.flow_control.dest_offset + 1;
+ } else if (code[i2].type == EndLoop) {
+ break;
+ }
+
+ if (i2 == code.size() - 1) {
+ throw "No closing EndLoop directive found";
+ }
+ }
+ } else {
+ throw "Unknown flow control instruction";
+ }
+ break;
+
+ default:
+ throw "Unknown instruction";
+ }
+
+ binary.program.push_back(command.full_instruction.instr);
+ break;
+ }
+
+ case Output:
+ {
+ OutputRegisterInfo output;
+ output.type = command.output_semantic;
+ output.id = command.reg_id;
+ output.component_mask = 0xF; // TODO: Make configurable
+ binary.output_table.push_back(output);
+ break;
+ }
+
+ case Constant:
+ {
+ ConstantInfo constant;
+ constant.type = command.constant_type;
+ constant.regid = command.reg_id;
+
+ switch (command.constant_type) {
+ case ConstantInfo::Float:
+ constant.f.x = to_float24(command.value[0]);
+ constant.f.y = to_float24(command.value[1]);
+ constant.f.z = to_float24(command.value[2]);
+ constant.f.w = to_float24(command.value[3]);
+ break;
+
+ default:
+ throw "Unknown constant type";
+ }
+ binary.constant_table.push_back(constant);
+ break;
+ }
+
+ case Uniform:
+ {
+ UniformInfo uniform;
+ uniform.basic.symbol_offset = binary.symbol_table.size();
+ uniform.basic.reg_start = command.reg_id + 16; // TODO: Hardcoded against float uniforms
+ uniform.basic.reg_end = command.reg_id_last + 16;
+ binary.uniform_table.push_back(uniform);
+
+ std::copy(command.name.begin(), command.name.end(), std::back_inserter(binary.symbol_table));
+ binary.symbol_table.push_back('\0');
+
+ break;
+ }
+
+ case EndLoop:
+ break;
+
+ default:
+ throw "Unknown type";
+ }
+ }
+ return binary;
+ }
+
+ // Overestimates the actual size
+ static const size_t CompiledShbinSize(std::initializer_list<InlineAsm> code) {
+ size_t size = 0;
+ size += sizeof(DVLBHeader);
+ size += sizeof(DVLPHeader);
+ size += sizeof(uint32_t) + sizeof(DVLEHeader); // Currently only one DVLE is supported
+
+ for (const auto& command : code) {
+ switch (command.type) {
+ case Regular:
+ size += sizeof(Instruction);
+ size += sizeof(SwizzleInfo);
+ break;
+
+ case Output:
+ size += sizeof(OutputRegisterInfo);
+ break;
+
+ case Constant:
+ size += sizeof(ConstantInfo);
+ break;
+
+ case Uniform:
+ size += command.name.size() + 1;
+ size += sizeof(UniformInfo);
+ break;
+
+ case EndLoop:
+ break;
+
+ default:
+ throw "Unknown command type";
+ }
+ }
+
+ return size;
+ }
+
+ static const std::vector<uint8_t> CompileToShbin(std::initializer_list<InlineAsm> code) {
+ std::vector<uint8_t> ret(CompiledShbinSize(code));
+
+ ShaderBinary bin = CompileToRawBinary(code);
+
+ struct {
+ DVLBHeader header;
+ uint32_t dvle_offset;
+ } *dvlb = (decltype(dvlb))ret.data();
+ dvlb->header.magic_word = DVLBHeader::MAGIC_WORD;
+ dvlb->header.num_programs = 1;
+
+ unsigned dvlp_offset = sizeof(*dvlb);
+ DVLPHeader* dvlp = (DVLPHeader*)&ret.data()[dvlp_offset];
+ dvlp->magic_word = DVLPHeader::MAGIC_WORD;
+
+ unsigned dvle_offset = dvlb->dvle_offset = dvlp_offset + sizeof(DVLPHeader);
+ DVLEHeader* dvle = (DVLEHeader*)&ret.data()[dvle_offset];
+ dvle->magic_word = DVLEHeader::MAGIC_WORD;
+ dvlb->dvle_offset = dvle_offset;
+
+ unsigned binary_offset = dvle_offset + sizeof(DVLEHeader);
+ dvlp->binary_offset = binary_offset - dvlp_offset;
+ dvlp->binary_size_words = bin.program.size();
+ std::copy(bin.program.begin(), bin.program.end(), (Instruction*)&ret.data()[binary_offset]);
+
+ unsigned swizzle_table_offset = binary_offset + bin.program.size() * sizeof(Instruction);
+ dvlp->swizzle_info_offset = swizzle_table_offset - dvlp_offset;
+ dvlp->swizzle_info_num_entries = bin.swizzle_table.size();
+ SwizzleInfo* swizzle_table_ptr = (SwizzleInfo*)&ret.data()[swizzle_table_offset];
+ for (const auto& swizzle : bin.swizzle_table) {
+ swizzle_table_ptr->pattern = swizzle;
+ swizzle_table_ptr->unknown = 0;
+ swizzle_table_ptr++;
+ }
+
+ unsigned output_table_offset = swizzle_table_offset + bin.swizzle_table.size() * sizeof(SwizzleInfo);
+ OutputRegisterInfo* output_table_ptr = (OutputRegisterInfo*)&ret.data()[output_table_offset];
+ for (const auto& output : bin.output_table) {
+ *output_table_ptr = output;
+ output_table_ptr++;
+ }
+ dvle->output_register_table_offset = output_table_offset - dvle_offset;
+ dvle->output_register_table_size = bin.output_table.size();
+
+ unsigned constant_table_offset = output_table_offset + bin.output_table.size() * sizeof(OutputRegisterInfo);
+ ConstantInfo* constant_table_ptr = (ConstantInfo*)&ret.data()[constant_table_offset];
+ for (const auto& constant : bin.constant_table) {
+ *constant_table_ptr = constant;
+ constant_table_ptr++;
+ }
+ dvle->constant_table_offset = constant_table_offset - dvle_offset;
+ dvle->constant_table_size = bin.constant_table.size();
+
+ // TODO: UniformTable spans more than the written data.. fix this design issue :/
+ unsigned uniform_table_offset = constant_table_offset + bin.constant_table.size() * sizeof(ConstantInfo);
+ uint64_t* uniform_table_ptr = (uint64_t*)&ret.data()[uniform_table_offset];
+ for (const auto& uniform : bin.uniform_table) {
+ *uniform_table_ptr = reinterpret_cast<const uint64_t&>(uniform.basic);
+ uniform_table_ptr++;
+ }
+ dvle->uniform_table_offset = uniform_table_offset - dvle_offset;
+ dvle->uniform_table_size = bin.uniform_table.size();
+
+ unsigned symbol_table_offset = uniform_table_offset + bin.uniform_table.size() * sizeof(uint64_t);
+ std::copy(bin.symbol_table.begin(), bin.symbol_table.end(), &ret.data()[symbol_table_offset]);
+ dvle->symbol_table_offset = symbol_table_offset - dvle_offset;
+ dvle->symbol_table_size = bin.symbol_table.size();
+
+ ret.resize(symbol_table_offset + bin.symbol_table.size());
+
+ return ret;
+ }
+};
+
+} // namespace
--- /dev/null
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <memory>
+#include <vector>
+#include <ostream>
+
+#include <boost/optional.hpp>
+#include <boost/variant.hpp>
+
+#include "source_tree.h"
+
+#include "shader_binary.h"
+#include "shader_bytecode.h"
+
+namespace nihstro {
+
+struct InputSwizzlerMask {
+ int num_components;
+
+ enum Component : uint8_t {
+ x = 0,
+ y = 1,
+ z = 2,
+ w = 3,
+ };
+ std::array<Component,4> components;
+
+ static InputSwizzlerMask FullMask() {
+ return { 4, {x,y,z,w} };
+ }
+
+ bool operator == (const InputSwizzlerMask& oth) const {
+ return this->num_components == oth.num_components && this->components == oth.components;
+ }
+
+ // TODO: Move to implementation?
+ friend std::ostream& operator<<(std::ostream& os, const Component& v) {
+ switch(v) {
+ case x: return os << "x";
+ case y: return os << "y";
+ case z: return os << "z";
+ case w: return os << "w";
+ default: return os << "?";
+ }
+ }
+ friend std::ostream& operator<<(std::ostream& os, const InputSwizzlerMask& v) {
+ if (!v.num_components)
+ return os << "(empty_mask)";
+
+ for (int i = 0; i < v.num_components; ++i)
+ os << v.components[i];
+
+ return os;
+ }
+};
+
+using Identifier = std::string;
+
+// A sign, i.e. +1 or -1
+using Sign = int;
+
+struct IntegerWithSign {
+ int sign;
+ unsigned value;
+
+ int GetValue() const {
+ return sign * value;
+ }
+};
+
+// Raw index + address register index
+struct IndexExpression : std::vector<boost::variant<IntegerWithSign, Identifier>> {
+ int GetCount() const {
+ return this->size();
+ }
+
+ bool IsRawIndex(int arg) const {
+ return (*this)[arg].which() == 0;
+ }
+
+ int GetRawIndex(int arg) const {
+ return boost::get<IntegerWithSign>((*this)[arg]).GetValue();
+ }
+
+ bool IsAddressRegisterIdentifier(int arg) const {
+ return (*this)[arg].which() == 1;
+ }
+
+ Identifier GetAddressRegisterIdentifier(int arg) const {
+ return boost::get<Identifier>((*this)[arg]);
+ }
+};
+
+
+struct Expression {
+ struct SignedIdentifier {
+ boost::optional<Sign> sign;
+ Identifier identifier;
+ } signed_identifier;
+
+ boost::optional<IndexExpression> index;
+ std::vector<InputSwizzlerMask> swizzle_masks;
+
+ int GetSign() const {
+ if (!RawSign())
+ return +1;
+ else
+ return *RawSign();
+ }
+
+ const Identifier& GetIdentifier() const {
+ return RawIdentifier();
+ }
+
+ bool HasIndexExpression() const {
+ return static_cast<bool>(RawIndex());
+ }
+
+ const IndexExpression& GetIndexExpression() const {
+ return *RawIndex();
+ }
+
+ const std::vector<InputSwizzlerMask>& GetSwizzleMasks() const {
+ return RawSwizzleMasks();
+ }
+
+private:
+ const boost::optional<Sign>& RawSign() const {
+ return signed_identifier.sign;
+ }
+
+ const Identifier& RawIdentifier() const {
+ return signed_identifier.identifier;
+ }
+
+ const boost::optional<IndexExpression>& RawIndex() const {
+ return index;
+ }
+
+ const std::vector<InputSwizzlerMask>& RawSwizzleMasks() const {
+ return swizzle_masks;
+ }
+};
+
+struct ConditionInput {
+ bool invert;
+ Identifier identifier;
+ boost::optional<InputSwizzlerMask> swizzler_mask;
+
+ bool GetInvertFlag() const {
+ return invert;
+ }
+
+ const Identifier& GetIdentifier() const {
+ return identifier;
+ }
+
+ bool HasSwizzleMask() const {
+ return static_cast<bool>(swizzler_mask);
+ }
+
+ const InputSwizzlerMask& GetSwizzleMask() const {
+ return *swizzler_mask;
+ }
+};
+
+struct Condition {
+ ConditionInput input1;
+ Instruction::FlowControlType::Op op;
+ ConditionInput input2;
+
+ const ConditionInput& GetFirstInput() const {
+ return input1;
+ }
+
+ Instruction::FlowControlType::Op GetConditionOp() const {
+ return op;
+ }
+
+ const ConditionInput& GetSecondInput() const {
+ return input2;
+ }
+};
+
+using StatementLabel = std::string;
+
+struct StatementInstruction {
+ OpCode opcode;
+ std::vector<Expression> expressions;
+
+ StatementInstruction() = default;
+
+ // TODO: Obsolete constructor?
+ StatementInstruction(const OpCode& opcode) : opcode(opcode) {
+ }
+
+ StatementInstruction(const OpCode& opcode, const std::vector<Expression> expressions) : opcode(opcode), expressions(expressions) {
+ }
+
+ const OpCode& GetOpCode() const {
+ return opcode;
+ }
+
+ const std::vector<Expression>& GetArguments() const {
+ return expressions;
+ }
+};
+using FloatOpInstruction = StatementInstruction;
+
+struct CompareInstruction {
+ OpCode opcode;
+ std::vector<Expression> arguments;
+ std::vector<Instruction::Common::CompareOpType::Op> ops;
+
+ const OpCode& GetOpCode() const {
+ return opcode;
+ }
+
+ const Expression& GetSrc1() const {
+ return arguments[0];
+ }
+
+ const Expression& GetSrc2() const {
+ return arguments[1];
+ }
+
+ Instruction::Common::CompareOpType::Op GetOp1() const {
+ return ops[0];
+ }
+
+ Instruction::Common::CompareOpType::Op GetOp2() const {
+ return ops[1];
+ }
+};
+
+struct FlowControlInstruction {
+ OpCode opcode;
+ std::string target_label;
+ boost::optional<std::string> return_label;
+ boost::optional<Condition> condition;
+
+ const OpCode& GetOpCode() const {
+ return opcode;
+ }
+
+ const std::string& GetTargetLabel() const {
+ return target_label;
+ }
+
+ bool HasReturnLabel() const {
+ return static_cast<bool>(return_label);
+ }
+
+ const std::string& GetReturnLabel() const {
+ return *return_label;
+ }
+
+ bool HasCondition() const {
+ return static_cast<bool>(condition);
+ }
+
+ const Condition& GetCondition() const {
+ return *condition;
+ }
+};
+
+struct SetEmitInstruction {
+ OpCode opcode;
+ unsigned vertex_id;
+
+ struct Flags {
+ boost::optional<bool> primitive_flag;
+ boost::optional<bool> invert_flag;
+ } flags;
+
+ bool PrimitiveFlag() const {
+ return flags.primitive_flag && *flags.primitive_flag;
+ }
+
+ bool InvertFlag() const {
+ return flags.invert_flag && *flags.invert_flag;
+ }
+};
+
+struct StatementDeclaration {
+ std::string alias_name;
+ Identifier identifier_start; /* aliased identifier (start register) */
+ boost::optional<Identifier> identifier_end; /* aliased identifier (end register) */
+ boost::optional<InputSwizzlerMask> swizzle_mask; // referring to the aliased identifier
+
+ struct Extra {
+ std::vector<float> constant_value;
+ boost::optional<OutputRegisterInfo::Type> output_semantic;
+ } extra;
+};
+
+struct ParserContext {
+ // There currently is no context
+};
+
+
+struct Parser {
+ using Iterator = SourceTreeIterator;
+
+ Parser(const ParserContext& context);
+ ~Parser();
+
+ // Skip whitespaces, blank lines, and comments; returns number of line breaks skipped.
+ unsigned Skip(Iterator& begin, Iterator end);
+
+ // Skip to the next line
+ void SkipSingleLine(Iterator& begin, Iterator end);
+
+ // Parse alias declaration including line ending
+ bool ParseDeclaration(Iterator& begin, Iterator end, StatementDeclaration* declaration);
+
+ // Parse label declaration including line ending
+ bool ParseLabel(Iterator& begin, Iterator end, StatementLabel* label);
+
+ // Parse nothing but a single opcode
+ bool ParseOpCode(Iterator& begin, Iterator end, OpCode* opcode);
+
+ // Parse trival instruction including line ending
+ bool ParseSimpleInstruction(Iterator& begin, Iterator end, OpCode* opcode);
+
+ // Parse float instruction including line ending
+ bool ParseFloatOp(Iterator& begin, Iterator end, FloatOpInstruction* content);
+
+ // Parse compare instruction including line ending
+ bool ParseCompare(Iterator& begin, Iterator end, CompareInstruction* content);
+
+ // Parse flow control instruction including line ending
+ bool ParseFlowControl(Iterator& begin, Iterator end, FlowControlInstruction* content);
+
+ // Parse SetEmit instruction including line ending
+ bool ParseSetEmit(Iterator& begin, Iterator end, SetEmitInstruction* content);
+
+private:
+ struct ParserImpl;
+ std::unique_ptr<ParserImpl> impl;
+};
+
+} // namespace
--- /dev/null
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+// Enable this for detailed XML overview of parser results
+// #define BOOST_SPIRIT_DEBUG
+
+#include <boost/fusion/include/adapt_struct.hpp>
+#include <boost/spirit/include/qi.hpp>
+
+#include "nihstro/parser_assembly.h"
+#include "nihstro/source_tree.h"
+
+#include "nihstro/shader_binary.h"
+#include "nihstro/shader_bytecode.h"
+
+namespace spirit = boost::spirit;
+namespace qi = boost::spirit::qi;
+namespace ascii = boost::spirit::qi::ascii;
+namespace phoenix = boost::phoenix;
+
+using spirit::_1;
+using spirit::_2;
+using spirit::_3;
+using spirit::_4;
+
+using namespace nihstro;
+
+// Adapt common parser data structures for use with boost::spirit
+
+BOOST_FUSION_ADAPT_STRUCT(
+ Expression::SignedIdentifier,
+ (boost::optional<Sign>, sign)
+ (Identifier, identifier)
+)
+
+BOOST_FUSION_ADAPT_STRUCT(
+ Expression,
+ (Expression::SignedIdentifier, signed_identifier)
+ (boost::optional<IndexExpression>, index)
+ (std::vector<InputSwizzlerMask>, swizzle_masks)
+)
+
+class Diagnostics
+{
+public:
+ // Ass a new diagnostic message corresponding to the specified rule tag
+ void Add(const std::string& tag, const char* diagnostic) {
+ entries[tag] = diagnostic;
+ }
+
+ // Lookup the diagnostic of the specified rule tag and return it (or nullptr if it can't be found)
+ const char* operator [](const char* tag) const {
+ auto it = entries.find(tag);
+ if (it == entries.end())
+ return nullptr;
+ else
+ return it->second;
+ }
+
+private:
+ std::map<std::string, const char*> entries;
+};
+
+struct ErrorHandler
+{
+ template <class, class, class, class, class>
+ struct result { typedef void type; };
+
+ template <class D, class B, class E, class W, class I>
+ void operator ()(const D& diagnostics, B begin, E end, W where, const I& info) const
+ {
+ const spirit::utf8_string& tag(info.tag);
+ const char* const what(tag.c_str());
+ const char* diagnostic(diagnostics[what]);
+ std::string scratch;
+ if (!diagnostic) {
+ scratch.reserve(25 + tag.length());
+ scratch = "Expected ";
+ scratch += tag;
+ diagnostic = scratch.c_str();
+ }
+
+ auto newline_iterator = std::find(begin, end, '\n');
+
+ std::stringstream err;
+ err << diagnostic << std::endl
+ << std::string(4, ' ') << std::string(begin, newline_iterator) << std::endl
+ << std::string(4 + std::distance(begin, where), ' ') << '^' << std::endl;
+ throw err.str();
+ }
+};
+extern phoenix::function<ErrorHandler> error_handler;
+
+template<typename Iterator>
+struct AssemblySkipper : public qi::grammar<Iterator> {
+
+ AssemblySkipper() : AssemblySkipper::base_type(skip) {
+ comments = qi::char_("//") >> *(qi::char_ - qi::eol);
+
+ skip = +(comments | ascii::blank);
+ }
+
+ qi::rule<Iterator> comments;
+ qi::rule<Iterator> skip;
+};
+
+namespace std {
+
+static std::ostream& operator<<(std::ostream& os, const OpCode& opcode) {
+ // TODO: Should print actual opcode here..
+ return os << static_cast<uint32_t>(static_cast<OpCode::Id>(opcode));
+}
+
+}
+
+template<typename Iterator>
+struct CommonRules {
+ using Skipper = AssemblySkipper<Iterator>;
+
+ CommonRules(const ParserContext& context);
+
+ // Rule-ified symbols, which can be assigned names
+ qi::rule<Iterator, Skipper> peek_identifier;
+
+ // Building blocks
+ qi::rule<Iterator, std::string(), Skipper> identifier;
+ qi::rule<Iterator, Expression(), Skipper> expression;
+ qi::rule<Iterator, Skipper> end_of_statement;
+
+ qi::symbols<char, OpCode> opcodes_trivial;
+ qi::symbols<char, OpCode> opcodes_compare;
+ std::array<qi::symbols<char, OpCode>, 4> opcodes_float; // indexed by number of arguments
+ std::array<qi::symbols<char, OpCode>, 2> opcodes_flowcontrol;
+ qi::symbols<char, OpCode> opcodes_setemit;
+
+ qi::symbols<char, int> signs;
+
+ qi::symbols<char, InputSwizzlerMask::Component> swizzlers;
+ qi::rule<Iterator, InputSwizzlerMask(), Skipper> swizzle_mask;
+
+ Diagnostics diagnostics;
+
+private:
+ qi::rule<Iterator, IndexExpression(), Skipper> index_expression;
+ qi::rule<Iterator, boost::variant<IntegerWithSign, Identifier>(), Skipper> index_expression_first_term;
+ qi::rule<Iterator, boost::variant<IntegerWithSign, Identifier>(), Skipper> index_expression_following_terms;
+
+ // Empty rule
+ qi::rule<Iterator, Skipper> opening_bracket;
+ qi::rule<Iterator, Skipper> closing_bracket;
+ qi::rule<Iterator, IntegerWithSign(), Skipper> sign_with_uint;
+ qi::rule<Iterator, unsigned int(), Skipper> uint_after_sign;
+};
+
+template<typename Iterator, bool require_end_of_line>
+struct TrivialOpParser : qi::grammar<Iterator, OpCode(), AssemblySkipper<Iterator>> {
+ using Skipper = AssemblySkipper<Iterator>;
+
+ TrivialOpParser(const ParserContext& context);
+
+ CommonRules<Iterator> common;
+
+ qi::symbols<char, OpCode>& opcodes_trivial;
+ qi::symbols<char, OpCode>& opcodes_compare;
+ std::array<qi::symbols<char, OpCode>, 4>& opcodes_float; // indexed by number of arguments
+ std::array<qi::symbols<char, OpCode>, 2>& opcodes_flowcontrol;
+
+ // Rule-ified symbols, which can be assigned names
+ qi::rule<Iterator, OpCode(), Skipper> opcode;
+
+ // Compounds
+ qi::rule<Iterator, OpCode(), Skipper> trivial_instruction;
+ qi::rule<Iterator, Skipper>& end_of_statement;
+
+ Diagnostics diagnostics;
+};
+
+template<typename Iterator>
+struct FloatOpParser : qi::grammar<Iterator, FloatOpInstruction(), AssemblySkipper<Iterator>> {
+ using Skipper = AssemblySkipper<Iterator>;
+
+ FloatOpParser(const ParserContext& context);
+
+ CommonRules<Iterator> common;
+
+ std::array<qi::symbols<char, OpCode>, 4>& opcodes_float;
+
+ // Rule-ified symbols, which can be assigned names
+ qi::rule<Iterator, OpCode(), Skipper> opcode[4];
+
+ // Building blocks
+ qi::rule<Iterator, Expression(), Skipper>& expression;
+ qi::rule<Iterator, std::vector<Expression>(), Skipper> expression_chain[4]; // sequence of instruction arguments
+ qi::rule<Iterator, Skipper>& end_of_statement;
+
+ // Compounds
+ qi::rule<Iterator, FloatOpInstruction(), Skipper> float_instr[4];
+ qi::rule<Iterator, FloatOpInstruction(), Skipper> float_instruction;
+
+ // Utility
+ qi::rule<Iterator, Skipper> not_comma;
+
+ Diagnostics diagnostics;
+};
+
+template<typename Iterator>
+struct CompareParser : qi::grammar<Iterator, CompareInstruction(), AssemblySkipper<Iterator>> {
+ using Skipper = AssemblySkipper<Iterator>;
+ using CompareOp = Instruction::Common::CompareOpType;
+ using CompareOpEnum = CompareOp::Op;
+
+ CompareParser(const ParserContext& context);
+
+ CommonRules<Iterator> common;
+
+ qi::symbols<char, OpCode>& opcodes_compare;
+ qi::symbols<char, CompareOpEnum> compare_ops;
+
+ // Rule-ified symbols, which can be assigned debug names
+ qi::rule<Iterator, OpCode(), Skipper> opcode;
+ qi::rule<Iterator, CompareOpEnum(), Skipper> compare_op;
+ qi::rule<Iterator, std::vector<CompareOpEnum>(), Skipper> two_ops;
+
+ // Building blocks
+ qi::rule<Iterator, Expression(), Skipper>& expression;
+ qi::rule<Iterator, std::vector<Expression>(), Skipper> two_expressions;
+ qi::rule<Iterator, Skipper>& end_of_statement;
+
+ // Compounds
+ qi::rule<Iterator, CompareInstruction(), Skipper> instr[1];
+ qi::rule<Iterator, CompareInstruction(), Skipper> instruction;
+
+ // Utility
+ qi::rule<Iterator, Skipper> not_comma;
+
+ Diagnostics diagnostics;
+};
+
+template<typename Iterator>
+struct FlowControlParser : qi::grammar<Iterator, FlowControlInstruction(), AssemblySkipper<Iterator>> {
+ using Skipper = AssemblySkipper<Iterator>;
+ using ConditionOp = Instruction::FlowControlType;
+ using ConditionOpEnum = Instruction::FlowControlType::Op;
+
+ FlowControlParser(const ParserContext& context);
+
+ CommonRules<Iterator> common;
+
+ std::array<qi::symbols<char, OpCode>, 2>& opcodes_flowcontrol;
+ qi::symbols<char, ConditionOpEnum> condition_ops;
+
+ // Rule-ified symbols, which can be assigned debug names
+ qi::rule<Iterator, OpCode(), Skipper> opcode[2];
+ qi::rule<Iterator, ConditionOpEnum(), Skipper> condition_op;
+
+ // Building blocks
+ qi::rule<Iterator, Expression(), Skipper>& expression;
+ qi::rule<Iterator, std::string(), Skipper>& identifier;
+ qi::rule<Iterator, InputSwizzlerMask(), Skipper>& swizzle_mask;
+ qi::rule<Iterator, ConditionInput(), Skipper> condition_input;
+ qi::rule<Iterator, Condition(), Skipper> condition;
+ qi::rule<Iterator, Skipper>& end_of_statement;
+
+ // Compounds
+ qi::rule<Iterator, FlowControlInstruction(), Skipper> instr[2];
+ qi::rule<Iterator, FlowControlInstruction(), Skipper> flow_control_instruction;
+
+ // Utility
+ qi::rule<Iterator, Skipper> not_comma;
+ qi::rule<Iterator, bool(), Skipper> negation;
+
+ Diagnostics diagnostics;
+};
+
+template<typename Iterator>
+struct SetEmitParser : qi::grammar<Iterator, SetEmitInstruction(), AssemblySkipper<Iterator>> {
+ using Skipper = AssemblySkipper<Iterator>;
+
+ SetEmitParser(const ParserContext& context);
+
+ CommonRules<Iterator> common;
+
+ qi::symbols<char, OpCode>& opcodes_setemit;
+
+ // Rule-ified symbols, which can be assigned debug names
+ qi::rule<Iterator, OpCode(), Skipper> opcode;
+ qi::rule<Iterator, unsigned int(), Skipper> vertex_id;
+ qi::rule<Iterator, bool(), Skipper> prim_flag;
+ qi::rule<Iterator, bool(), Skipper> inv_flag;
+ qi::rule<Iterator, SetEmitInstruction::Flags(), Skipper> flags;
+
+ // Building blocks
+ qi::rule<Iterator, Skipper>& end_of_statement;
+
+ // Compounds
+ qi::rule<Iterator, SetEmitInstruction(), Skipper> setemit_instruction;
+
+ // Utility
+ qi::rule<Iterator, Skipper> not_comma;
+ qi::rule<Iterator, bool(), Skipper> negation;
+
+ Diagnostics diagnostics;
+};
+
+template<typename Iterator>
+struct LabelParser : qi::grammar<Iterator, StatementLabel(), AssemblySkipper<Iterator>> {
+ using Skipper = AssemblySkipper<Iterator>;
+
+ LabelParser(const ParserContext& context);
+
+ CommonRules<Iterator> common;
+
+ qi::rule<Iterator, Skipper>& end_of_statement;
+
+ qi::rule<Iterator, std::string(), Skipper>& identifier;
+ qi::rule<Iterator, std::string(), Skipper> label;
+
+ Diagnostics diagnostics;
+};
+
+template<typename Iterator>
+struct DeclarationParser : qi::grammar<Iterator, StatementDeclaration(), AssemblySkipper<Iterator>> {
+ using Skipper = AssemblySkipper<Iterator>;
+
+ DeclarationParser(const ParserContext& context);
+
+ CommonRules<Iterator> common;
+
+ qi::rule<Iterator, Skipper> string_as;
+ qi::rule<Iterator, std::vector<float>(), Skipper> dummy_const;
+ qi::rule<Iterator, boost::optional<OutputRegisterInfo::Type>(), Skipper> dummy_semantic;
+
+ qi::symbols<char, OutputRegisterInfo::Type> output_semantics;
+
+ // Rule-ified symbols, which can be assigned names
+ qi::rule<Iterator, OutputRegisterInfo::Type(),Skipper> output_semantics_rule;
+
+ // Building blocks
+ qi::rule<Iterator, std::string(), Skipper>& identifier;
+ qi::rule<Iterator, InputSwizzlerMask(), Skipper>& swizzle_mask;
+ qi::rule<Iterator, std::vector<float>(), Skipper> constant;
+ qi::rule<Iterator, std::string(), Skipper> alias_identifier;
+ qi::rule<Iterator, StatementDeclaration::Extra(), Skipper> const_or_semantic;
+ qi::rule<Iterator, Skipper>& end_of_statement;
+
+ qi::rule<Iterator, StatementDeclaration(), Skipper> declaration;
+ Diagnostics diagnostics;
+};
+
+using ParserIterator = SourceTreeIterator;
--- /dev/null
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <fstream>
+#include <map>
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "nihstro/shader_binary.h"
+
+namespace nihstro {
+
+struct ShaderInfo {
+ std::vector<Instruction> code;
+ std::vector<SwizzleInfo> swizzle_info;
+
+ std::vector<ConstantInfo> constant_table;
+ std::vector<LabelInfo> label_table;
+ std::map<uint32_t, std::string> labels;
+ std::vector<OutputRegisterInfo> output_register_info;
+ std::vector<UniformInfo> uniform_table;
+
+ void Clear() {
+ code.clear();
+ swizzle_info.clear();
+ constant_table.clear();
+ label_table.clear();
+ labels.clear();
+ output_register_info.clear();
+ uniform_table.clear();
+ }
+
+ bool HasLabel(uint32_t offset) const {
+ return labels.find(offset) != labels.end();
+ }
+
+ std::string GetLabel (uint32_t offset) const {
+ auto it = labels.find(offset);
+ if (it != labels.end())
+ return it->second;
+ return "";
+ }
+
+ template<typename T>
+ std::string LookupDestName(const T& dest, const SwizzlePattern& swizzle) const {
+ if (dest < 0x8) {
+ // TODO: This one still needs some prettification in case
+ // multiple output_infos describing this output register
+ // are found.
+ std::string ret;
+ for (const auto& output_info : output_register_info) {
+ if (dest != output_info.id)
+ continue;
+
+ // Only display output register name if the output components it's mapped to are
+ // actually written to.
+ // swizzle.dest_mask and output_info.component_mask use different bit order,
+ // so we can't use AND them bitwise to check this.
+ int matching_mask = 0;
+ for (int i = 0; i < 4; ++i)
+ matching_mask |= output_info.component_mask & (swizzle.DestComponentEnabled(i) << i);
+
+ if (!matching_mask)
+ continue;
+
+ // Add a vertical bar so that we have at least *some*
+ // indication that we hit multiple matches.
+ if (!ret.empty())
+ ret += "|";
+
+ ret += output_info.GetSemanticName();
+ }
+ if (!ret.empty())
+ return ret;
+ } else if (dest.GetRegisterType() == RegisterType::Temporary) {
+ // TODO: Not sure if uniform_info can assign names to temporary registers.
+ // If that is the case, we should check the table for better names here.
+ std::stringstream stream;
+ stream << "temp_" << std::hex << dest.GetIndex();
+ return stream.str();
+ }
+ return "(?)";
+ }
+
+ template<class T>
+ std::string LookupSourceName(const T& source, unsigned addr_reg_index) const {
+ if (source.GetRegisterType() != RegisterType::Temporary) {
+ for (const auto& uniform_info : uniform_table) {
+ // Magic numbers are needed because uniform info registers use the
+ // range 0..0x10 for input registers and 0x10...0x70 for uniform registers,
+ // i.e. there is a "gap" at the temporary registers, for which no
+ // name can be assigned (?).
+ int off = (source.GetRegisterType() == RegisterType::Input) ? 0 : 0x10;
+ if (source - off >= uniform_info.basic.reg_start &&
+ source - off <= uniform_info.basic.reg_end) {
+ std::string name = uniform_info.name;
+
+ std::string index;
+ bool is_array = uniform_info.basic.reg_end != uniform_info.basic.reg_start;
+ if (is_array) {
+ index += std::to_string(source - off - uniform_info.basic.reg_start);
+ }
+ if (addr_reg_index != 0) {
+ index += (is_array) ? " + " : "";
+ index += "a" + std::to_string(addr_reg_index - 1);
+ }
+
+ if (!index.empty())
+ name += "[" + index + "]";
+
+ return name;
+ }
+ }
+ }
+
+ // Constants and uniforms really are the same internally
+ for (const auto& constant_info : constant_table) {
+ if (source - 0x20 == constant_info.regid) {
+ return "const_" + std::to_string(constant_info.regid.Value());
+ }
+ }
+
+ // For temporary registers, we at least print "temp_X" if no better name could be found.
+ if (source.GetRegisterType() == RegisterType::Temporary) {
+ std::stringstream stream;
+ stream << "temp_" << std::hex << source.GetIndex();
+ return stream.str();
+ }
+
+ return "(?)";
+ }
+};
+
+class ShbinParser {
+public:
+ void ReadHeaders(const std::string& filename);
+
+ void ReadDVLE(int dvle_index);
+
+ const DVLBHeader& GetDVLBHeader() const {
+ return dvlb_header;
+ }
+
+ const DVLPHeader& GetDVLPHeader() const {
+ return dvlp_header;
+ }
+
+ const DVLEHeader& GetDVLEHeader(int index) const {
+ return dvle_headers[index];
+ }
+
+ const std::string& GetFilename(int dvle_index) const {
+ return dvle_filenames[dvle_index];
+ }
+
+private:
+
+ // Reads a null-terminated string from the given offset
+ std::string ReadSymbol(uint32_t offset);
+
+ std::fstream file;
+
+
+ DVLBHeader dvlb_header;
+ DVLPHeader dvlp_header;
+
+ uint32_t dvlp_offset;
+
+public:
+ std::vector<uint32_t> dvle_offsets;
+ std::vector<DVLEHeader> dvle_headers;
+ std::vector<std::string> dvle_filenames;
+
+ ShaderInfo shader_info;
+
+ uint32_t main_offset;
+};
+
+
+} // namespace
--- /dev/null
+// Copyright 2015 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+namespace nihstro {
+
+struct SourceTree;
+
+SourceTree PreprocessAssemblyFile(const std::string& filename);
+
+} // namespace
--- /dev/null
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <cstdint>
+
+#include "shader_bytecode.h"
+
+namespace nihstro {
+
+#pragma pack(1)
+struct DVLBHeader {
+ enum : uint32_t {
+ MAGIC_WORD = 0x424C5644, // "DVLB"
+ };
+
+ uint32_t magic_word;
+ uint32_t num_programs;
+
+ // DVLE offset table with num_programs entries follows
+};
+static_assert(sizeof(DVLBHeader) == 0x8, "Incorrect structure size");
+
+struct DVLPHeader {
+ enum : uint32_t {
+ MAGIC_WORD = 0x504C5644, // "DVLP"
+ };
+
+ uint32_t magic_word;
+ uint32_t version;
+ uint32_t binary_offset; // relative to DVLP start
+ uint32_t binary_size_words;
+ uint32_t swizzle_info_offset;
+ uint32_t swizzle_info_num_entries;
+ uint32_t filename_symbol_offset;
+};
+static_assert(sizeof(DVLPHeader) == 0x1C, "Incorrect structure size");
+
+struct DVLEHeader {
+ enum : uint32_t {
+ MAGIC_WORD = 0x454c5644, // "DVLE"
+ };
+
+ enum class ShaderType : uint8_t {
+ VERTEX = 0,
+ GEOMETRY = 1,
+ };
+
+ uint32_t magic_word;
+ uint16_t pad1;
+ ShaderType type;
+ uint8_t pad2;
+
+ // Offset within binary blob to program entry point
+ uint32_t main_offset_words;
+ uint32_t endmain_offset_words;
+
+ uint32_t pad3;
+ uint32_t pad4;
+
+ // Table of constant values for single registers
+ uint32_t constant_table_offset;
+ uint32_t constant_table_size; // number of entries
+
+ // Table of program code labels
+ uint32_t label_table_offset;
+ uint32_t label_table_size;
+
+ // Table of output registers and their semantics
+ uint32_t output_register_table_offset;
+ uint32_t output_register_table_size;
+
+ // Table of uniforms (which may span multiple registers) and their values
+ uint32_t uniform_table_offset;
+ uint32_t uniform_table_size;
+
+ // Table of null-terminated strings referenced by the tables above
+ uint32_t symbol_table_offset;
+ uint32_t symbol_table_size;
+
+};
+static_assert(sizeof(DVLEHeader) == 0x40, "Incorrect structure size");
+
+
+struct SwizzleInfo {
+ SwizzlePattern pattern;
+ uint32_t unknown;
+};
+
+struct ConstantInfo {
+ enum Type : uint32_t {
+ Bool = 0,
+ Int = 1,
+ Float = 2
+ };
+
+ union {
+ uint32_t full_first_word;
+
+ BitField<0, 2, Type> type;
+
+ BitField<16, 8, uint32_t> regid;
+ };
+
+ union {
+ uint32_t value_hex[4];
+
+ BitField<0, 1, uint32_t> b;
+
+ struct {
+ uint8_t x;
+ uint8_t y;
+ uint8_t z;
+ uint8_t w;
+ } i;
+
+ struct {
+ // All of these are float24 values!
+ uint32_t x;
+ uint32_t y;
+ uint32_t z;
+ uint32_t w;
+ } f;
+ };
+};
+
+struct LabelInfo {
+ BitField<0, 8, uint32_t> id;
+ uint32_t program_offset;
+ uint32_t unk;
+ uint32_t name_offset;
+};
+
+union OutputRegisterInfo {
+ enum Type : uint64_t {
+ POSITION = 0,
+ QUATERNION = 1,
+ COLOR = 2,
+ TEXCOORD0 = 3,
+
+ TEXCOORD1 = 5,
+ TEXCOORD2 = 6,
+
+ VIEW = 8,
+ };
+
+ OutputRegisterInfo& operator =(const OutputRegisterInfo& oth) {
+ hex.Assign(oth.hex);
+ return *this;
+ }
+
+ BitField< 0, 64, uint64_t> hex;
+
+ BitField< 0, 16, Type> type;
+ BitField<16, 16, uint64_t> id;
+ BitField<32, 4, uint64_t> component_mask;
+ BitField<32, 32, uint64_t> descriptor;
+
+ const std::string GetMask() const {
+ std::string ret;
+ if (component_mask & 1) ret += "x";
+ if (component_mask & 2) ret += "y";
+ if (component_mask & 4) ret += "z";
+ if (component_mask & 8) ret += "w";
+ return ret;
+ }
+
+ const std::string GetSemanticName() const {
+ static const std::map<Type, std::string> map = {
+ { POSITION, "out.pos" },
+ { QUATERNION, "out.quat" },
+ { COLOR, "out.col" },
+ { TEXCOORD0, "out.tex0" },
+ { TEXCOORD1, "out.tex1" },
+ { TEXCOORD2, "out.tex2" },
+ { VIEW, "out.view" }
+ };
+ auto it = map.find(type);
+ if (it != map.end())
+ return it->second;
+ else
+ return "out.unk";
+ }
+};
+
+struct UniformInfo {
+ struct {
+ static RegisterType GetType(uint32_t reg) {
+ if (reg < 0x10) return RegisterType::Input;
+ else if (reg < 0x70) return RegisterType::FloatUniform;
+ else if (reg < 0x74) return RegisterType::IntUniform;
+ else if (reg >= 0x78 && reg < 0x88) return RegisterType::BoolUniform;
+ else return RegisterType::Unknown;
+ }
+
+ static int GetIndex(uint32_t reg) {
+ switch (GetType(reg)) {
+ case RegisterType::Input: return reg;
+ case RegisterType::FloatUniform: return reg - 0x10;
+ case RegisterType::IntUniform: return reg - 0x70;
+ case RegisterType::BoolUniform: return reg - 0x78;
+ default: return -1;
+ }
+ }
+
+ RegisterType GetStartType() const {
+ return GetType(reg_start);
+ }
+
+ RegisterType GetEndType() const {
+ return GetType(reg_end);
+ }
+
+ int GetStartIndex() const {
+ return GetIndex(reg_start);
+ }
+
+ int GetEndIndex() const {
+ return GetIndex(reg_end);
+ }
+
+ uint32_t symbol_offset;
+ union {
+ BitField< 0, 16, uint32_t> reg_start;
+ BitField<16, 16, uint32_t> reg_end; // inclusive
+ };
+ } basic;
+ std::string name;
+};
+
+#pragma pack()
+
+} // namespace
--- /dev/null
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <cstdint>
+#include <map>
+#include <stdexcept>
+#include <string>
+#include <sstream>
+
+#include "bit_field.h"
+
+namespace nihstro {
+
+enum class RegisterType {
+ Input,
+ Output,
+ Temporary,
+ FloatUniform,
+ IntUniform,
+ BoolUniform,
+ Address,
+ ConditionalCode,
+ Unknown
+};
+
+static std::string GetRegisterName(RegisterType type) {
+ switch (type) {
+ case RegisterType::Input: return "v";
+ case RegisterType::Output: return "o";
+ case RegisterType::Temporary: return "r";
+ case RegisterType::FloatUniform: return "c";
+ case RegisterType::IntUniform: return "i";
+ case RegisterType::BoolUniform: return "b";
+ case RegisterType::ConditionalCode: return "cc";
+ case RegisterType::Unknown: return "u";
+ default: return "";
+ }
+}
+
+struct SourceRegister {
+ SourceRegister() = default;
+
+ SourceRegister(uint32_t value) {
+ this->value = value;
+ }
+
+ RegisterType GetRegisterType() const {
+ if (value < 0x10)
+ return RegisterType::Input;
+ else if (value < 0x20)
+ return RegisterType::Temporary;
+ else
+ return RegisterType::FloatUniform;
+ }
+
+ int GetIndex() const {
+ if (GetRegisterType() == RegisterType::Input)
+ return value;
+ else if (GetRegisterType() == RegisterType::Temporary)
+ return value - 0x10;
+ else if (GetRegisterType() == RegisterType::FloatUniform)
+ return value - 0x20;
+ }
+
+ static const SourceRegister FromTypeAndIndex(RegisterType type, int index) {
+ SourceRegister reg;
+ if (type == RegisterType::Input)
+ reg.value = index;
+ else if (type == RegisterType::Temporary)
+ reg.value = index + 0x10;
+ else if (type == RegisterType::FloatUniform)
+ reg.value = index + 0x20;
+ else {
+ // TODO: Should throw an exception or something.
+ }
+ return reg;
+ }
+
+ static const SourceRegister MakeInput(int index) {
+ return FromTypeAndIndex(RegisterType::Input, index);
+ }
+
+ static const SourceRegister MakeTemporary(int index) {
+ return FromTypeAndIndex(RegisterType::Temporary, index);
+ }
+
+ static const SourceRegister MakeFloat(int index) {
+ return FromTypeAndIndex(RegisterType::FloatUniform, index);
+ }
+
+ std::string GetName() const {
+ std::stringstream ss;
+ ss << GetRegisterName(GetRegisterType()) << GetIndex();
+ return ss.str();
+ }
+
+ operator uint32_t() const {
+ return value;
+ }
+
+ template<typename T>
+ decltype(uint32_t{} - T{}) operator -(const T& oth) const {
+ return value - oth;
+ }
+
+ template<typename T>
+ decltype(uint32_t{} & T{}) operator &(const T& oth) const {
+ return value & oth;
+ }
+
+ uint32_t operator &(const SourceRegister& oth) const {
+ return value & oth.value;
+ }
+
+ uint32_t operator ~() const {
+ return ~value;
+ }
+
+private:
+ uint32_t value;
+};
+
+struct DestRegister {
+ DestRegister() = default;
+
+ DestRegister(uint32_t value) {
+ this->value = value;
+ }
+
+ RegisterType GetRegisterType() const {
+ if (value < 0x10)
+ return RegisterType::Output;
+ else
+ return RegisterType::Temporary;
+ }
+
+ int GetIndex() const {
+ if (GetRegisterType() == RegisterType::Output)
+ return value;
+ else if (GetRegisterType() == RegisterType::Temporary)
+ return value - 0x10;
+ else // if (GetRegisterType() == RegisterType::FloatUniform)
+ // TODO: This will lead to negative returned values...
+ return value - 0x20;
+ }
+
+ static const DestRegister FromTypeAndIndex(RegisterType type, int index) {
+ DestRegister reg;
+ if (type == RegisterType::Output)
+ reg.value = index;
+ else if (type == RegisterType::Temporary)
+ reg.value = index + 0x10;
+ else if (type == RegisterType::FloatUniform) // TODO: Wait what? These shouldn't be writable..
+ reg.value = index + 0x20;
+ else {
+ // TODO: Should throw an exception or something.
+ }
+ return reg;
+ }
+
+ static const DestRegister MakeOutput(int index) {
+ return FromTypeAndIndex(RegisterType::Output, index);
+ }
+
+ static const DestRegister MakeTemporary(int index) {
+ return FromTypeAndIndex(RegisterType::Temporary, index);
+ }
+
+ std::string GetName() const {
+ std::stringstream ss;
+ ss << GetRegisterName(GetRegisterType()) << GetIndex();
+ return ss.str();
+ }
+
+ operator uint32_t() const {
+ return value;
+ }
+
+ template<typename T>
+ decltype(uint32_t{} - T{}) operator -(const T& oth) const {
+ return value - oth;
+ }
+
+ template<typename T>
+ decltype(uint32_t{} & T{}) operator &(const T& oth) const {
+ return value & oth;
+ }
+
+ uint32_t operator &(const DestRegister& oth) const {
+ return value & oth.value;
+ }
+
+ uint32_t operator ~() const {
+ return ~value;
+ }
+
+private:
+ uint32_t value;
+};
+
+struct OpCode {
+ enum class Id : uint32_t {
+ ADD = 0x00,
+ DP3 = 0x01,
+ DP4 = 0x02,
+ DPH = 0x03, // Dot product of Vec4 and Vec3; the Vec3 is made into
+ // a Vec4 by appending 1.0 as the fourth component
+
+ EX2 = 0x05, // Base-2 exponential
+ LG2 = 0x06, // Base-2 logarithm
+
+ MUL = 0x08,
+ SGE = 0x09, // Set to 1.0 if SRC1 is greater or equal to SRC2
+ SLT = 0x0A, // Set to 1.0 if SRC1 is less than SRC2
+ FLR = 0x0B,
+ MAX = 0x0C,
+ MIN = 0x0D,
+ RCP = 0x0E, // Reciprocal
+ RSQ = 0x0F, // Reciprocal of square root
+
+ MOVA = 0x12, // Move to Address Register
+ MOV = 0x13,
+
+ DPHI = 0x18,
+
+ SGEI = 0x1A,
+ SLTI = 0x1B,
+
+ NOP = 0x21,
+ END = 0x22,
+ BREAKC = 0x23,
+ CALL = 0x24,
+ CALLC = 0x25,
+ CALLU = 0x26,
+ IFU = 0x27,
+ IFC = 0x28,
+ LOOP = 0x29,
+ EMIT = 0x2A,
+ SETEMIT = 0x2B,
+ JMPC = 0x2C,
+ JMPU = 0x2D,
+ CMP = 0x2E, // LSB opcode bit ignored
+
+ // lower 3 opcode bits ignored for these
+ MADI = 0x30,
+ MAD = 0x38, // lower 3 opcode bits ignored
+
+ // Pseudo-instructions, used internally by the assembler
+ PSEUDO_INSTRUCTION_START = 0x40,
+
+ GEN_IF = PSEUDO_INSTRUCTION_START, // Generic IF (IFC or IFU)
+ ELSE,
+ ENDIF,
+ GEN_CALL, // Generic CALL (CALL, CALC, or CALLU)
+ GEN_JMP, // Generic JMP (JMPC or JMPU)
+ //RET, // Return from function (not supported yet)
+ ENDLOOP,
+ };
+
+ enum class Type {
+ Trivial, // 3dbrew format 0
+ Arithmetic, // 3dbrew format 1
+ Conditional, // 3dbrew format 2
+ UniformFlowControl, // 3dbrew format 3
+ SetEmit, // 3dbrew format 4
+ MultiplyAdd, // 3dbrew format 5
+ Unknown
+ };
+
+ struct Info {
+ Type type;
+
+ // Arithmetic
+ enum : uint32_t {
+ OpDesc = 1,
+ Src1 = 2,
+ Src2 = 4,
+ Idx = 8,
+ Dest = 16,
+ SrcInversed = 32,
+ CompareOps = 64,
+ MOVA = 128 | OpDesc | Src1 | Idx,
+ OneArgument = OpDesc | Src1 | Idx | Dest,
+ TwoArguments = OneArgument | Src2,
+ Compare = OpDesc | Idx | Src1 | Src2 | CompareOps,
+ };
+
+ // Flow Control
+ enum : uint32_t {
+ HasUniformIndex = 1,
+ HasCondition = 2,
+ HasExplicitDest = 4, // target code given explicitly and context-independently (contrary to e.g. BREAKC)
+ HasFinishPoint = 8, // last instruction until returning to caller
+ HasAlternative = 16, // has an "else" branch
+ LOOP = 32,
+
+ BREAKC = HasCondition,
+
+ JMP = HasExplicitDest,
+ JMPC = JMP | HasCondition,
+ JMPU = JMP | HasUniformIndex,
+
+ CALL = JMP | HasFinishPoint,
+ CALLC = CALL | HasCondition,
+ CALLU = CALL | HasUniformIndex,
+ IFU = CALLU | HasAlternative,
+ IFC = CALLC | HasAlternative,
+ };
+
+ enum : uint32_t {
+ FullAndBool,
+ SimpleAndInt,
+ };
+
+ uint32_t subtype;
+
+ const char* name;
+
+ // TODO: Deprecate.
+ size_t NumArguments() const {
+ if (type == Type::Arithmetic) {
+ if (subtype & Src2)
+ return 3;
+ else if (subtype & Src1)
+ return 2;
+ }
+
+ return 0;
+ }
+ };
+
+ OpCode() = default;
+
+ OpCode(Id value) {
+ this->value = static_cast<uint32_t>(value);
+ }
+
+ OpCode(uint32_t value) {
+ this->value = value;
+ }
+
+ Id EffectiveOpCode() const {
+ uint32_t op = static_cast<uint32_t>(value);
+ if (static_cast<Id>(op & ~0x7) == Id::MAD)
+ return Id::MAD;
+ else if (static_cast<Id>(op & ~0x7) == Id::MADI)
+ return Id::MADI;
+ else if (static_cast<Id>(op & ~0x1) == Id::CMP)
+ return Id::CMP;
+ else
+ return static_cast<Id>(value);
+ }
+
+ const Info& GetInfo() const {
+ #define unknown_instruction { OpCode::Type::Unknown, 0, "UNK" }
+ static const OpCode::Info info_table[] = {
+ { OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "add" },
+ { OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "dp3" },
+ { OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "dp4" },
+ { OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "dph" },
+ unknown_instruction,
+ { OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "exp" },
+ { OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "log" },
+ unknown_instruction,
+ { OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "mul" },
+ { OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "sge" },
+ { OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "slt" },
+ { OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "flr" },
+ { OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "max" },
+ { OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "min" },
+ { OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "rcp" },
+ { OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "rsq" },
+ unknown_instruction,
+ unknown_instruction,
+ { OpCode::Type::Arithmetic, OpCode::Info::MOVA, "mova" },
+ { OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "mov" },
+ unknown_instruction,
+ unknown_instruction,
+ unknown_instruction,
+ unknown_instruction,
+ { OpCode::Type::Arithmetic, OpCode::Info::TwoArguments | OpCode::Info::SrcInversed, "dphi" },
+ unknown_instruction,
+ { OpCode::Type::Arithmetic, OpCode::Info::TwoArguments | OpCode::Info::SrcInversed, "sgei" },
+ { OpCode::Type::Arithmetic, OpCode::Info::TwoArguments | OpCode::Info::SrcInversed, "slti" },
+ unknown_instruction,
+ unknown_instruction,
+ unknown_instruction,
+ unknown_instruction,
+ unknown_instruction,
+ { OpCode::Type::Trivial, 0, "nop" },
+ { OpCode::Type::Trivial, 0, "end" },
+ { OpCode::Type::Conditional, OpCode::Info::BREAKC, "breakc" },
+ { OpCode::Type::Conditional, OpCode::Info::CALL, "call" },
+ { OpCode::Type::Conditional, OpCode::Info::CALLC, "callc" },
+ { OpCode::Type::UniformFlowControl, OpCode::Info::CALLU, "callu" },
+ { OpCode::Type::UniformFlowControl, OpCode::Info::IFU, "ifu" },
+ { OpCode::Type::Conditional, OpCode::Info::IFC, "ifc" },
+ { OpCode::Type::UniformFlowControl, OpCode::Info::LOOP, "loop" },
+ { OpCode::Type::Trivial, 0, "emit" },
+ { OpCode::Type::SetEmit, 0, "setemit" },
+ { OpCode::Type::Conditional, OpCode::Info::JMPC, "jmpc" },
+ { OpCode::Type::Conditional, OpCode::Info::JMPU, "jmpu" },
+ { OpCode::Type::Arithmetic, OpCode::Info::Compare, "cmp" },
+ { OpCode::Type::Arithmetic, OpCode::Info::Compare, "cmp" },
+ { OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
+ { OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
+ { OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
+ { OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
+ { OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
+ { OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
+ { OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
+ { OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
+ { OpCode::Type::MultiplyAdd, 0, "mad" },
+ { OpCode::Type::MultiplyAdd, 0, "mad" },
+ { OpCode::Type::MultiplyAdd, 0, "mad" },
+ { OpCode::Type::MultiplyAdd, 0, "mad" },
+ { OpCode::Type::MultiplyAdd, 0, "mad" },
+ { OpCode::Type::MultiplyAdd, 0, "mad" },
+ { OpCode::Type::MultiplyAdd, 0, "mad" },
+ { OpCode::Type::MultiplyAdd, 0, "mad" }
+ };
+ #undef unknown_instruction
+ return info_table[value];
+ }
+
+ operator Id() const {
+ return static_cast<Id>(value);
+ }
+
+ OpCode operator << (size_t bits) const {
+ return value << bits;
+ }
+
+ template<typename T>
+ decltype(uint32_t{} - T{}) operator -(const T& oth) const {
+ return value - oth;
+ }
+
+ uint32_t operator &(const OpCode& oth) const {
+ return value & oth.value;
+ }
+
+ uint32_t operator ~() const {
+ return ~value;
+ }
+
+private:
+ uint32_t value;
+};
+
+} // namespace nihstro
+
+namespace std {
+ template<>
+ struct make_unsigned<nihstro::SourceRegister> {
+ using type = nihstro::SourceRegister;
+ };
+
+ template<>
+ struct make_unsigned<nihstro::DestRegister> {
+ using type = nihstro::DestRegister;
+ };
+
+ template<>
+ struct make_unsigned<nihstro::OpCode> {
+ using type = nihstro::OpCode;
+ };
+}
+
+namespace nihstro {
+
+#pragma pack(1)
+union Instruction {
+ Instruction& operator =(const Instruction& instr) {
+ hex = instr.hex;
+ return *this;
+ }
+
+ uint32_t hex;
+
+ BitField<0x1a, 0x6, OpCode> opcode;
+
+
+ // General notes:
+ //
+ // When two input registers are used, one of them uses a 5-bit index while the other
+ // one uses a 7-bit index. This is because at most one floating point uniform may be used
+ // as an input.
+
+
+ // Format used e.g. by arithmetic instructions and comparisons
+ union Common { // TODO: Remove name
+ BitField<0x00, 0x7, uint32_t> operand_desc_id;
+
+ const SourceRegister GetSrc1(bool is_inverted) const {
+ if (!is_inverted) {
+ return src1;
+ } else {
+ return src1i;
+ }
+ }
+
+ const SourceRegister GetSrc2(bool is_inverted) const {
+ if (!is_inverted) {
+ return src2;
+ } else {
+ return src2i;
+ }
+ }
+
+ /**
+ * Source inputs may be reordered for certain instructions.
+ * Use GetSrc1 and GetSrc2 instead to access the input register indices hence.
+ */
+ BitField<0x07, 0x5, SourceRegister> src2;
+ BitField<0x0c, 0x7, SourceRegister> src1;
+ BitField<0x07, 0x7, SourceRegister> src2i;
+ BitField<0x0e, 0x5, SourceRegister> src1i;
+
+ // Address register value is used for relative addressing of src1
+ BitField<0x13, 0x2, uint32_t> address_register_index;
+
+ union CompareOpType { // TODO: Make nameless once MSVC supports it
+ enum Op : uint32_t {
+ Equal = 0,
+ NotEqual = 1,
+ LessThan = 2,
+ LessEqual = 3,
+ GreaterThan = 4,
+ GreaterEqual = 5,
+ Unk6 = 6,
+ Unk7 = 7
+ };
+
+ BitField<0x15, 0x3, Op> y;
+ BitField<0x18, 0x3, Op> x;
+
+ const std::string ToString(Op op) const {
+ switch (op) {
+ case Equal: return "==";
+ case NotEqual: return "!=";
+ case LessThan: return "<";
+ case LessEqual: return "<=";
+ case GreaterThan: return ">";
+ case GreaterEqual: return ">=";
+ case Unk6: return "UNK6";
+ case Unk7: return "UNK7";
+ default: return "";
+ };
+ }
+ } compare_op;
+
+ std::string AddressRegisterName() const {
+ if (address_register_index == 0) return "";
+ else if (address_register_index == 1) return "a0.x";
+ else if (address_register_index == 2) return "a0.y";
+ else /*if (address_register_index == 3)*/ return "aL";
+ }
+
+ BitField<0x15, 0x5, DestRegister> dest;
+ } common;
+
+ union FlowControlType { // TODO: Make nameless once MSVC supports it
+ enum Op : uint32_t {
+ Or = 0,
+ And = 1,
+ JustX = 2,
+ JustY = 3
+ };
+
+ BitField<0x00, 0x8, uint32_t> num_instructions;
+ BitField<0x0a, 0xc, uint32_t> dest_offset;
+
+ BitField<0x16, 0x2, Op> op;
+ BitField<0x16, 0x4, uint32_t> bool_uniform_id;
+ BitField<0x16, 0x2, uint32_t> int_uniform_id; // TODO: Verify that only this many bits are used...
+
+ BitFlag<0x18, uint32_t> refy;
+ BitFlag<0x19, uint32_t> refx;
+ } flow_control;
+
+ union {
+ const SourceRegister GetSrc1(bool is_inverted) const {
+ // The inverted form for src1 is the same, this function is just here for consistency
+ return src1;
+ }
+
+ const SourceRegister GetSrc2(bool is_inverted) const {
+ if (!is_inverted) {
+ return src2;
+ } else {
+ return src2i;
+ }
+ }
+
+ const SourceRegister GetSrc3(bool is_inverted) const {
+ if (!is_inverted) {
+ return src3;
+ } else {
+ return src3i;
+ }
+ }
+
+ BitField<0x00, 0x5, uint32_t> operand_desc_id;
+
+ BitField<0x05, 0x5, SourceRegister> src3;
+ BitField<0x0a, 0x7, SourceRegister> src2;
+ BitField<0x11, 0x7, SourceRegister> src1;
+
+ BitField<0x05, 0x7, SourceRegister> src3i;
+ BitField<0x0c, 0x5, SourceRegister> src2i;
+
+ BitField<0x18, 0x5, DestRegister> dest;
+ } mad;
+
+ union {
+ BitField<0x16, 1, uint32_t> winding;
+ BitField<0x17, 1, uint32_t> prim_emit;
+ BitField<0x18, 2, uint32_t> vertex_id;
+ } setemit;
+};
+static_assert(sizeof(Instruction) == 0x4, "Incorrect structure size");
+static_assert(std::is_standard_layout<Instruction>::value, "Structure does not have standard layout");
+
+union SwizzlePattern {
+ SwizzlePattern& operator =(const SwizzlePattern& instr) {
+ hex = instr.hex;
+ return *this;
+ }
+
+ uint32_t hex;
+
+ enum class Selector : uint32_t {
+ x = 0,
+ y = 1,
+ z = 2,
+ w = 3
+ };
+
+ /**
+ * Gets the raw 8-bit selector for the specified (1-indexed) source register.
+ */
+ unsigned GetRawSelector(unsigned src) const {
+ if (src == 0 || src > 3)
+ throw std::out_of_range("src needs to be between 1 and 3");
+
+ unsigned selectors[] = {
+ src1_selector, src2_selector, src3_selector
+ };
+ return selectors[src - 1];
+ }
+
+ Selector GetSelectorSrc1(int comp) const {
+ Selector selectors[] = {
+ src1_selector_0, src1_selector_1, src1_selector_2, src1_selector_3
+ };
+ return selectors[comp];
+ }
+
+ Selector GetSelectorSrc2(int comp) const {
+ Selector selectors[] = {
+ src2_selector_0, src2_selector_1, src2_selector_2, src2_selector_3
+ };
+ return selectors[comp];
+ }
+
+ Selector GetSelectorSrc3(int comp) const {
+ Selector selectors[] = {
+ src3_selector_0, src3_selector_1, src3_selector_2, src3_selector_3
+ };
+ return selectors[comp];
+ }
+
+ void SetSelectorSrc1(int comp, Selector value) {
+ if (comp == 0)
+ src1_selector_0 = value;
+ else if (comp == 1)
+ src1_selector_1 = value;
+ else if (comp == 2)
+ src1_selector_2 = value;
+ else if (comp == 3)
+ src1_selector_3 = value;
+ else
+ throw std::out_of_range("comp needs to be smaller than 4");
+ }
+
+ void SetSelectorSrc2(int comp, Selector value) {
+ if (comp == 0)
+ src2_selector_0 = value;
+ else if (comp == 1)
+ src2_selector_1 = value;
+ else if (comp == 2)
+ src2_selector_2 = value;
+ else if (comp == 3)
+ src2_selector_3 = value;
+ else
+ throw std::out_of_range("comp needs to be smaller than 4");
+ }
+
+ void SetSelectorSrc3(int comp, Selector value) {
+ if (comp == 0)
+ src3_selector_0 = value;
+ else if (comp == 1)
+ src3_selector_1 = value;
+ else if (comp == 2)
+ src3_selector_2 = value;
+ else if (comp == 3)
+ src3_selector_3 = value;
+ else
+ throw std::out_of_range("comp needs to be smaller than 4");
+ }
+
+ std::string SelectorToString(bool src2) const {
+ std::map<Selector, std::string> map = {
+ { Selector::x, "x" },
+ { Selector::y, "y" },
+ { Selector::z, "z" },
+ { Selector::w, "w" }
+ };
+ std::string ret;
+ for (int i = 0; i < 4; ++i) {
+ ret += map.at(src2 ? GetSelectorSrc2(i) : GetSelectorSrc1(i));
+ }
+ return ret;
+ }
+
+ bool DestComponentEnabled(unsigned int i) const {
+ return (dest_mask & (0x8 >> i));
+ }
+
+ void SetDestComponentEnabled(unsigned int i, bool enabled) {
+ int mask = 0xffff & (0x8 >> i);
+ dest_mask = (dest_mask & ~mask) | (enabled * mask);
+ }
+
+ std::string DestMaskToString() const {
+ std::string ret;
+ for (int i = 0; i < 4; ++i) {
+ if (!DestComponentEnabled(i))
+ ret += "_";
+ else
+ ret += "xyzw"[i];
+ }
+ return ret;
+ }
+
+ // Components of "dest" that should be written to: LSB=dest.w, MSB=dest.x
+ BitField< 0, 4, uint32_t> dest_mask;
+
+ BitFlag < 4, uint32_t> negate_src1;
+ BitField< 5, 8, uint32_t> src1_selector;
+ BitField< 5, 2, Selector> src1_selector_3;
+ BitField< 7, 2, Selector> src1_selector_2;
+ BitField< 9, 2, Selector> src1_selector_1;
+ BitField<11, 2, Selector> src1_selector_0;
+
+ BitFlag <13, uint32_t> negate_src2;
+ BitField<14, 8, uint32_t> src2_selector;
+ BitField<14, 2, Selector> src2_selector_3;
+ BitField<16, 2, Selector> src2_selector_2;
+ BitField<18, 2, Selector> src2_selector_1;
+ BitField<20, 2, Selector> src2_selector_0;
+
+ BitFlag <22, uint32_t> negate_src3;
+ BitField<23, 8, uint32_t> src3_selector;
+ BitField<23, 2, Selector> src3_selector_3;
+ BitField<25, 2, Selector> src3_selector_2;
+ BitField<27, 2, Selector> src3_selector_1;
+ BitField<29, 2, Selector> src3_selector_0;
+};
+static_assert(sizeof(SwizzlePattern) == 0x4, "Incorrect structure size");
+
+
+#pragma pack()
+
+} // namespace
--- /dev/null
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <list>
+#include <string>
+
+#include <boost/optional.hpp>
+#include <boost/variant/recursive_wrapper.hpp>
+
+namespace nihstro {
+
+struct SourceTreeIterator;
+
+
+struct Node;
+// SequenceContainer
+struct SourceTree {
+ SourceTree() = default;
+ SourceTree(const SourceTree& oth);
+
+ std::string code;
+
+ struct {
+ std::string filename;
+ } file_info;
+
+ SourceTree* parent = nullptr;
+
+ // ordered with respect to "position"
+ std::list<Node> children;
+
+ SourceTreeIterator begin();
+ SourceTreeIterator end();
+
+ // Attach the given tree, changing the child's owner to *this.
+ SourceTree& Attach(SourceTree tree, std::string::difference_type offset);
+};
+
+struct Node {
+ SourceTree tree;
+
+ std::string::difference_type offset_within_parent; // within "code"
+};
+
+inline SourceTree::SourceTree(const SourceTree& oth) : code(oth.code), file_info(oth.file_info), parent(oth.parent), children(oth.children) {
+ for (auto& child : children)
+ child.tree.parent = this;
+}
+
+inline SourceTree& SourceTree::Attach(SourceTree tree, std::string::difference_type offset) {
+ tree.parent = this;
+ children.push_back(Node{tree, offset});
+ return *this;
+}
+
+// RandomAccessIterator
+struct SourceTreeIterator {
+ using difference_type = std::string::iterator::difference_type;
+ using reference = std::string::iterator::reference;
+ using value_type = std::string::iterator::value_type;
+ using pointer = std::string::iterator::pointer;
+ using iterator_category = std::random_access_iterator_tag;
+
+ SourceTreeIterator(SourceTree& tree) : tree(&tree), position(tree.code.begin()), node_iterator(tree.children.begin()) {
+ UpdateChildIterator();
+ }
+
+ SourceTreeIterator(const SourceTreeIterator&) = default;
+
+ SourceTreeIterator& operator += (difference_type n) {
+ if (n > 0) {
+ while (n) {
+ if (child_iterator) {
+ auto remaining_to_child = node_iterator->offset_within_parent - (position - tree->code.begin());
+ if (remaining_to_child >= n) {
+ // If the next child is more than n steps away, increase position by n and return
+ // TODO: Should we make sure that we don't end up out-of-bounds here?
+ position += n;
+ UpdateNodeIterator();
+ break;
+ } else {
+ // Otherwise, move current position to the child if it isn't there already
+ position += remaining_to_child;
+ n -= remaining_to_child;
+ UpdateNodeIterator();
+ }
+
+ if (child_iterator->get().StepsRemaining() > n) {
+ // If child is larger than n, advance child by n and return
+ child_iterator->get() += n;
+ break;
+ } else {
+ // else step out of the child and increment next child iterator by one
+ n -= child_iterator->get().StepsRemaining();
+ if (node_iterator != tree->children.end())
+ node_iterator++;
+ UpdateChildIterator();
+ }
+ } else {
+ // TODO: Should we make sure that we don't end up out-of-bounds here?
+ position += n;
+ UpdateNodeIterator();
+ break;
+ }
+ }
+ } else if (n < 0) {
+ // Reduce to n>0 case by starting from begin()
+ n = (*this - tree->begin()) + n;
+ *this = tree->begin() + n;
+ }
+ return *this;
+ }
+
+ SourceTreeIterator& operator -= (difference_type n) {
+ *this += -n;
+ return *this;
+ }
+
+ difference_type operator -(SourceTreeIterator it) const {
+ return this->StepsGone() - it.StepsGone();
+ }
+
+ bool operator < (const SourceTreeIterator& it) const {
+ return std::distance(*this, it) > 0;
+ }
+
+ bool operator <= (const SourceTreeIterator& it) const {
+ return std::distance(*this, it) >= 0;
+ }
+
+ bool operator > (const SourceTreeIterator& it) const {
+ return !(*this <= it);
+ }
+
+ bool operator >= (const SourceTreeIterator& it) const {
+ return !(*this < it);
+ }
+
+ bool operator == (const SourceTreeIterator& it) const {
+ return (*this <= it) && !(*this < it);
+ }
+
+ bool operator != (const SourceTreeIterator& it) const {
+ return !(*this == it);
+ }
+
+ reference operator* () {
+ return (*this)[0];
+ }
+
+ SourceTreeIterator operator++ () {
+ *this += 1;
+ return *this;
+ }
+
+ SourceTreeIterator operator++ (int) {
+ auto it = *this;
+ *this += 1;
+ return it;
+ }
+
+ SourceTreeIterator operator +(difference_type n) const {
+ SourceTreeIterator it2 = *this;
+ it2 += n;
+ return it2;
+ }
+
+ SourceTreeIterator operator -(SourceTreeIterator::difference_type n) const {
+ return *this + (-n);
+ }
+
+ reference operator [] (difference_type n) {
+ auto it = (*this + n);
+ if (it.WithinChild())
+ return it.child_iterator->get()[0];
+ else return *it.position;
+ }
+
+ // Get line number (one-based) within "tree"
+ unsigned GetLineNumber() const {
+ // Adding one for natural (i.e. one-based) line numbers
+ return std::count(tree->code.begin(), position, '\n') + 1;
+ }
+
+ // Get line number (one-based) within the tree of the current child
+ unsigned GetCurrentLineNumber() const {
+ if (WithinChild())
+ return child_iterator->get().GetCurrentLineNumber();
+
+ return GetLineNumber();
+ }
+
+ const std::string GetCurrentFilename() const {
+ if (WithinChild())
+ return child_iterator->get().GetCurrentFilename();
+
+ return tree->file_info.filename;
+ }
+
+ SourceTreeIterator GetParentIterator(const SourceTree* reference_tree) const {
+ if (tree == reference_tree) {
+ return *this;
+ } else {
+ return child_iterator->get().GetParentIterator(reference_tree);
+ }
+ }
+
+ SourceTree* GetCurrentTree() {
+ if (WithinChild())
+ return child_iterator->get().GetCurrentTree();
+ else
+ return tree;
+ }
+
+private:
+ difference_type StepsRemaining() const {
+ return std::distance(*this, tree->end());
+ }
+
+ difference_type StepsGone() const {
+ auto it = tree->begin();
+
+ difference_type diff = 0;
+
+ // Advance reference iterator starting from the beginning until we reach *this,
+ // making sure that both the main position and the child iterator match.
+ while (it.position != position ||
+ ((bool)it.child_iterator ^ (bool)child_iterator) ||
+ (it.child_iterator && child_iterator && it.child_iterator->get() != child_iterator->get())) {
+ // Move to next child (if there is one), or abort if we reach the reference position
+ if (it.child_iterator) {
+ auto distance_to_child = std::min(it.node_iterator->offset_within_parent - (it.position -it.tree->code.begin() ), position - it.position);
+
+ // Move to child or this->position
+ diff += distance_to_child;
+ it.position += distance_to_child;
+
+ if (it.position - it.tree->code.begin() == it.node_iterator->offset_within_parent) {
+ if (node_iterator != tree->children.end() && it.node_iterator == node_iterator) {
+ return diff + (child_iterator->get() - it.child_iterator->get());
+ } else {
+ // Move out of child
+ diff += it.child_iterator->get().StepsRemaining();
+ }
+ } else {
+ // We moved to this->position => done
+ return diff;
+ }
+
+ // Move to next child
+ if (it.node_iterator != it.tree->children.end()) {
+ it.node_iterator++;
+ it.UpdateChildIterator();
+ }
+ } else {
+ // no child remaining, hence just move to the given position
+ return diff + (position - it.position);
+ }
+ }
+
+ return diff;
+ }
+
+ bool WithinChild() const {
+ return child_iterator && position - tree->code.begin() == node_iterator->offset_within_parent;
+ }
+
+ void UpdateChildIterator() {
+ if (node_iterator != tree->children.end())
+ child_iterator = SourceTreeIterator(node_iterator->tree);
+ else
+ child_iterator = boost::none;
+ }
+
+ void UpdateNodeIterator() {
+ // Move to the first node which is at the cursor or behind it
+ while (node_iterator != tree->children.end() && node_iterator->offset_within_parent < std::distance(tree->code.begin(), position)) {
+ node_iterator++;
+ UpdateChildIterator();
+ }
+ }
+
+ SourceTree* tree;
+ std::string::iterator position;
+
+ boost::optional<boost::recursive_wrapper<SourceTreeIterator>> child_iterator; // points to current or next child
+ std::list<Node>::iterator node_iterator; // points to current or next node
+
+ friend struct SourceTree;
+};
+
+inline SourceTreeIterator operator +(SourceTreeIterator::difference_type n, const SourceTreeIterator& it) {
+ return it + n;
+}
+
+inline SourceTreeIterator operator -(SourceTreeIterator::difference_type n, const SourceTreeIterator& it) {
+ return it - n;
+}
+
+inline SourceTreeIterator SourceTree::begin() {
+ return SourceTreeIterator(*this);
+}
+
+inline SourceTreeIterator SourceTree::end() {
+ auto it = SourceTreeIterator(*this);
+ it.position = code.end();
+ it.node_iterator = children.end();
+ it.child_iterator = boost::none;
+ return it;
+}
+
+} // namespace
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "settings.h"
+
+namespace Settings {
+
+Values values = {};
+
+}
--- /dev/null
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include <array>
+
+namespace Settings {
+
+namespace NativeInput {
+enum Values {
+ A, B, X, Y,
+ L, R, ZL, ZR,
+ START, SELECT, HOME,
+ DUP, DDOWN, DLEFT, DRIGHT,
+ SUP, SDOWN, SLEFT, SRIGHT,
+ CUP, CDOWN, CLEFT, CRIGHT,
+ NUM_INPUTS
+};
+static const std::array<const char*, NUM_INPUTS> Mapping = {{
+ "pad_a", "pad_b", "pad_x", "pad_y",
+ "pad_l", "pad_r", "pad_zl", "pad_zr",
+ "pad_start", "pad_select", "pad_home",
+ "pad_dup", "pad_ddown", "pad_dleft", "pad_dright",
+ "pad_sup", "pad_sdown", "pad_sleft", "pad_sright",
+ "pad_cup", "pad_cdown", "pad_cleft", "pad_cright"
+}};
+static const std::array<Values, NUM_INPUTS> All = {{
+ A, B, X, Y,
+ L, R, ZL, ZR,
+ START, SELECT, HOME,
+ DUP, DDOWN, DLEFT, DRIGHT,
+ SUP, SDOWN, SLEFT, SRIGHT,
+ CUP, CDOWN, CLEFT, CRIGHT
+}};
+}
+
+
+struct Values {
+ // Controls
+ std::array<int, NativeInput::NUM_INPUTS> input_mappings;
+
+ // Core
+ int frame_skip;
+
+ // Data Storage
+ bool use_virtual_sd;
+
+ // System Region
+ int region_value;
+
+ // Renderer
+ bool use_hw_renderer;
+ bool use_shader_jit;
+
+ float bg_red;
+ float bg_green;
+ float bg_blue;
+
+ std::string log_filter;
+} extern values;
+
+}
--- /dev/null
+#include <stdio.h>
+#include "Gui.h"
+
+
+static void on_error(int error, const char* description) {
+ fprintf(stderr, "GLFW error %d: %s\n", error, description);
+}
+
+MainWindow::MainWindow() {
+ // Setup window
+ glfwSetErrorCallback(on_error);
+ if (!glfwInit())
+ exit(1);
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+ GLFWwindow* window = glfwCreateWindow(1280, 720, "xds", NULL, NULL);
+
+ if (window)
+ {
+
+ glfwMakeContextCurrent(window);
+ gl3wInit();
+
+ ImGui_ImplGlfwGL3_Init(window, true);
+
+
+ while (!glfwWindowShouldClose(window))
+ {
+ ImGuiIO& io = ImGui::GetIO();
+ glfwPollEvents();
+ ImGui_ImplGlfwGL3_NewFrame();
+
+ ImGui::SetNextWindowSize(ImVec2(400, 100), ImGuiSetCond_FirstUseEver);
+ ImGui::Begin("Test Window");
+ ImGui::Text("TODO: Move this to new thread so xds can run in the background.");
+ ImGui::Text("Close the window to continue xds execution.");
+ ImGui::End();
+
+ // Rendering
+ glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
+ ImVec4 clear_color = ImColor(114, 144, 154);
+ glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
+ glClear(GL_COLOR_BUFFER_BIT);
+ ImGui::Render();
+
+ glfwSwapBuffers(window);
+ }
+
+ // Cleanup
+ ImGui_ImplGlfwGL3_Shutdown();
+ glfwTerminate();
+
+ }
+}
--- /dev/null
+// ImGui GLFW binding with OpenGL3 + shaders
+// https://github.com/ocornut/imgui
+
+#include "../external/imgui/imgui.h"
+#include "imgui_impl_glfw_gl3.h"
+
+// GL3W/GLFW
+#include <GL/gl3w.h>
+#include <GLFW/glfw3.h>
+#ifdef _MSC_VER
+#undef APIENTRY
+#define GLFW_EXPOSE_NATIVE_WIN32
+#define GLFW_EXPOSE_NATIVE_WGL
+#include <GLFW/glfw3native.h>
+#endif
+
+// Data
+static GLFWwindow* g_Window = NULL;
+static double g_Time = 0.0f;
+static bool g_MousePressed[3] = { false, false, false };
+static float g_MouseWheel = 0.0f;
+static GLuint g_FontTexture = 0;
+static int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
+static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0;
+static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0;
+static size_t g_VboSize = 0;
+static unsigned int g_VboHandle = 0, g_VaoHandle = 0;
+
+// This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure)
+// If text or lines are blurry when integrating ImGui in your engine:
+// - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f)
+static void ImGui_ImplGlfwGL3_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
+{
+ if (cmd_lists_count == 0)
+ return;
+
+ // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
+ GLint last_program, last_texture;
+ glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
+ glEnable(GL_BLEND);
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_SCISSOR_TEST);
+ glActiveTexture(GL_TEXTURE0);
+
+ // Setup orthographic projection matrix
+ const float width = ImGui::GetIO().DisplaySize.x;
+ const float height = ImGui::GetIO().DisplaySize.y;
+ const float ortho_projection[4][4] =
+ {
+ { 2.0f/width, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 2.0f/-height, 0.0f, 0.0f },
+ { 0.0f, 0.0f, -1.0f, 0.0f },
+ { -1.0f, 1.0f, 0.0f, 1.0f },
+ };
+ glUseProgram(g_ShaderHandle);
+ glUniform1i(g_AttribLocationTex, 0);
+ glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
+
+ // Grow our buffer according to what we need
+ size_t total_vtx_count = 0;
+ for (int n = 0; n < cmd_lists_count; n++)
+ total_vtx_count += cmd_lists[n]->vtx_buffer.size();
+ glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
+ size_t needed_vtx_size = total_vtx_count * sizeof(ImDrawVert);
+ if (g_VboSize < needed_vtx_size)
+ {
+ g_VboSize = needed_vtx_size + 5000 * sizeof(ImDrawVert); // Grow buffer
+ glBufferData(GL_ARRAY_BUFFER, g_VboSize, NULL, GL_STREAM_DRAW);
+ }
+
+ // Copy and convert all vertices into a single contiguous buffer
+ unsigned char* buffer_data = (unsigned char*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
+ if (!buffer_data)
+ return;
+ for (int n = 0; n < cmd_lists_count; n++)
+ {
+ const ImDrawList* cmd_list = cmd_lists[n];
+ memcpy(buffer_data, &cmd_list->vtx_buffer[0], cmd_list->vtx_buffer.size() * sizeof(ImDrawVert));
+ buffer_data += cmd_list->vtx_buffer.size() * sizeof(ImDrawVert);
+ }
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(g_VaoHandle);
+
+ int cmd_offset = 0;
+ for (int n = 0; n < cmd_lists_count; n++)
+ {
+ const ImDrawList* cmd_list = cmd_lists[n];
+ int vtx_offset = cmd_offset;
+ const ImDrawCmd* pcmd_end = cmd_list->commands.end();
+ for (const ImDrawCmd* pcmd = cmd_list->commands.begin(); pcmd != pcmd_end; pcmd++)
+ {
+ if (pcmd->user_callback)
+ {
+ pcmd->user_callback(cmd_list, pcmd);
+ }
+ else
+ {
+ glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->texture_id);
+ glScissor((int)pcmd->clip_rect.x, (int)(height - pcmd->clip_rect.w), (int)(pcmd->clip_rect.z - pcmd->clip_rect.x), (int)(pcmd->clip_rect.w - pcmd->clip_rect.y));
+ glDrawArrays(GL_TRIANGLES, vtx_offset, pcmd->vtx_count);
+ }
+ vtx_offset += pcmd->vtx_count;
+ }
+ cmd_offset = vtx_offset;
+ }
+
+ // Restore modified state
+ glBindVertexArray(0);
+ glUseProgram(last_program);
+ glDisable(GL_SCISSOR_TEST);
+ glBindTexture(GL_TEXTURE_2D, last_texture);
+}
+
+static const char* ImGui_ImplGlfwGL3_GetClipboardText()
+{
+ return glfwGetClipboardString(g_Window);
+}
+
+static void ImGui_ImplGlfwGL3_SetClipboardText(const char* text)
+{
+ glfwSetClipboardString(g_Window, text);
+}
+
+void ImGui_ImplGlfwGL3_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/)
+{
+ if (action == GLFW_PRESS && button >= 0 && button < 3)
+ g_MousePressed[button] = true;
+}
+
+void ImGui_ImplGlfwGL3_ScrollCallback(GLFWwindow*, double /*xoffset*/, double yoffset)
+{
+ g_MouseWheel += (float)yoffset; // Use fractional mouse wheel, 1.0 unit 5 lines.
+}
+
+void ImGui_ImplGlfwGL3_KeyCallback(GLFWwindow*, int key, int, int action, int mods)
+{
+ ImGuiIO& io = ImGui::GetIO();
+ if (action == GLFW_PRESS)
+ io.KeysDown[key] = true;
+ if (action == GLFW_RELEASE)
+ io.KeysDown[key] = false;
+
+ (void)mods; // Modifiers are not reliable across systems
+ io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL];
+ io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT];
+ io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT];
+}
+
+void ImGui_ImplGlfwGL3_CharCallback(GLFWwindow*, unsigned int c)
+{
+ ImGuiIO& io = ImGui::GetIO();
+ if (c > 0 && c < 0x10000)
+ io.AddInputCharacter((unsigned short)c);
+}
+
+void ImGui_ImplGlfwGL3_CreateFontsTexture()
+{
+ ImGuiIO& io = ImGui::GetIO();
+
+ unsigned char* pixels;
+ int width, height;
+ io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits for OpenGL3 demo because it is more likely to be compatible with user's existing shader.
+
+ glGenTextures(1, &g_FontTexture);
+ glBindTexture(GL_TEXTURE_2D, g_FontTexture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+
+ // Store our identifier
+ io.Fonts->TexID = (void *)(intptr_t)g_FontTexture;
+
+ // Cleanup (don't clear the input data if you want to append new fonts later)
+ io.Fonts->ClearInputData();
+ io.Fonts->ClearTexData();
+}
+
+bool ImGui_ImplGlfwGL3_CreateDeviceObjects()
+{
+ const GLchar *vertex_shader =
+ "#version 330\n"
+ "uniform mat4 ProjMtx;\n"
+ "in vec2 Position;\n"
+ "in vec2 UV;\n"
+ "in vec4 Color;\n"
+ "out vec2 Frag_UV;\n"
+ "out vec4 Frag_Color;\n"
+ "void main()\n"
+ "{\n"
+ " Frag_UV = UV;\n"
+ " Frag_Color = Color;\n"
+ " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
+ "}\n";
+
+ const GLchar* fragment_shader =
+ "#version 330\n"
+ "uniform sampler2D Texture;\n"
+ "in vec2 Frag_UV;\n"
+ "in vec4 Frag_Color;\n"
+ "out vec4 Out_Color;\n"
+ "void main()\n"
+ "{\n"
+ " Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n"
+ "}\n";
+
+ g_ShaderHandle = glCreateProgram();
+ g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
+ g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(g_VertHandle, 1, &vertex_shader, 0);
+ glShaderSource(g_FragHandle, 1, &fragment_shader, 0);
+ glCompileShader(g_VertHandle);
+ glCompileShader(g_FragHandle);
+ glAttachShader(g_ShaderHandle, g_VertHandle);
+ glAttachShader(g_ShaderHandle, g_FragHandle);
+ glLinkProgram(g_ShaderHandle);
+
+ g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
+ g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
+ g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position");
+ g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV");
+ g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color");
+
+ glGenBuffers(1, &g_VboHandle);
+
+ glGenVertexArrays(1, &g_VaoHandle);
+ glBindVertexArray(g_VaoHandle);
+ glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
+ glEnableVertexAttribArray(g_AttribLocationPosition);
+ glEnableVertexAttribArray(g_AttribLocationUV);
+ glEnableVertexAttribArray(g_AttribLocationColor);
+
+#define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
+ glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, pos));
+ glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, uv));
+ glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, col));
+#undef OFFSETOF
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ ImGui_ImplGlfwGL3_CreateFontsTexture();
+
+ return true;
+}
+
+bool ImGui_ImplGlfwGL3_Init(GLFWwindow* window, bool install_callbacks)
+{
+ g_Window = window;
+
+ ImGuiIO& io = ImGui::GetIO();
+ io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array.
+ io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
+ io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
+ io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
+ io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
+ io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
+ io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
+ io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
+ io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
+ io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
+ io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
+ io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
+ io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
+ io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
+ io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
+ io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
+ io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
+
+ io.RenderDrawListsFn = ImGui_ImplGlfwGL3_RenderDrawLists;
+ io.SetClipboardTextFn = ImGui_ImplGlfwGL3_SetClipboardText;
+ io.GetClipboardTextFn = ImGui_ImplGlfwGL3_GetClipboardText;
+#ifdef _MSC_VER
+ io.ImeWindowHandle = glfwGetWin32Window(g_Window);
+#endif
+
+ if (install_callbacks)
+ {
+ glfwSetMouseButtonCallback(window, ImGui_ImplGlfwGL3_MouseButtonCallback);
+ glfwSetScrollCallback(window, ImGui_ImplGlfwGL3_ScrollCallback);
+ glfwSetKeyCallback(window, ImGui_ImplGlfwGL3_KeyCallback);
+ glfwSetCharCallback(window, ImGui_ImplGlfwGL3_CharCallback);
+ }
+
+ return true;
+}
+
+void ImGui_ImplGlfwGL3_Shutdown()
+{
+ if (g_VaoHandle) glDeleteVertexArrays(1, &g_VaoHandle);
+ if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle);
+ g_VaoHandle = 0;
+ g_VboHandle = 0;
+
+ glDetachShader(g_ShaderHandle, g_VertHandle);
+ glDeleteShader(g_VertHandle);
+ g_VertHandle = 0;
+
+ glDetachShader(g_ShaderHandle, g_FragHandle);
+ glDeleteShader(g_FragHandle);
+ g_FragHandle = 0;
+
+ glDeleteProgram(g_ShaderHandle);
+ g_ShaderHandle = 0;
+
+ if (g_FontTexture)
+ {
+ glDeleteTextures(1, &g_FontTexture);
+ ImGui::GetIO().Fonts->TexID = 0;
+ g_FontTexture = 0;
+ }
+ ImGui::Shutdown();
+}
+
+void ImGui_ImplGlfwGL3_NewFrame()
+{
+ if (!g_FontTexture)
+ ImGui_ImplGlfwGL3_CreateDeviceObjects();
+
+ ImGuiIO& io = ImGui::GetIO();
+
+ // Setup display size (every frame to accommodate for window resizing)
+ int w, h;
+ int display_w, display_h;
+ glfwGetWindowSize(g_Window, &w, &h);
+ glfwGetFramebufferSize(g_Window, &display_w, &display_h);
+ io.DisplaySize = ImVec2((float)display_w, (float)display_h);
+
+ // Setup time step
+ double current_time = glfwGetTime();
+ io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f);
+ g_Time = current_time;
+
+ // Setup inputs
+ // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents())
+ if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED))
+ {
+ double mouse_x, mouse_y;
+ glfwGetCursorPos(g_Window, &mouse_x, &mouse_y);
+ mouse_x *= (float)display_w / w; // Convert mouse coordinates to pixels
+ mouse_y *= (float)display_h / h;
+ io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); // Mouse position, in pixels (set to -1,-1 if no mouse / on another screen, etc.)
+ }
+ else
+ {
+ io.MousePos = ImVec2(-1,-1);
+ }
+
+ for (int i = 0; i < 3; i++)
+ {
+ io.MouseDown[i] = g_MousePressed[i] || glfwGetMouseButton(g_Window, i) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
+ g_MousePressed[i] = false;
+ }
+
+ io.MouseWheel = g_MouseWheel;
+ g_MouseWheel = 0.0f;
+
+ // Hide OS mouse cursor if ImGui is drawing it
+ glfwSetInputMode(g_Window, GLFW_CURSOR, io.MouseDrawCursor ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL);
+
+ // Start the frame
+ ImGui::NewFrame();
+}
--- /dev/null
+// ImGui GLFW binding with OpenGL3 + shaders
+// https://github.com/ocornut/imgui
+
+struct GLFWwindow;
+
+bool ImGui_ImplGlfwGL3_Init(GLFWwindow* window, bool install_callbacks);
+void ImGui_ImplGlfwGL3_Shutdown();
+void ImGui_ImplGlfwGL3_NewFrame();
+
+// Use if you want to reset your rendering device without losing ImGui state.
+void ImGui_ImplGlfwGL3_InvalidateDeviceObjects();
+bool ImGui_ImplGlfwGL3_CreateDeviceObjects();
+
+// GLFW callbacks (installed by default if you enable 'install_callbacks' during initialization)
+// Provided here if you want to chain callbacks.
+// You can also handle inputs yourself and use those as a reference.
+void ImGui_ImplGlfwGL3_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
+void ImGui_ImplGlfwGL3_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
+void ImGui_ImplGlfwGL3_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
+void ImGui_ImplGlfwGL3_CharCallback(GLFWwindow* window, unsigned int c);
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+DSP::DSP(KKernel * kernel) : m_kernel(kernel), m_DSP_PDATA(0), m_DSP_PADR(0), m_DSP_PCFG(0x100) /*Write FIFO Empty (Read FIFO Empty Flag)*/, m_DSP_PSTS(0), m_DSP_PSEM(0), m_DSP_PMASK(0), m_DSP_PCLEAR(0), m_DSP_SEM(0)
+{
+ memset(m_DSP_CMD, 0, sizeof(m_DSP_CMD));
+ memset(m_DSP_REP, 0, sizeof(m_DSP_REP));
+}
+u8 DSP::Read8(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("DSP unknown u8 read from %08x",addr);
+ }
+ return 0;
+}
+u16 DSP::Read16(u32 addr)
+{
+ u16 temp;
+ LOG("DSP u16 read from %08x", addr);
+ switch (addr & 0xFFF)
+ {
+ case 0:
+ return m_DSP_PDATA;
+
+ case 4:
+ return m_DSP_PADR;
+ case 8:
+ return m_DSP_PCFG;
+ case 0xC:
+ return m_DSP_PSTS;
+ case 0x10:
+ return m_DSP_PSEM;
+ case 0x14:
+ return m_DSP_PMASK;
+ /*case 0x18:
+ return m_DSP_PCLEAR;*/
+ case 0x1C:
+ return m_DSP_SEM;
+ case 0x24:
+ m_DSP_PSTS &= ~(1 << 10);
+ temp = m_DSP_REP[0];
+ REPread(0);
+ return temp;
+ case 0x2C:
+ m_DSP_PSTS &= ~(1 << 11);
+ temp = m_DSP_REP[1];
+ REPread(1);
+ return temp;
+ case 0x34:
+ m_DSP_PSTS &= ~(1 << 12);
+ temp = m_DSP_REP[2];
+ REPread(2);
+ return temp;
+ default:
+ LOG("DSP unknown u16 read from %08x", addr);
+ }
+ return 0;
+}
+u32 DSP::Read32(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("DSP unknown u32 read from %08x", addr);
+ }
+ return 0;
+}
+
+void DSP::Write8(u32 addr, u8 data)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("DSP unknown write %02x to %08x", data, addr);
+ }
+}
+void DSP::Write16(u32 addr, u16 data)
+{
+ LOG("DSP write %04x to %08x", data, addr);
+ switch (addr & 0xFFF)
+ {
+ case 0:
+ m_DSP_PDATA = data;
+ break;
+ case 4:
+ m_DSP_PADR = data;
+ break;
+ case 8:
+ if (m_DSP_PCFG & 0x1 && !(data & 0x1))
+ Reset();
+ m_DSP_PCFG = data;
+ break;
+ case 0xC:
+ m_DSP_PSTS = data;
+ break;
+ case 0x10:
+ m_DSP_PSEM = data;
+ break;
+ case 0x14:
+ m_DSP_PMASK = data;
+ break;
+ case 0x18:
+ m_DSP_SEM &= ~data;
+ break;
+ /*case 0x1C:
+ m_DSP_SEM = data;
+ break;*/
+ case 0x20:
+ m_DSP_CMD[0] = data;
+ m_DSP_PSTS &= ~(1 << 13);
+ break;
+ case 0x28:
+ m_DSP_CMD[1] = data;
+ m_DSP_PSTS &= ~(1 << 14);
+ break;
+ case 0x30:
+ m_DSP_CMD[2] = data;
+ m_DSP_PSTS &= ~(1 << 15);
+ break;
+ default:
+ LOG("DSP unknown write %04x to %08x", data, addr);
+ }
+}
+void DSP::Write32(u32 addr, u32 data)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("DSP unknown write %08x to %08x", data, addr);
+ }
+}
+
+u16 DSP::DSPreadCMD(u8 numb)
+{
+ m_DSP_PSTS |= (1 << (12 + numb));
+ return m_DSP_CMD[numb];
+}
+void DSP::DSPwriteRES(u16 data, u8 numb)
+{
+ m_DSP_PSTS |= (1 << (10 + numb));
+ m_DSP_REP[numb] = data;
+}
+
+//HLE stuff
+
+void DSP::Reset()
+{
+ //this is the reset that must happen inorder for the DSP module to return
+ for (int i = 0; i < 3; i++)
+ {
+ DSPreadCMD(i);
+ DSPwriteRES(0x1,i); //the 1 can be any other val the vals are ignoerd
+ }
+ m_phase = 0;
+ //m_kernel->FireInterrupt(0x4a);
+ //this causes
+ //DSP u16 read from 1ed0301c
+ //DSP u16 read from 1ed0300c
+}
+void DSP::REPread(u8 id)
+{
+ if (id == 2)
+ {
+ if (m_phase == 0) //just for handshake
+ {
+ DSPreadCMD(0);
+ DSPwriteRES(0x1, 0); //must be 1
+ DSPreadCMD(1);
+ DSPwriteRES(0x1, 1); //must be 1
+ DSPreadCMD(2);
+ DSPwriteRES(0x1, 1); //must be 1
+ m_kernel->FireInterrupt(0x4a);
+ LOG("DSP 0");
+ }
+ else if (m_phase == 1)
+ {
+ DSPreadCMD(2);
+ DSPwriteRES(0x1, 2);
+
+ m_kernel->FireInterrupt(0x4a);
+ LOG("DSP 1");
+ }
+ else if (m_phase == 2)
+ {
+ DSPreadCMD(2);
+ DSPwriteRES(0x1, 2);
+
+
+ //it also updates the current PSTS state
+ //must be between 0 and 8
+ //Bit (3-1) select the funtion
+ //Bit (0) sets a flag r2
+
+ //function 0 only with flag not set else it dose nothing
+
+ //function 1-3 are the same
+ //flag not set else nothing happen
+ //signales a event
+ m_DSP_PSTS |= 0x140; //why do I have to send data when I recv data
+
+ DSPwriteRES(4, 0);//this should contain something for the data recv
+
+ m_kernel->FireInterrupt(0x4a);
+ LOG("DSP 4");
+ }
+ else if (m_phase == 3) //this is part of the sending
+ {
+ DSPreadCMD(2);
+ DSPwriteRES(4, 0);//this should contain something for the data recv
+ m_kernel->FireInterrupt(0x4a);
+ LOG("DSP 5");
+ }
+ else if (m_phase == 4)
+ {
+ DSPreadCMD(2);
+ DSPwriteRES(0x1, 4);
+
+ m_kernel->FireInterrupt(0x4a);
+ LOG("DSP 2");
+ }
+ else if (m_phase == 5)
+ {
+ DSPreadCMD(2);
+ DSPwriteRES(0x1, 6);
+
+ m_kernel->FireInterrupt(0x4a);
+ LOG("DSP 3");
+ }
+ m_phase++;
+ }
+}
+void DSP::CMDwrite(u8 id)
+{
+ DSPreadCMD(id); //this is needed
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+GPIO::GPIO(KKernel * kernel) : m_kernel(kernel), m_IO(0), m_DIR(0)
+{
+}
+u8 GPIO::Read8(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("GPIO unknown u8 read from %08x",addr);
+ }
+ return 0;
+}
+u16 GPIO::Read16(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ case 0x14:
+ //return ~(m_DIR >> 16);
+ return 0xFFFF;
+ default:
+ LOG("GPIO unknown u16 read from %08x", addr);
+ }
+ return 0;
+}
+u32 GPIO::Read32(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ case 0x24: //direction? not sure but looks like
+ return m_DIR;
+ default:
+ LOG("GPIO unknown u32 read from %08x", addr);
+ }
+ return 0;
+}
+
+void GPIO::Write8(u32 addr, u8 data)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("GPIO unknown write %02x to %08x", data, addr);
+ }
+}
+void GPIO::Write16(u32 addr, u16 data)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("GPIO unknown write %04x to %08x", data, addr);
+ }
+}
+void GPIO::Write32(u32 addr, u32 data)
+{
+ switch (addr & 0xFFF)
+ {
+ case 0x24:
+ m_DIR = data;
+ break;
+ default:
+ LOG("GPIO unknown write %08x to %08x", data, addr);
+ }
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+namespace GPU {
+ template <typename T>
+ void Read(T &var, const u32 addr);
+
+ template <typename T>
+ void Write(u32 addr, const T data);
+} // namespace
+
+
+GPUHW::GPUHW(KKernel * kernel) : m_kernel(kernel)
+{
+ top = new Syncer(this, false);
+ bot = new Syncer(this, true);
+ memset(m_data, 0, sizeof(m_data));
+}
+u8 GPUHW::Read8(u32 addr)
+{
+ u8 data;
+ GPU::Read<u8>(data,addr);
+ return data;
+ /*switch (addr & 0x1FFFF)
+ {
+ default:
+ LOG("GPUHW unknown u8 read from %08x",addr);
+ }
+ return 0;*/
+}
+u16 GPUHW::Read16(u32 addr)
+{
+ u16 data;
+ GPU::Read<u16>(data, addr);
+ return data;
+ /*switch (addr & 0x1FFFF)
+ {
+ default:
+ LOG("GPUHW unknown u16 read from %08x", addr);
+ }
+ return 0;*/
+}
+u32 GPUHW::Read32(u32 addr)
+{
+ u32 data;
+ LOG("GPUHW unknown u32 read from %08x", addr);
+ GPU::Read<u32>(data, addr);
+ return data;
+ /*switch (addr & 0x1FFFF)
+ {
+ default:
+ LOG("GPUHW unknown u32 read from %08x", addr);
+ return m_data[(addr & 0x1FFFF)/ 4];
+ }
+ return 0;*/
+}
+
+void GPUHW::Write8(u32 addr, u8 data)
+{
+ GPU::Write<u8>(addr, data);
+ return;
+ /*
+ switch (addr & 0x1FFFF)
+ {
+ default:
+ LOG("GPUHW unknown write %02x to %08x", data, addr);
+ }*/
+}
+void GPUHW::Write16(u32 addr, u16 data)
+{
+ GPU::Write<u16>(addr, data);
+ return;
+ /*
+ switch (addr & 0x1FFFF)
+ {
+ default:
+ LOG("GPUHW unknown write %04x to %08x", data, addr);
+ }*/
+}
+void GPUHW::Write32(u32 addr, u32 data)
+{
+ GPU::Write<u32>(addr, data);
+ LOG("GPUHW unknown write %08x to %08x", data, addr);
+ return;
+ /*
+ switch (addr & 0x1FFFF)
+ {
+ case 0x1C: //Memory Fill1 "PSC0"
+ {
+ LOG("GPUHW unknown write %08x to %08x", data, addr);
+ if (data & 0x1 == 1)
+ m_kernel->FireInterrupt(0x29);
+ break;
+ }
+ case 0x2C: //Memory Fill2 "PSC1"
+ {
+ LOG("GPUHW unknown write %08x to %08x", data, addr);
+ if (data&0x1 == 1)
+ m_kernel->FireInterrupt(0x28);
+ break;
+ }
+ case 0xc18: //PFF start
+ {
+ LOG("GPUHW unknown write %08x to %08x", data, addr);
+ if (data == 1)
+ m_kernel->FireInterrupt(0x2C);
+ break;
+ }
+ case 0x18f0:
+ {
+ LOG("GPUHW unknown write %08x to %08x", data, addr);
+ if (data == 1)
+ m_kernel->FireInterrupt(0x2D);
+ break;
+ }
+ //0x2C PFF
+ //0x2B PDC1/VBlankTop
+ //0x2A PDC0/VBlankTop
+ //0x29 PSC0
+ //0x28 PSC1
+ default:
+ LOG("GPUHW unknown write %08x to %08x", data, addr);
+ }*/
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+extern "C" void VBlankCallback();
+
+Syncer::Syncer(GPUHW *owner, bool bottom) : m_owner(owner), m_bottom(bottom)
+{
+ m_owner->m_kernel->m_Timedevent.AddItem(this);
+ m_owner->m_kernel->FireNextTimeEvent(this, 4468724); //60 times per sec
+}
+Syncer::~Syncer()
+{
+ KLinkedListNode<KTimeedEvent> *t = m_owner->m_kernel->m_Timedevent.list;
+ while (t)
+ {
+ if (t->data == this)
+ m_owner->m_kernel->m_Timedevent.RemoveItem(t);
+ t = t->next;
+ }
+}
+void Syncer::trigger_event()
+{
+ if (m_bottom)
+ m_owner->m_kernel->FireInterrupt(0x2B); //PDC1/VBlankBottom
+ else
+ {
+ VBlankCallback();
+ m_owner->m_kernel->FireInterrupt(0x2A); //PDC0/VBlankTop
+ m_owner->m_kernel->FireInterrupt(0x64); //HID interrupt to signal new buttonpressed data is here
+ }
+ m_owner->m_kernel->FireNextTimeEvent(this, 4468724); //60 times per sec
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+static const unsigned int sha256_k[64] = //UL = uint32
+{ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };
+
+
+#define SHA2_SHFR(x, n) (x >> n)
+#define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
+#define SHA2_ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
+#define SHA2_CH(x, y, z) ((x & y) ^ (~x & z))
+#define SHA2_MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+#define SHA256_F1(x) (SHA2_ROTR(x, 2) ^ SHA2_ROTR(x, 13) ^ SHA2_ROTR(x, 22))
+#define SHA256_F2(x) (SHA2_ROTR(x, 6) ^ SHA2_ROTR(x, 11) ^ SHA2_ROTR(x, 25))
+#define SHA256_F3(x) (SHA2_ROTR(x, 7) ^ SHA2_ROTR(x, 18) ^ SHA2_SHFR(x, 3))
+#define SHA256_F4(x) (SHA2_ROTR(x, 17) ^ SHA2_ROTR(x, 19) ^ SHA2_SHFR(x, 10))
+#define SHA2_UNPACK32(x, str) \
+{ \
+ *((str) + 3) = (u8) ((x) ); \
+ *((str) + 2) = (u8) ((x) >> 8); \
+ *((str) + 1) = (u8) ((x) >> 16); \
+ *((str) + 0) = (u8) ((x) >> 24); \
+}
+#define SHA2_PACK32(str, x) \
+{ \
+ *(x) = ((u32) *((str) + 3) ) \
+ | ((u32) *((str) + 2) << 8) \
+ | ((u32) *((str) + 1) << 16) \
+ | ((u32) *((str) + 0) << 24); \
+}
+
+
+HWHASH::HWHASH(KKernel * kernel) : m_kernel(kernel), HASH_CNT(0)
+{
+
+}
+u8 HWHASH::Read8(u32 addr)
+{
+ LOG("HASH u8 read from %08x", addr);
+ return 0;
+}
+u16 HWHASH::Read16(u32 addr)
+{
+ LOG("HASH u16 read from %08x", addr);
+ return 0;
+}
+u32 HWHASH::Read32(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ case 0:
+ return HASH_CNT;
+ case 0x4:
+ return m_total_len;
+ case 0x40:
+ LOG("");
+ return m_h[0];
+ case 0x44:
+ return m_h[1];
+ case 0x48:
+ return m_h[2];
+ case 0x4C:
+ return m_h[3];
+ case 0x50:
+ return m_h[4];
+ case 0x54:
+ return m_h[5];
+ case 0x58:
+ return m_h[6];
+ case 0x5C:
+ return m_h[7];
+ default:
+ LOG("HASH u32 read from %08x", addr);
+ break;
+ }
+ return 0;
+}
+
+void HWHASH::Write8(u32 addr, u8 data)
+{
+ LOG("IPC u8 write %08x (%02x)", addr, data);
+}
+void HWHASH::Write16(u32 addr, u16 data)
+{
+ LOG("HASH u16 write %08x (%04x)", addr, data);
+}
+void HWHASH::Write32(u32 addr, u32 data)
+{
+ switch (addr & 0xFFF)
+ {
+ case 0: //HASH_CNT
+ if (data & 0x1) //init
+ {
+ m_h[0] = 0x6a09e667;
+ m_h[1] = 0xbb67ae85;
+ m_h[2] = 0x3c6ef372;
+ m_h[3] = 0xa54ff53a;
+ m_h[4] = 0x510e527f;
+ m_h[5] = 0x9b05688c;
+ m_h[6] = 0x1f83d9ab;
+ m_h[7] = 0x5be0cd19;
+ m_len = 0;
+ m_total_len = 0;
+ }
+ if (data & 0x2)
+ {
+ m_HASH2->flush();
+ finalise();
+ }
+ if ((data & ~0x3) != 0x8)
+ LOG("HASH HASH_CNT (%08x)", data);
+ HASH_CNT = data & ~0x3;
+ break;
+ case 0x4:
+ m_total_len = data;
+ break;
+ case 0x40:
+ m_h[0] = data;
+ break;
+ case 0x44:
+ m_h[1] = data;
+ break;
+ case 0x48:
+ m_h[2] = data;
+ break;
+ case 0x4C:
+ m_h[3] = data;
+ break;
+ case 0x50:
+ m_h[4] = data;
+ break;
+ case 0x54:
+ m_h[5] = data;
+ break;
+ case 0x58:
+ m_h[6] = data;
+ break;
+ case 0x5C:
+ m_h[7] = data;
+ break;
+ default:
+ LOG("HASH u32 write %08x (%08x)", addr, data);
+ break;
+ }
+}
+void HWHASH::finalise()
+{
+ unsigned int block_nb;
+ unsigned int pm_len;
+ unsigned int len_b;
+ int i;
+ block_nb = (1 + ((SHA256_BLOCK_SIZE - 9)
+ < (m_len % SHA256_BLOCK_SIZE)));
+ len_b = (m_total_len + m_len) << 3;
+ pm_len = block_nb << 6;
+ memset(m_block + m_len, 0, pm_len - m_len);
+ m_block[m_len] = 0x80;
+ SHA2_UNPACK32(len_b, m_block + pm_len - 4);
+ transform(m_block, block_nb);
+ for (i = 0; i < 8; i++) {
+ u32 temp = m_h[i];
+ SHA2_UNPACK32(temp, (u8*)&m_h[i]);
+ }
+}
+void HWHASH::transform(const u8 *message, u32 block_nb)
+{
+ u32 w[64];
+ u32 wv[8];
+ u32 t1, t2;
+ const unsigned char *sub_block;
+ int i;
+ int j;
+ for (i = 0; i < (int)block_nb; i++) {
+ sub_block = message + (i << 6);
+ for (j = 0; j < 16; j++) {
+ SHA2_PACK32(&sub_block[j << 2], &w[j]);
+ }
+ for (j = 16; j < 64; j++) {
+ w[j] = SHA256_F4(w[j - 2]) + w[j - 7] + SHA256_F3(w[j - 15]) + w[j - 16];
+ }
+ for (j = 0; j < 8; j++) {
+ wv[j] = m_h[j];
+ }
+ for (j = 0; j < 64; j++) {
+ t1 = wv[7] + SHA256_F2(wv[4]) + SHA2_CH(wv[4], wv[5], wv[6])
+ + sha256_k[j] + w[j];
+ t2 = SHA256_F1(wv[0]) + SHA2_MAJ(wv[0], wv[1], wv[2]);
+ wv[7] = wv[6];
+ wv[6] = wv[5];
+ wv[5] = wv[4];
+ wv[4] = wv[3] + t1;
+ wv[3] = wv[2];
+ wv[2] = wv[1];
+ wv[1] = wv[0];
+ wv[0] = t1 + t2;
+ }
+ for (j = 0; j < 8; j++) {
+ m_h[j] += wv[j];
+ }
+ }
+}
+void HWHASH::update(const u8 *message, u32 len)
+{
+ unsigned int block_nb;
+ unsigned int new_len, rem_len, tmp_len;
+ const unsigned char *shifted_message;
+ tmp_len = SHA256_BLOCK_SIZE - m_len;
+ rem_len = len < tmp_len ? len : tmp_len;
+ memcpy(&m_block[m_len], message, rem_len);
+ if (m_len + len < SHA256_BLOCK_SIZE) {
+ m_len += len;
+ return;
+ }
+ new_len = len - rem_len;
+ block_nb = new_len / SHA256_BLOCK_SIZE;
+ shifted_message = message + rem_len;
+ transform(m_block, 1);
+ transform(shifted_message, block_nb);
+ rem_len = new_len % SHA256_BLOCK_SIZE;
+ memcpy(m_block, &shifted_message[block_nb << 6], rem_len);
+ m_len = rem_len;
+ m_total_len += (block_nb + 1) << 6;
+}
+
+HWHASH2::HWHASH2(KKernel * kernel, HWHASH * hash1) : m_kernel(kernel), m_hash1(hash1), m_curret(0)
+{
+ hash1->m_HASH2 = this;
+}
+u8 HWHASH2::Read8(u32 addr)
+{
+ LOG("HASH u8 read from %08x", addr);
+ return 0;
+}
+u16 HWHASH2::Read16(u32 addr)
+{
+ LOG("HASH u16 read from %08x", addr);
+ return 0;
+}
+u32 HWHASH2::Read32(u32 addr)
+{
+ LOG("HASH u32 read from %08x", addr);
+ return 0;
+}
+
+void HWHASH2::Write8(u32 addr, u8 data)
+{
+ LOG("IPC u8 write %08x (%02x)", addr, data);
+}
+void HWHASH2::Write16(u32 addr, u16 data)
+{
+ LOG("HASH u16 write %08x (%04x)", addr, data);
+}
+void HWHASH2::Write32(u32 addr, u32 data)
+{
+ if ((addr & 0xFFF) <= 0x40)
+ {
+ //printf("%08x", data);
+ m_buffer[(m_curret << 2) + 3] = data >> 24;
+ m_buffer[(m_curret << 2) + 2] = data >> 16;
+ m_buffer[(m_curret << 2) + 1] = data >> 8;
+ m_buffer[(m_curret << 2) + 0] = data;
+ m_curret++;
+ if (m_curret == 0x10)
+ {
+ m_curret = 0;
+ m_hash1->update(m_buffer, 0x40);
+ }
+ }
+ else
+ LOG("HASH u32 write %08x (%08x)", addr, data);
+}
+void HWHASH2::flush()
+{
+ if (m_curret != 0)
+ m_hash1->update(m_buffer, m_curret * 4);
+ m_curret = 0;
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+
+extern "C" int citraPressedkey;
+HID::HID(KKernel * kernel) : m_kernel(kernel), m_IO(0), m_DIR(0)
+{
+}
+u8 HID::Read8(u32 addr)
+{
+ LOG("HID unknown u8 read from %08x",addr);
+ return 0;
+}
+u16 HID::Read16(u32 addr)
+{
+ if ((addr & 0xFFF) == 0)
+ {
+ LOG("hid read key %04x", citraPressedkey);
+ return citraPressedkey;
+ }
+ LOG("HID unknown u16 read from %08x", addr);
+ return 0;
+}
+
+u32 HID::Read32(u32 addr)
+{
+ if ((addr & 0xFFF) == 0)
+ {
+ LOG("hid read key %08x", citraPressedkey);
+ return citraPressedkey;
+ }
+
+ LOG("HID unknown u32 read from %08x", addr);
+ return 0;
+}
+void HID::Write8(u32 addr, u8 data)
+{
+ LOG("HID unknown write %02x to %08x", data, addr);
+}
+void HID::Write16(u32 addr, u16 data)
+{
+ LOG("HID unknown write %04x to %08x", data, addr);
+}
+void HID::Write32(u32 addr, u32 data)
+{
+ LOG("HID unknown write %08x to %08x", data, addr);
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+#define LOGI2C
+
+IOI2C::IOI2C(KKernel * kernel) : m_kernel(kernel)
+{
+ m_CNT = 0xFF;
+#ifdef LOGI2C
+ m_index = 0;
+#endif
+}
+u8 IOI2C::Read8(u32 addr)
+{
+ //LOG("I2C u8 read from %08x", addr);
+ switch (addr & 0xFFF)
+ {
+ case 0x0:
+ return m_data;
+ case 0x1:
+ return 0x10;
+ default:
+ LOG("I2C u8 read from %08x", addr);
+ }
+ return 0x0;
+}
+u16 IOI2C::Read16(u32 addr)
+{
+ LOG("I2C u16 read from %08x", addr);
+ return 0;
+}
+u32 IOI2C::Read32(u32 addr)
+{
+ LOG("I2C u32 read from %08x", addr);
+ return 0;
+}
+
+void IOI2C::Write8(u32 addr, u8 data)
+{
+ //LOG("I2C u8 write %08x (%02x)", addr, data);
+ switch (addr & 0xFFF)
+ {
+ case 0x0:
+#ifdef LOGI2C
+ if (sizeof(m_buffer) > m_index)
+ {
+ m_buffer[m_index] = data;
+ }
+#endif
+ {
+ bool m_ack = true;
+ if (!(m_CNT & 0x20))
+ Write(data, m_deviceID, m_CNT & 0x1, m_ack);
+ }
+ m_data = data;
+ break;
+ case 0x1:
+ if (data & 0x2) //Start
+ {
+ m_deviceID = m_data;
+ }
+ if (data & 0x80)
+ {
+ bool m_ack = true;
+ if (data & 0x20)
+ Read(m_data, m_deviceID, data & 0x1, m_ack);
+ }
+#ifdef LOGI2C
+ m_index++;
+ if (data & 0x1) //Stop
+ {
+ if (data & 0x20)
+ {
+ LOG("I2C read (%02x) (%08x) end", m_deviceID, m_index); //this is of by one when the direction swap
+ }
+ if (!(data & 0x20))
+ {
+ LOG("I2C write (%02x) (%08x) end", m_deviceID, m_index); //this is of by one when the direction swap
+ for (int i = 0; i < m_index; i++)
+ printf("%02x ", m_buffer[i]);
+ LOG("");
+ }
+ }
+ if (data & 0x2) //Start
+ {
+ m_index = 0;
+ }
+ if ((m_CNT & 0x20) != (data & 0x20))
+ {
+ if (m_CNT & 0x20)
+ {
+ LOG("I2C read (%02x) (%08x)", m_deviceID, m_index); //this is of by one when the direction swap
+ }
+ else
+ {
+ LOG("I2C write (%02x) (%08x)", m_deviceID, m_index); //this is of by one when the direction swap
+ for (int i = 0; i < m_index; i++)
+ printf("%02x ", m_buffer[i]);
+ LOG("");
+ }
+ m_index = 0;
+ }
+#endif
+ m_CNT = data;
+ break;
+ default:
+ LOG("I2C u8 write %08x (%02x)", addr, data);
+ break;
+ }
+}
+void IOI2C::Write16(u32 addr, u16 data)
+{
+ LOG("I2C u16 write %08x (%04x)", addr, data);
+}
+void IOI2C::Write32(u32 addr, u32 data)
+{
+ LOG("I2C u32 write %08x (%08x)", addr, data);
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+//todo Interrupt
+
+HWIPC::HWIPC(KKernel * kernel)
+{
+ m_kernel = kernel;
+ m_IPCSYNCP9 = 0;
+ m_IPCSYNCP11 = 0;
+ m_IPCIRQ = 0;
+ m_SENDFIFOSTAT = 1; //Send Fifo Empty
+ m_RECVFIFOSTAT_ERROR = 1; //Receive Fifo Empty
+ m_recvarm9.data[0] = 0;
+ m_recvarm9.size = 0;
+ m_recvarm9.nextwrite = 0;
+ m_recvarm9.nextread = 0;
+ m_sendarm9.data[0] = 0;
+ m_sendarm9.size = 0;
+ m_sendarm9.nextwrite = 0;
+ m_sendarm9.nextread = 0;
+}
+u32 HWIPC::FIFOget(struct FIFO* FIFO) //nin fifo with its own funtions
+{
+ s32 readfrom;
+ if (FIFO->size == 0)
+ {
+ readfrom = FIFO->nextread - 1;
+ }
+ else
+ {
+ readfrom = FIFO->nextread;
+ FIFO->size--;
+ FIFO->nextread = (FIFO->nextread + 1) < FIFOSIZE ? (FIFO->nextread + 1) : 0;
+ }
+ readfrom = readfrom < 0 ? FIFOSIZE - 1 : readfrom ;
+ return FIFO->data[readfrom];
+}
+void HWIPC::FIFOset(struct FIFO* FIFO, u32 data) //nin fifo with its own funtions
+{
+ if (FIFO->size == FIFOSIZE)
+ {
+ return;
+ }
+ else
+ {
+ FIFO->data[FIFO->nextwrite] = data;
+ FIFO->size++;
+ FIFO->nextwrite = (FIFO->nextwrite + 1) < FIFOSIZE ? (FIFO->nextwrite + 1) : 0;
+ }
+}
+u8 HWIPC::Read8(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ case 0:
+ return m_IPCSYNCP9;
+ case 1:
+ return m_IPCSYNCP11;
+ case 2:
+ return m_IPCIRQ;
+ case 3:
+ return 0;
+ case 4:
+ return m_SENDFIFOSTAT;
+ case 5:
+ return m_RECVFIFOSTAT_ERROR;
+ case 6:
+ return 0;
+ case 7:
+ return 0;
+ default:
+ LOG("IPC u8 read from %08x", addr);
+ return 0;
+ }
+}
+u16 HWIPC::Read16(u32 addr)
+{
+ if ((addr & 0xFFF) <= 6)
+ return (Read8(addr) | ((u16)Read8(addr + 1) << 8));
+ else
+ LOG("IPC u16 read from %08x", addr);
+ return 0;
+}
+u32 HWIPC::Read32(u32 addr)
+{
+ if ((addr &0xFFF) <= 4)
+ return (Read8(addr) | ((u32)Read8(addr + 1) << 8) | ((u32)Read8(addr + 2) << 16) | ((u32)Read8(addr + 3) << 24));
+ else if ((addr & 0xFFF) == 0xC)
+ {
+ if (m_recvarm9.size == 0)
+ {
+ m_RECVFIFOSTAT_ERROR |= 0x40; //error
+ LOG("IPC u32 reading empty fifo");
+ }
+
+ u32 ret = FIFOget(&m_recvarm9);
+
+ if (m_recvarm9.size == 0)
+ m_RECVFIFOSTAT_ERROR |= 0x1; //Empty
+ m_RECVFIFOSTAT_ERROR &= ~0x2; //not Full
+
+ FIFOReadBackCall();
+
+ return ret;
+ }
+ else
+ LOG("IPC u32 read from %08x", addr);
+ return 0;
+}
+
+void HWIPC::Write8(u32 addr,u8 data)
+{
+ switch (addr & 0xFFF)
+ {
+ case 0:
+ break;
+ case 1:
+ m_IPCSYNCP11 = data&0x4F;
+ if (data & 0x20)
+ FIFOIRQOLD();
+ break;
+ case 2:
+ if (data & 0x40) //irq enable bit possition changed on 3DS (cmp to DS)
+ FIFOIRQ();
+ m_IPCIRQ = data & 0x80; //irq bit possition changed on 3DS (cmp to DS)
+ break;
+ case 3:
+ break;
+ case 4:
+ if (data & 0x8)
+ {
+ m_sendarm9.data[0] = 0;
+ m_sendarm9.size = 0;
+ m_sendarm9.nextwrite = 0;
+ m_sendarm9.nextread = 0;
+ m_SENDFIFOSTAT = 1; //Send Fifo Empty
+ FIFOReadBackCall();
+ }
+ m_SENDFIFOSTAT = (m_SENDFIFOSTAT & ~0x4) | data & 0x4;
+ break;
+ case 5:
+ m_RECVFIFOSTAT_ERROR = (m_RECVFIFOSTAT_ERROR & ~0x84) | data & 0x84;
+ if (data & 0x40)
+ m_RECVFIFOSTAT_ERROR &= 0x40;
+ break;
+ case 6:
+ break;
+ case 7:
+ break;
+ default:
+ LOG("IPC u8 write %08x (%02x)", addr, data);
+ }
+}
+void HWIPC::Write16(u32 addr, u16 data)
+{
+ if ((addr & 0xFFF) <= 6)
+ {
+ Write8(addr, (u8)data);
+ Write8(addr + 1, (u8)(data << 8));
+ }
+ else
+ LOG("IPC u16 write %08x (%04x)", addr, data);
+}
+void HWIPC::Write32(u32 addr, u32 data)
+{
+ if ((addr & 0xFFF) <= 4)
+ {
+ Write8(addr, data);
+ Write8(addr + 1, data << 8);
+ Write8(addr + 2, data << 16);
+ Write8(addr + 3, data << 24);
+ }
+ else if ((addr & 0xFFF) == 0x8)
+ {
+ if (m_sendarm9.size == FIFOSIZE)
+ {
+ m_RECVFIFOSTAT_ERROR |= 0x40; //error
+ LOG("IPC u32 writing full fifo");
+ }
+
+ FIFOset(&m_sendarm9,data);
+
+ if (m_sendarm9.size == FIFOSIZE)
+ m_SENDFIFOSTAT |= 0x2; //Full
+ m_SENDFIFOSTAT &= ~0x1; //not Empty
+
+ FIFOWriteBackCall();
+
+ }
+ else
+ LOG("IPC u32 write %08x (%08x)", addr, data);
+}
+u32 HWIPC::FIFOp9read()
+{
+ u32 ret = FIFOget(&m_sendarm9);
+
+ if (m_sendarm9.size == 0)
+ {
+ m_SENDFIFOSTAT |= 0x1; //Empty
+ m_kernel->FireInterrupt(0x53); //Send Fifo Empty IRQ
+ }
+ m_SENDFIFOSTAT &= ~0x2; //not Full
+
+ return ret;
+}
+void HWIPC::FIFOp9write(u32 data)
+{
+ FIFOset(&m_recvarm9, data);
+
+ if (m_recvarm9.size == FIFOSIZE)
+ m_RECVFIFOSTAT_ERROR |= 0x2; //Full
+ m_RECVFIFOSTAT_ERROR &= ~0x1; //not Empty
+ m_kernel->FireInterrupt(0x52); //Receive Fifo Not Empty
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+
+MIC::MIC(KKernel * kernel) : m_kernel(kernel)
+{
+}
+u8 MIC::Read8(u32 addr)
+{
+ LOG("MIC unknown u8 read from %08x",addr);
+ return 0;
+}
+u16 MIC::Read16(u32 addr)
+{
+ if ((addr & 0xFFF) == 0)
+ {
+ return m_CNT;
+ }
+ LOG("MIC unknown u16 read from %08x", addr);
+ return 0;
+}
+
+u32 MIC::Read32(u32 addr)
+{
+ if ((addr & 0xFFF) == 4)
+ {
+ return 0; //mic data
+ }
+ LOG("MIC unknown u32 read from %08x", addr);
+ return 0;
+}
+void MIC::Write8(u32 addr, u8 data)
+{
+ LOG("MIC unknown write %02x to %08x", data, addr);
+}
+void MIC::Write16(u32 addr, u16 data)
+{
+ if ((addr & 0xFFF) == 0)
+ {
+ m_CNT = data;
+ return;
+ }
+ LOG("MIC unknown write %04x to %08x", data, addr);
+}
+void MIC::Write32(u32 addr, u32 data)
+{
+ LOG("MIC unknown write %08x to %08x", data, addr);
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+#define LOGI2C
+
+PDN::PDN(KKernel * kernel) : m_kernel(kernel), m_SPI_CNT(0)
+{
+}
+u8 PDN::Read8(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("PDN unknown u8 read from %08x",addr);
+ }
+ return 0;
+}
+u16 PDN::Read16(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ case 0x1c0:
+ return m_SPI_CNT;
+ default:
+ LOG("PDN unknown u16 read from %08x", addr);
+ }
+ return 0;
+}
+u32 PDN::Read32(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("PDN unknown u32 read from %08x", addr);
+ }
+ return 0;
+}
+
+void PDN::Write8(u32 addr, u8 data)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("PDN unknown write %02x to %08x", data, addr);
+ }
+}
+void PDN::Write16(u32 addr, u16 data)
+{
+ switch (addr & 0xFFF)
+ {
+ case 0x1c0:
+ m_SPI_CNT = data;
+ return;
+ default:
+ LOG("PDN unknown write %04x to %08x", data, addr);
+ }
+}
+void PDN::Write32(u32 addr, u32 data)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("GPIO unknown write %08x to %08x", data, addr);
+ }
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+#define LOGI2C
+
+SPI::SPI(KKernel * kernel) : m_kernel(kernel), SPI_NEW_CNT(0)
+{
+}
+u8 SPI::Read8(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("SPI unknown u8 read from %08x",addr);
+ }
+ return 0;
+}
+u16 SPI::Read16(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("SPI unknown u16 read from %08x", addr);
+ }
+ return 0;
+}
+u32 SPI::Read32(u32 addr)
+{
+ switch (addr & 0xFFF)
+ {
+ case 0x800:
+ return SPI_NEW_CNT;
+ default:
+ LOG("SPI unknown u32 read from %08x", addr);
+ }
+ return 0;
+}
+
+void SPI::Write8(u32 addr, u8 data)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("SPI unknown write %02x to %08x", data, addr);
+ }
+}
+void SPI::Write16(u32 addr, u16 data)
+{
+ switch (addr & 0xFFF)
+ {
+ default:
+ LOG("SPI unknown write %04x to %08x", data, addr);
+ }
+}
+void SPI::Write32(u32 addr, u32 data)
+{
+ switch (addr & 0xFFF)
+ {
+ case 0x800:
+ SPI_NEW_CNT = data &~0x8000; //always ready
+ default:
+ LOG("SPI unknown write %08x to %08x", data, addr);
+ }
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+HWBUS1::HWBUS1(KKernel * kernel) : IOI2C(kernel)
+{
+ m_active = false;
+}
+
+bool HWBUS1::Read(u8 &data, u8 device, bool end, bool &noack)
+{
+ XDSERROR("unknown bus %02x", device);
+ data = 0;
+ return true;
+}
+bool HWBUS1::Write(u8 &data, u8 device, bool end, bool &noack)
+{
+ XDSERROR("unknown bus %02x", device);
+ return true;
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+HWBUS2::HWBUS2(KKernel * kernel) : IOI2C(kernel)
+{
+ m_active = false;
+}
+
+bool HWBUS2::Read(u8 &data, u8 device, bool end, bool &noack)
+{
+ data = 0x1;
+ switch (device)
+ {
+ case 0x4B: //MCU 8 Bit addresses (read only)
+ {
+ switch (m_register)
+ {
+ case 0xF: // ShellState 1 Byte? open here
+ data = 0xFF;
+ break;
+ }
+ break;
+ }
+ default:
+ XDSERROR("unknown device %02x", device);
+ break;
+ }
+
+ m_active = true;
+ if (end)
+ m_active = false;
+ return true;
+}
+bool HWBUS2::Write(u8& data, u8 device, bool end, bool &noack)
+{
+
+ switch (device)
+ {
+ case 0x4A: //MCU 8 Bit addresses (write only)
+ {
+ if (!m_active)
+ m_register = data;
+ {
+
+ }
+ break;
+ }
+ default:
+ XDSERROR("unknown device %02x", device);
+ break;
+ }
+ m_active = true;
+ if (end)
+ m_active = false;
+ return true;
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+
+HWBUS3::HWBUS3(KKernel * kernel) : IOI2C(kernel)
+{
+ m_active = false;
+}
+
+bool HWBUS3::Read(u8 &data, u8 device, bool end, bool &noack)
+{
+ XDSERROR("unknown bus %02x", device);
+ data = 0;
+ return true;
+}
+bool HWBUS3::Write(u8 &data, u8 device, bool end, bool &noack)
+{
+ XDSERROR("unknown bus %02x", device);
+ return true;
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+
+
+KAddressArbiter::KAddressArbiter(KProcess* owner) : arbiterlist()
+{
+ m_owner = owner;
+}
+
+KAddressArbiter::~KAddressArbiter()
+{
+
+}
+bool KAddressArbiter::IsInstanceOf(ClassName name) {
+ if (name == KAddressArbiter::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
+static KLinkedListNode<KThread>* threads_ArbitrateHighestPrioThread(KLinkedList<KThread> *list, u32 addr) //this also removes
+{
+ KLinkedListNode<KThread> *ret = NULL;
+ s32 highest_prio = 0x80;
+ KLinkedListNode<KThread>* node = list->list;
+ while (node) {
+ if (node->data->arb_addr == addr) {
+ if (node->data->m_thread_prio <= highest_prio) {
+ ret = node;
+ highest_prio = node->data->m_thread_prio;
+ }
+ }
+ node = node->next;
+ }
+
+ return ret;
+}
+Result KAddressArbiter::ArbitrateAddress(u32 addr,u32 type, s32 val, s64 time, KThread * caller)
+{
+ KLinkedListNode<KThread>* p;
+ switch (type) {
+ case 0: // Free
+ // Negative value means resume all threads
+ if (val < 0) {
+ while (p = threads_ArbitrateHighestPrioThread(&arbiterlist, addr))
+ {
+ m_owner->m_Kernel->StartThread(p->data);
+ arbiterlist.RemoveItem(p);
+ }
+ return 0;
+ }
+
+ // Resume first N threads
+ for (int i = 0; i<val; i++) {
+ if (p = threads_ArbitrateHighestPrioThread(&arbiterlist, addr))
+ {
+ m_owner->m_Kernel->StartThread(p->data);
+ arbiterlist.RemoveItem(p);
+ }
+ else break;
+ }
+
+ return 0;
+
+ case 1: // Acquire
+ // If (signed cmp) value >= *addr, the thread is locked and added to wait-list.
+ // Otherwise, thread keeps running and returns 0.
+ u32 data;
+ if (caller->m_owner->getMemoryMap()->Read32(addr, data) != Success)
+ {
+ return -1;
+ }
+ if (val > (s32)data)
+ {
+ m_owner->m_Kernel->ReScheduler();
+ m_owner->m_Kernel->StopThread(caller);
+ caller->arb_addr = addr;
+ arbiterlist.AddItem(caller);
+ }
+
+ return 0;
+
+ /*case 3: // Acquire Timeout
+ if (value >= (s32)mem_Read32(addr))
+ threads_SetCurrentThreadArbitrationSuspend(arbiter, addr);
+
+ // XXX: Add timeout based on val2,3
+ return 0;
+
+ case 2: // Acquire Decrement
+ val_addr = mem_Read32(addr) - 1;
+ mem_Write32(addr, val_addr);
+
+ if (value >= (s32)val_addr)
+ threads_SetCurrentThreadArbitrationSuspend(arbiter, addr);
+
+ return 0;
+
+ case 4: // Acquire Decrement Timeout
+ val_addr = mem_Read32(addr) - 1;
+ mem_Write32(addr, val_addr);
+
+ if (value >= (s32)val_addr)
+ threads_SetCurrentThreadArbitrationSuspend(arbiter, addr);
+
+ // XXX: Add timeout based on val2,3
+ Reg[0] = 0;
+ break;*/
+
+ default:
+ LOG("Invalid arbiter type %u\n", type);
+ return 0xD8E093ED;
+ }
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+
+KAutoObject::KAutoObject() : m_refcount(0)
+{
+
+}
+void KAutoObject::AcquireReference() {
+ m_refcount++;
+}
+
+void KAutoObject::ReleaseReference() {
+ m_refcount--;
+
+ if(m_refcount == 0)
+ Destroy();
+}
+
+void KAutoObject::Destroy() {
+ delete this;
+ // Empty. Overridden.
+}
+
+bool KAutoObject::IsInstanceOf(ClassName name) {
+ return name == KAutoObject::name;
+}
--- /dev/null
+#include "Kernel.h"
+
+KAutoObjectRef::KAutoObjectRef(const KAutoObjectRef &obj) : m_object(obj.m_object)
+{
+ m_object->AcquireReference();
+}
+
+KAutoObjectRef::KAutoObjectRef(KAutoObject* object) {
+ m_object = object;
+ if (m_object)
+ m_object->AcquireReference();
+}
+
+KAutoObjectRef::KAutoObjectRef() {
+ m_object = NULL;
+}
+
+void KAutoObjectRef::SetObject(KAutoObject* object) {
+ if(m_object != NULL) {
+ m_object->ReleaseReference();
+ }
+
+ m_object = object;
+ m_object->AcquireReference();
+}
+
+KAutoObjectRef::~KAutoObjectRef() {
+ if(m_object != NULL)
+ m_object->ReleaseReference();
+}
+
+KAutoObject* KAutoObjectRef::operator*() {
+ return m_object;
+}
--- /dev/null
+#include "Kernel.h"
+
+//tools
+
+
+bool KClientPort::Synchronization(KThread* thread, u32 &error)
+{
+ return true; //stall
+}
+s32 KClientPort::connect(KClientSession* &sesion)
+{
+ if ( m_maxConnection > m_CurrentConnection)
+ {
+ //free the server so he can accept the connection
+ KThread* found = m_owner->m_Server.SynGetNextPrio();
+ if (found)
+ {
+ m_owner->m_Server.SynFree(0, found);
+ }
+ KSession* sesi = new KSession(m_owner);
+ m_owner->m_Server.m_sessionToTake.AddItem(sesi);
+ sesion = &sesi->m_Client;
+ return Success;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+KClientPort::KClientPort(char* name, u32 maxConnection, KPort *owner)
+{
+ m_maxConnection = maxConnection;
+ m_CurrentConnection = 0;
+ m_owner = owner;
+}
+
+bool KClientPort::IsInstanceOf(ClassName name) {
+ if (name == KClientPort::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
+
--- /dev/null
+#include "Kernel.h"
+
+//tools
+
+void KClientSession::Destroy() {
+ m_owner->m_Server.SynFreeAll(0xC920181A);
+ m_owner->m_Server.m_killed = true;
+}
+bool KClientSession::Synchronization(KThread* thread, u32 &error)
+{
+ KThread * tnew = m_owner->m_Server.SynGetNextPrio();
+ if (tnew && !m_owner->m_Server.m_processingCmd)
+ {
+ m_owner->m_Server.m_waitingForCmdResp = thread;
+ m_owner->m_Server.m_processingCmd = tnew;
+ m_owner->Communicate(thread,tnew , false);
+ m_owner->m_Server.SynFree(0, tnew);
+ }
+ return true; //stall
+}
+
+KClientSession::KClientSession(KSession *owner)
+{
+ m_owner = owner;
+}
+
+bool KClientSession::IsInstanceOf(ClassName name) {
+ if (name == KClientSession::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+
+static u32 Read32(uint8_t p[4])
+{
+ u32 temp = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+ return temp;
+}
+static u16 Read16(uint8_t p[2])
+{
+ u16 temp = p[0] | p[1] << 8;
+ return temp;
+}
+
+static u32 TranslateAddr(u32 addr, struct _3DSX_LoadInfo *d, u32* offsets)
+{
+ if (addr < offsets[0])
+ return d->segAddrs[0] + addr;
+ if (addr < offsets[1])
+ return d->segAddrs[1] + addr - offsets[0];
+ return d->segAddrs[2] + addr - offsets[1];
+}
+
+static u32 AlignPage(u32 in)
+{
+ return ((in + 0xFFF) / 0x1000) * 0x1000;
+}
+
+int KCodeSet::Load3DSXFile(FILE* f, u32 baseAddr)
+{
+ u32 i, j, k, m;
+
+ _3DSX_Header hdr;
+ if (fread(&hdr, sizeof(hdr), 1, f) != 1) {
+ XDSERROR("error reading 3DSX header");
+ return 2;
+ }
+
+ // Endian swap!
+#define ESWAP(_field, _type) \
+ hdr._field = le_##_type(hdr._field)
+ ESWAP(magic, word);
+ ESWAP(headerSize, hword);
+ ESWAP(relocHdrSize, hword);
+ ESWAP(formatVer, word);
+ ESWAP(flags, word);
+ ESWAP(codeSegSize, word);
+ ESWAP(rodataSegSize, word);
+ ESWAP(dataSegSize, word);
+ ESWAP(bssSize, word);
+#undef ESWAP
+
+ if (hdr.magic != _3DSX_MAGIC) {
+ XDSERROR("error not a 3DSX file");
+ return 3;
+ }
+
+ struct _3DSX_LoadInfo d;
+ d.segSizes[0] = (hdr.codeSegSize + 0xFFF) &~0xFFF;
+ d.segSizes[1] = (hdr.rodataSegSize + 0xFFF) &~0xFFF;
+ d.segSizes[2] = (hdr.dataSegSize + 0xFFF) &~0xFFF;
+ u32 offsets[2] = { d.segSizes[0], d.segSizes[0] + d.segSizes[1] };
+ u32 dataLoadSize = (hdr.dataSegSize - hdr.bssSize + 0xFFF) &~0xFFF;
+ u32 bssLoadSize = d.segSizes[2] - dataLoadSize;
+ u32 nRelocTables = hdr.relocHdrSize / 4;
+ u8* allMem = (u8*)malloc(d.segSizes[0] + d.segSizes[1] + d.segSizes[2] + (4 * 3 * nRelocTables));
+ if (!allMem)
+ return 3;
+ d.segAddrs[0] = baseAddr;
+ d.segAddrs[1] = d.segAddrs[0] + d.segSizes[0];
+ d.segAddrs[2] = d.segAddrs[1] + d.segSizes[1];
+ d.segPtrs[0] = (char*)allMem;
+ d.segPtrs[1] = (char*)d.segPtrs[0] + d.segSizes[0];
+ d.segPtrs[2] = (char*)d.segPtrs[1] + d.segSizes[1];
+
+ // Skip header for future compatibility.
+ fseek(f, hdr.headerSize, SEEK_SET);
+
+ // Read the relocation headers
+ u32* relocs = (u32*)((char*)d.segPtrs[2] + hdr.dataSegSize);
+
+ for (i = 0; i < 3; i++)
+ if (fread(&relocs[i*nRelocTables], nRelocTables * 4, 1, f) != 1) {
+ XDSERROR("error reading reloc header");
+ return 4;
+ }
+
+ // Read the segments
+ if (fread(d.segPtrs[0], hdr.codeSegSize, 1, f) != 1) {
+ XDSERROR("error reading code");
+ return 5;
+ }
+ if (fread(d.segPtrs[1], hdr.rodataSegSize, 1, f) != 1) {
+ XDSERROR("error reading rodata");
+ return 5;
+ }
+ if (fread(d.segPtrs[2], hdr.dataSegSize - hdr.bssSize, 1, f) != 1) {
+ XDSERROR("error reading data");
+ return 5;
+ }
+
+ // BSS clear
+ memset((char*)d.segPtrs[2] + hdr.dataSegSize - hdr.bssSize, 0, hdr.bssSize);
+
+ // Relocate the segments
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < nRelocTables; j++) {
+ u32 nRelocs = le_word(relocs[i*nRelocTables + j]);
+ if (j >= 2) {
+ // We are not using this table - ignore it
+ fseek(f, nRelocs*sizeof(_3DSX_Reloc), SEEK_CUR);
+ continue;
+ }
+
+#define RELOCBUFSIZE 512*4
+ _3DSX_Reloc relocTbl[RELOCBUFSIZE];
+
+ u32* pos = (u32*)d.segPtrs[i];
+ u32* endPos = pos + (d.segSizes[i] / 4);
+
+ while (nRelocs) {
+ u32 toDo = nRelocs > RELOCBUFSIZE ? RELOCBUFSIZE : nRelocs;
+ nRelocs -= toDo;
+
+ if (fread(relocTbl, toDo*sizeof(_3DSX_Reloc), 1, f) != 1)
+ return 6;
+
+ for (k = 0; k < toDo && pos < endPos; k++) {
+ //DEBUG("(t=%d,skip=%u,patch=%u)\n", j, (u32)relocTbl[k].skip, (u32)relocTbl[k].patch);
+ pos += le_hword(relocTbl[k].skip);
+ u32 num_patches = le_hword(relocTbl[k].patch);
+ for (m = 0; m < num_patches && pos < endPos; m++) {
+ u32 inAddr = (char*)pos - (char*)allMem;
+ u32 addr = TranslateAddr(le_word(*pos), &d, offsets);
+ //DEBUG("Patching %08X <-- rel(%08X,%d) (%08X)\n", baseAddr + inAddr, addr, j, le_word(*pos));
+ switch (j) {
+ case 0:
+ *pos = le_word(addr);
+ break;
+ case 1:
+ *pos = le_word(addr - inAddr);
+ break;
+ }
+ pos++;
+ }
+ }
+ }
+ }
+ }
+
+ // Write the data
+ m_code = (u8*)calloc(d.segSizes[0], 1);
+ m_rodata = (u8*)calloc(d.segSizes[1], 1);
+ m_data = (u8*)calloc((dataLoadSize + bssLoadSize), 1);
+
+ memcpy(m_code, allMem, d.segSizes[0]);
+ memcpy(m_rodata, allMem + d.segSizes[0], d.segSizes[1]);
+ memcpy(m_data, allMem + d.segSizes[0] + d.segSizes[1], dataLoadSize);
+
+ m_code_pages = d.segSizes[0]/0x1000;
+ m_rodata_pages = d.segSizes[1] / 0x1000;
+ m_data_pages = dataLoadSize / 0x1000;
+ m_bss_pages = bssLoadSize / 0x1000;
+
+ free(allMem);
+
+ LOG("CODE: %u pages", d.segSizes[0] / 0x1000);
+ LOG("RODATA: %u pages", d.segSizes[1] / 0x1000);
+ LOG("DATA: %u pages", dataLoadSize / 0x1000);
+ LOG("BSS: %u pages", bssLoadSize / 0x1000);
+
+ return 0; // Success.
+}
+void KCodeSet::LoadElfFile(u8 *addr)
+{
+ u32 *header = (u32*)addr;
+ u32 *phdr = (u32*)(addr + Read32((u8*)&header[7]));
+ u32 n = Read32((u8*)&header[11]) & 0xFFFF;
+ u32 i;
+
+ for (i = 0; i < n; i++, phdr += 8) {
+ if (phdr[0] != 1) // PT_LOAD
+ continue;
+
+ u32 off = Read32((u8*)&phdr[1]);
+ u32 dest = Read32((u8*)&phdr[3]);
+ u32 filesz = Read32((u8*)&phdr[4]);
+ u32 memsz = Read32((u8*)&phdr[5]);
+
+ //round up (this fixes bad malloc implementation in some homebrew)
+ memsz = (memsz + 0xFFF)&~0xFFF;
+
+ u8* data = (u8*)calloc(memsz, 1);
+ memcpy(data, addr + off, filesz);
+ //mem_AddSegment(dest, memsz, data);
+ switch (phdr[6] & 0x7)
+ {
+ case 5: //R_X
+ m_code = data;
+ m_code_pages = memsz / 0x1000;
+ break;
+ case 1: //R__
+ m_rodata = data;
+ m_rodata_pages = memsz / 0x1000;
+ break;
+ case 3: //RW_
+ m_data = data;
+ m_data_pages = filesz+0xFFF / 0x1000;
+ m_bss_pages = memsz / 0x1000;
+ break;
+ default:
+ XDSERROR("unknown elf section");
+ free(data);
+ break;
+ }
+ }
+}
+
+bool KCodeSet::Patch()
+{
+ char temp[0x80];
+ sprintf(temp, "HLE\\%s.3dsx", m_name);
+
+ FILE* f = fopen(temp,"rb");
+ if (f)
+ {
+ Load3DSXFile(f, 0x00100000);
+ fclose(f);
+ return true;
+ }
+ sprintf(temp, "HLE\\%s.elf", m_name);
+ f = fopen(temp, "rb");
+ if (f)
+ {
+ fseek(f, 0, SEEK_END);
+ u32 sz = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ u8* data = new u8[sz];
+ fread(data, sz, 1, f);
+ LoadElfFile(data);
+ delete data;
+ fclose(f);
+ return true;
+ }
+ return false;
+}
+KCodeSet::KCodeSet(u8* code_buf, u32 code_pages, u8* rodata_buf, u32 rodata_pages,
+ u8* data_buf, u32 data_pages, u32 bss_pages, u64 TitleID, const char* name)
+{
+ m_name = strdup(name);
+ m_TitleID = TitleID;
+
+ if (Patch())
+ return;
+
+ m_code = (u8*) calloc(code_pages, PAGE_SIZE);
+ m_rodata = (u8*) calloc(rodata_pages, PAGE_SIZE);
+ m_data = (u8*) calloc((data_pages + bss_pages), PAGE_SIZE);
+
+ if(m_code == NULL || m_rodata == NULL || m_data == NULL || m_name == NULL) {
+ XDSERROR("Out of memory.\n");
+ return;
+ }
+
+ memcpy(m_code, code_buf, code_pages * PAGE_SIZE);
+ memcpy(m_rodata, rodata_buf, rodata_pages * PAGE_SIZE);
+ memcpy(m_data, data_buf, data_pages * PAGE_SIZE);
+
+ /*FILE * pFile;
+ pFile = fopen("code.code", "wb");
+ if (pFile != NULL)
+ {
+ fwrite(code_buf, 1, code_pages * PAGE_SIZE, pFile);
+ fwrite(rodata_buf, 1, rodata_pages * PAGE_SIZE, pFile);
+ fwrite(data_buf, 1, data_pages * PAGE_SIZE, pFile);
+ fclose(pFile);
+ }*/
+
+
+ memset(m_data + data_pages * PAGE_SIZE, 0, bss_pages * PAGE_SIZE);
+
+ m_code_pages = code_pages;
+ m_rodata_pages = rodata_pages;
+ m_data_pages = data_pages;
+ m_bss_pages = bss_pages;
+
+}
+
+Result KCodeSet::MapInto(KMemoryMap* map,bool spezialmem)
+{
+ auto chunk_code = new MemChunk();
+ auto chunk_rodata = new MemChunk();
+ auto chunk_data = new MemChunk();
+
+ chunk_code->data = m_code;
+ chunk_rodata->data = m_rodata;
+ chunk_data->data = m_data;
+
+ chunk_code->size = m_code_pages * PAGE_SIZE;
+ chunk_rodata->size = m_rodata_pages * PAGE_SIZE;
+ chunk_data->size = (m_data_pages + m_bss_pages) * PAGE_SIZE;
+
+ u32 startaddr = spezialmem ? 0x14000000 : 0x00100000;
+
+ if (map->AddPages(startaddr,
+ chunk_code->size, chunk_code->data, chunk_code,
+ PERMISSION_RX, MEMTYPE_CODE,NULL) != Success)
+ return -1;
+
+ if (map->AddPages(startaddr + chunk_code->size,
+ chunk_rodata->size, chunk_rodata->data, chunk_rodata,
+ PERMISSION_R, MEMTYPE_CODE, NULL) != Success)
+ return -1;
+
+ if (map->AddPages(startaddr + chunk_code->size + chunk_rodata->size,
+ chunk_data->size, chunk_data->data, chunk_data,
+ PERMISSION_RW, MEMTYPE_CODE, NULL) != Success)
+ return -1;
+
+ return Success;
+}
+
+KCodeSet::~KCodeSet()
+{
+ free((void*) m_code);
+ free((void*) m_rodata);
+ free((void*) m_data);
+ free((void*) m_name);
+}
+const char* KCodeSet::GetName()
+{
+ return m_name;
+}
+bool KCodeSet::IsInstanceOf(ClassName name) {
+ if (name == KCodeSet::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+
+//tools
+
+
+bool KDmaObject::Synchronization(KThread* thread, u32 &error)
+{
+ return false;
+}
+
+KDmaObject::KDmaObject(u8 channel, u8 started, KProcess *owner)
+{
+ m_channel = channel;
+ m_started = started;
+ m_owner = owner;
+}
+
+KDmaObject::~KDmaObject()
+{
+}
+
+bool KDmaObject::IsInstanceOf(ClassName name) {
+ if (name == KDmaObject::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+
+//tools
+
+
+bool KEvent::Synchronization(KThread* thread, u32 &error)
+{
+ m_Mutex.Lock();
+ if (m_open)
+ {
+ if (!m_manual)
+ m_open = false;
+ m_Mutex.Unlock();
+ return false;
+ }
+ m_Mutex.Unlock();
+ return true; //stall
+}
+void KEvent::Triggerevent()
+{
+ m_Mutex.Lock();
+ KThread* found;
+ if (m_manual)
+ {
+ do
+ {
+ found = SynGetNextPrio();
+ SynFree(0, found);
+
+ } while(found);
+ m_open = true;
+ }
+ else
+ {
+ found = SynGetNextPrio();
+ if (found)
+ SynFree(0, found);
+ else
+ m_open = true;
+ }
+ m_Mutex.Unlock();
+}
+void KEvent::Clear()
+{
+ m_Mutex.Lock();
+ m_open = false;
+ m_Mutex.Unlock();
+}
+
+KEvent::KEvent(u32 priority, bool manual, KProcess *owner)
+{
+ m_open = false;
+ m_priority = priority;
+ m_manual = manual;
+ m_owner = owner;
+}
+
+bool KEvent::IsInstanceOf(ClassName name) {
+ if (name == KEvent::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+
+
+KHandleTable::KHandleTable(KProcess* process, u32 size) {
+ if(size == 0)
+ size = 0x28;
+
+ m_process = process;
+ m_size = size;
+ m_counter = 0;
+ m_next_free = NULL;
+
+ // Allocate handle-table.
+ if((m_handles = (HandleEntry*)malloc(sizeof(HandleEntry) * size)) == NULL) {
+ // XXX: Panic.
+ m_size = 0;
+ return;
+ }
+
+ // Generate empty handle-table.
+ for(u32 i=0; i<size-1; i++) {
+ m_handles[i].handle = 0;
+ m_handles[i].ptr.next_free = &m_handles[i+1];
+ }
+
+ m_handles[size-1].ptr.next_free = NULL;
+ m_next_free = &m_handles[0];
+}
+
+Result KHandleTable::CreateHandle(Handle& handle_out, KAutoObject* obj) {
+ // If we have no free, table is full.
+ if(m_next_free == NULL)
+ return -1;
+
+ // Get the next free entry.
+ HandleEntry* entry = m_next_free;
+ u32 index = (u32)((ptrdiff_t) (entry - m_handles));
+
+ // Move next free entry forward.
+ m_next_free = entry->ptr.next_free;
+ //LOG("use next %08x <- %08x", index, m_next_free - m_handles);
+
+ // Generate handle.
+ m_counter = (m_counter + 1) & 0x7FFF;
+ Handle handle = (m_counter << 15) | index;
+ handle_out = handle;
+
+ // Setup entry.
+ entry->handle = handle;
+ entry->ptr.object = obj;
+
+ obj->AcquireReference();
+ return Success;
+}
+
+Result KHandleTable::GetHandleObject(KAutoObjectRef& obj_out, Handle handle) {
+ u32 index = handle & 0x7FFF;
+
+ if(index >= m_size)
+ return -1;
+
+ if(m_handles[index].handle == handle) {
+ obj_out.SetObject(m_handles[index].ptr.object);
+ return Success;
+ }
+
+ return -1;
+}
+
+Result KHandleTable::CloseHandle(Handle handle) {
+ u32 index = handle & 0x7FFF;
+
+ if(index >= m_size)
+ return -1;
+
+ if(m_handles[index].handle == handle) {
+ m_handles[index].ptr.object->ReleaseReference();
+ m_handles[index].ptr.object = NULL;
+ m_handles[index].handle = -1; //prevent from freeing 2 times the same handle
+
+ // Update next free entry.
+ //LOG("free next %08x -> %08x", index, m_next_free - m_handles);
+ m_handles[index].ptr.next_free = m_next_free;
+ m_next_free = &m_handles[index];
+
+ return Success;
+ }
+
+ return -1;
+}
--- /dev/null
+#include "Kernel.h"
+
+
+KInterrupt::KInterrupt(KSynchronizationObject* syncObject, s32 priority, bool isManualClear) : m_syncObject(syncObject), m_priority(priority), m_isManualClear(isManualClear)
+{
+
+}
+
+KInterrupt::~KInterrupt()
+{
+
+}
+void KInterrupt::fire()
+{
+ if ((*m_syncObject)->IsInstanceOf(KEvent_Class))
+ {
+ KEvent *eve = (KEvent *)*m_syncObject;
+ eve->m_open = true;
+ }
+ KSynchronizationObject* obj = (KSynchronizationObject*)(*m_syncObject);
+ obj->SynFreeAll(Success); //hope that is correct...
+}
+bool KInterrupt::IsInstanceOf(ClassName name) {
+ if (name == KInterrupt::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
+KAutoObjectRef *KInterrupt::GetObjRef()
+{
+ return &m_syncObject;
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+
+
+KKernel::KKernel() : m_core0(this), m_core1(this), m_core2(this), m_core3(this), m_NextProcessID(0), m_NextThreadID(0), tempsh(), m_numbFirmProcess(0)
+{
+ memset(m_FIRM_Launch_Parameters, 0, sizeof(m_FIRM_Launch_Parameters));
+ for (int i = 0; i < sizeof(m_Interrupt) / sizeof(KLinkedList<KInterrupt>*); i++)
+ m_Interrupt[i] = new KLinkedList<KInterrupt>();
+ m_p9 = new Process9(this);
+ m_hash1 = new HWHASH(this);
+ m_hash2 = new HWHASH2(this, m_hash1);
+ m_I2C1 = new HWBUS1(this);
+ m_I2C2 = new HWBUS2(this);
+ m_I2C3 = new HWBUS3(this);
+ m_GPIO = new GPIO(this);
+ m_PDN = new PDN(this);
+ m_SPI0 = new SPI(this);
+ m_SPI1 = new SPI(this);
+ m_SPI2 = new SPI(this);
+ m_DSP = new DSP(this);
+ m_GPU = new GPUHW(this);
+ m_HID = new HID(this);
+ m_MIC = new MIC(this);
+}
+
+void KKernel::AddProcess(KProcess* p, bool is_firm_process)
+{
+ if (is_firm_process)
+ m_numbFirmProcess++;
+ m_processes.AddItem(p);
+}
+
+void KKernel::AddQuickCodeProcess(u8* buf, size_t size) {
+ //KCodeSet* codeset = new KCodeSet(buf, (size + 0xFFF) / 0x1000, NULL, 0, NULL, 0, 0, 0x1, "Code");
+ //auto p = new KProcess(codeset, 0, NULL);
+
+ //m_processes.AddItem(p);
+}
+void KKernel::StartThread(KThread* p)
+{
+ tempsh.AddItem(p);
+}
+void KKernel::ReScheduler()
+{
+ m_core0.ReSchedule();
+}
+void KKernel::StopThread(KThread* p)
+{
+ KLinkedListNode<KThread> *temp = tempsh.list;
+ while (temp)
+ {
+ if (temp->data == p)
+ break;
+ temp = temp->next;
+ if (!temp->next)
+ break;
+ }
+ if (temp)
+ tempsh.RemoveItem(temp);
+}
+u64 KKernel::FindTimedEventWithSmallestCyclesRemaining()
+{
+ int min = 1000;
+ KLinkedListNode<KTimeedEvent> *t = m_Timedevent.list;
+ while (t)
+ {
+ if (t->data->num_cycles_remaining != 0)
+ {
+ min = min > t->data->num_cycles_remaining ? t->data->num_cycles_remaining : min;
+ }
+ t = t->next;
+ }
+ return min + 1;
+}
+
+extern "C" void citraFireInterrupt(int id);
+
+void KKernel::ThreadsRunTemp()
+{
+ KLinkedListNode<KThread> *temp = tempsh.list;
+ while (true)
+ {
+ KThread * current = temp->data;
+ if (current->m_running)
+ {
+ m_core0.SetThread(current);
+ if (!current->Threadwaitlist || !current->Threadwaitlist->list)
+ {
+ current->m_core = &m_core0;
+
+ u64 min_cycles = FindTimedEventWithSmallestCyclesRemaining();
+ u64 cycles_run = m_core0.RunCycles((uint)min_cycles);
+ KLinkedListNode<KTimeedEvent> *t = m_Timedevent.list;
+ while (t)
+ {
+ if (t->data->num_cycles_remaining != 0)
+ {
+ t->data->num_cycles_remaining -= cycles_run;
+ if (t->data->num_cycles_remaining <= 0)
+ {
+ t->data->num_cycles_remaining = 0;
+ t->data->trigger_event();
+ }
+ }
+ t = t->next;
+ }
+
+
+ current->m_core = NULL;
+ }
+ }
+ else
+ {
+ StopThread(current);
+ }
+ if (!temp->next)
+ {
+ temp = tempsh.list;
+
+ //fallback todo increas cycels
+ u64 min_cycles = FindTimedEventWithSmallestCyclesRemaining();
+
+ m_core0.Addticks(min_cycles);
+ m_core1.Addticks(min_cycles);
+ m_core2.Addticks(min_cycles);
+ m_core3.Addticks(min_cycles);
+
+ KLinkedListNode<KTimeedEvent> *t = m_Timedevent.list;
+ while (t)
+ {
+ if (t->data->num_cycles_remaining != 0)
+ {
+ t->data->num_cycles_remaining -= min_cycles;
+ if (t->data->num_cycles_remaining <= 0)
+ {
+ t->data->num_cycles_remaining = 0;
+ t->data->trigger_event();
+ }
+ }
+ t = t->next;
+ }
+
+ }
+ else
+ temp = temp->next;
+
+
+
+ }
+}
+u32 KKernel::GetNextThreadID()
+{
+ return m_NextThreadID++;
+}
+u32 KKernel::GetNextProcessID()
+{
+ return m_NextProcessID++;
+}
+s32 KKernel::RegisterInterrupt(u32 name, KSynchronizationObject* syncObject, s32 priority, bool isManualClear)
+{
+ if (name < 0x80)
+ {
+ //check if already registered
+ KLinkedListNode<KInterrupt> *node = m_Interrupt[name]->list;
+ while (node)
+ {
+ if (**(node->data->GetObjRef()) == syncObject)
+ return -1;
+ node = node->next;
+ }
+
+ KInterrupt* inter = new KInterrupt(syncObject, priority, isManualClear);
+ m_Interrupt[name]->AddItem(inter);
+ return Success;
+ }
+ else
+ return -1;
+}
+s32 KKernel::UnRegisterInterrupt(u32 name, KSynchronizationObject* syncObject)
+{
+ KLinkedListNode<KInterrupt> *node = m_Interrupt[name]->list;
+ while (node)
+ {
+ if (**(node->data->GetObjRef()) == syncObject)
+ {
+ m_Interrupt[name]->RemoveItem(node);
+ return 1;
+ }
+ node = node->next;
+ }
+ return -1;
+
+}
+void KKernel::FireNextTimeEvent(KTimeedEvent* eve, u64 ticks)
+{
+ eve->num_cycles_remaining = ticks; //todo make more acurate
+}
+void KKernel::FireInterrupt(u32 name)
+{
+ KLinkedListNode<KInterrupt> *node = m_Interrupt[name]->list;
+ while (node)
+ {
+ node->data->fire();
+ node = node->next;
+ }
+}
--- /dev/null
+#include "Kernel.h"
+
+//tools
+#define Write32(p,d) \
+p[0] = d & 0xFF; \
+p[1] = (d >> 8) & 0xFF; \
+p[2] = (d >> 16) &0xFF; \
+p[3] = (d >> 24) &0xFF; \
+
+u8* Mem_VRAM = NULL;
+u8* Mem_DSP = NULL;
+u8* Mem_FCRAM = NULL;
+u8* Mem_Configuration = NULL;
+u8* Mem_Shared = NULL;
+bool* MEM_FCRAM_Used = NULL;
+
+MemChunk* chunk_Configuration;
+MemChunk* chunk_Shared;
+
+void Mem_Init(bool new3ds)
+{
+ Mem_VRAM = (u8*)calloc(0x600000, sizeof(u8));
+ Mem_DSP = (u8*)calloc(0x80000, sizeof(u8));
+ if (new3ds)
+ {
+ Mem_FCRAM = (u8*)calloc(0x10000000, sizeof(u8));
+ MEM_FCRAM_Used = (bool*)calloc((0x10000000 / 0x1000), sizeof(bool));
+ }
+ else
+ {
+ Mem_FCRAM = (u8*)calloc(0x8000000,sizeof(u8));
+ MEM_FCRAM_Used = (bool*)calloc((0x8000000 / 0x1000),sizeof(bool));
+ }
+}
+u8* Mem_GetPhysicalPointer(u32 addr) //this is unsave citra stuff
+{
+ if (0x18000000 <= addr && addr <= 0x18600000)
+ return Mem_VRAM + (addr - 0x18000000);
+ if (0x20000000 <= addr && addr <= 0x30000000)
+ return Mem_FCRAM + (addr - 0x20000000);
+ return NULL;
+}
+void Mem_SharedMemInit()
+{
+ Mem_Configuration = (u8*)calloc(0x1000, sizeof(u8));
+ Mem_Shared = (u8*)calloc(0x1000, sizeof(u8));
+
+ //configure the Configuration Memory mem to strart up normaly no need to configure Mem_Shared that is done by the modules
+ //the configuration is from 4.1 (Firm v7712)
+ Mem_Configuration[0x00] = 0x00; //KERNEL_? (Firm v7712)
+ Mem_Configuration[0x01] = 0x00; //KERNEL_VERSIONREVISION (Firm v7712)
+ Mem_Configuration[0x02] = 0x22; //KERNEL_VERSIONMINOR (Firm v7712)
+ Mem_Configuration[0x03] = 0x02; //KERNEL_VERSIONMAJOR (Firm v7712)
+ Write32((&Mem_Configuration[0x04]), 0x0); //UPDATEFLAG (no update)
+ Write32((&Mem_Configuration[0x08]), 0x00008002); //NSTID (NS)
+ Write32((&Mem_Configuration[0x0C]), 0x00040130); //NSTID (NS)
+ Write32((&Mem_Configuration[0x10]), 0x00000002); //SYSCOREVER (NATIVE_FIRM)
+ Mem_Configuration[0x14] = 0x01; //UNITINFO (1 for retail 2 for debug other?) if set to Bit(0) is 0 ErrDisp will display development error info
+ Mem_Configuration[0x15] = 0x00; //BOOT_FIRM (Bit(0) for debug Bit(1) for JTAG connected)
+ Mem_Configuration[0x16] = 0x00; //PREV_FIRM 0 for cold boot?
+ Write32((&Mem_Configuration[0x1C]), 0x0000BA0E); //KERNEL_CTRSDKVERSION (Firm v7712)
+ Write32((&Mem_Configuration[0x30]), 0x00000000); //APPMEMTYPE (0-5 for 3DS 0-7 for New3DS)
+ Write32((&Mem_Configuration[0x40]), 0x04000000); //APPMEMALLOC (APPMEMTYPE)
+ Write32((&Mem_Configuration[0x44]), 0x02C00000); //SYSMEMALLOC (APPMEMTYPE)
+ Write32((&Mem_Configuration[0x48]), 0x01400000); //BASEMEMALLOC (APPMEMTYPE)
+ Mem_Configuration[0x60] = 0x00; //KERNEL_? (Firm v7712)
+ Mem_Configuration[0x61] = 0x00; //KERNEL_VERSIONREVISION (Firm v7712)
+ Mem_Configuration[0x62] = 0x22; //KERNEL_VERSIONMINOR (Firm v7712)
+ Mem_Configuration[0x63] = 0x02; //KERNEL_VERSIONMAJOR (Firm v7712)
+ Write32((&Mem_Configuration[0x64]), 0x00000002); //FIRM_SYSCOREVER (Firm v7712 (no update))
+ Write32((&Mem_Configuration[0x68]), 0x0000BA0E); //FIRM_CTRSDKVERSION (Firm v7712 (no update))
+
+ chunk_Configuration = new MemChunk();
+ chunk_Shared = new MemChunk();
+ chunk_Configuration->data = Mem_Configuration;
+ chunk_Configuration->size = 0x1000;
+ chunk_Shared->data = Mem_Shared;
+ chunk_Shared->size = 0x1000;
+}
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+
+#define LOGHIGHACCESS
+
+//#define LOGMODULE "codec"
+//#define RWLOG
+//#define WLOG
+
+#define TtoTT(x) case x: \
+case x + 0x1000: \
+case x + 0x2000: \
+case x + 0x3000: \
+case x + 0x4000: \
+case x + 0x5000: \
+case x + 0x6000: \
+case x + 0x7000: \
+case x + 0x8000: \
+case x + 0x9000: \
+case x + 0xA000: \
+case x + 0xB000: \
+case x + 0xC000: \
+case x + 0xD000: \
+case x + 0xE000: \
+case x + 0xF000: \
+
+#define TtoTTT(x) TtoTT(x) \
+TtoTT(x + 0x10000) \
+TtoTT(x + 0x20000) \
+TtoTT(x + 0x30000) \
+TtoTT(x + 0x40000) \
+TtoTT(x + 0x50000) \
+TtoTT(x + 0x60000) \
+TtoTT(x + 0x70000) \
+TtoTT(x + 0x80000) \
+TtoTT(x + 0x90000) \
+TtoTT(x + 0xA0000) \
+TtoTT(x + 0xB0000) \
+TtoTT(x + 0xC0000) \
+TtoTT(x + 0xD0000) \
+TtoTT(x + 0xE0000) \
+TtoTT(x + 0xF0000) \
+
+KMemoryMap::KMemoryMap(KProcess* process) {
+ m_process = process;
+ memset(m_pages, 0, sizeof(m_pages));
+ memset(m_TLSused, 0, sizeof(m_TLSused));
+ memset(m_TLSpointer, 0, sizeof(m_TLSpointer));
+}
+
+Result KMemoryMap::ReadN(u32 addr, u8* out, u32 size) {
+
+ for (u32 i = 0; i < size; i++)
+ {
+ s32 res = Read8(addr + i, out[i]);
+ if (res != Success)
+ return res;
+ }
+
+ return Success;
+}
+
+Result KMemoryMap::Read8(u32 addr, u8& out) {
+
+ u32 offset = addr & PAGE_MASK;
+ u32 page = addr / PAGE_SIZE;
+
+ if (unlikely(page > NUM_PAGES))
+ return -1;
+ if (unlikely(m_pages[page].state == STATE_FREE))
+ return -1;
+ if (!(notcritical(m_pages[page].perm) & PERMISSION_R))
+ return -1;
+ if (unlikely((u8)(m_pages[page].state) == STATE_IO))
+ {
+ if (m_pages[page].HW)
+ {
+ out = m_pages[page].HW->Read8(addr);
+ return Success;
+ }
+ }
+ out = m_pages[page].data[offset];
+
+#ifdef RWLOG
+ if (strcmp(LOGMODULE, m_process->GetName()) == 0)
+ LOG("read8 %08x %02x", addr, out);
+#endif
+
+#ifdef LOGHIGHACCESS
+
+ if (addr >= 0x1FF80000 && addr <= 0x1FF81FFF)
+ LOG("read8 %08x %02x", addr, out);
+#endif
+
+ return Success;
+}
+
+Result KMemoryMap::Read16(u32 addr, u16& out) {
+ u32 page = addr/PAGE_SIZE;
+
+ u32 offset = addr & PAGE_MASK;
+
+ // Cross-page reading.
+ if (unlikely(offset == PAGE_MASK && !((u8)(m_pages[page].state) == STATE_IO))) {
+ u8 lo, hi;
+
+ if (unlikely(Read8(addr, lo) != Success))
+ return -1;
+ if (unlikely(Read8(addr + 1, hi) != Success))
+ return -1;
+
+ // XXX: Verify:
+ out = (lo<<8) | hi;
+
+#ifdef LOGHIGHACCESS
+ if (addr >= 0x1FF80000 && addr <= 0x1FF81FFF)
+ LOG("read16 %08x %04x", addr, out);
+#endif
+
+#ifdef RWLOG
+ if (strcmp(LOGMODULE, m_process->GetName()) == 0)
+ LOG("read16 %08x %04x", addr, out);
+#endif
+
+ return Success;
+ }
+
+ if(unlikely(page > NUM_PAGES))
+ return -1;
+ if(unlikely(m_pages[page].state == STATE_FREE))
+ return -1;
+ if(!(notcritical(m_pages[page].perm) & PERMISSION_R))
+ return -1;
+ if (unlikely((u8)(m_pages[page].state) == STATE_IO))
+ {
+ if (m_pages[page].HW)
+ {
+ out = m_pages[page].HW->Read16(addr);
+ return Success;
+ }
+ }
+ out = *(u16*) &m_pages[page].data[offset];
+
+#ifdef RWLOG
+ if (strcmp(LOGMODULE, m_process->GetName()) == 0)
+ LOG("read16 %08x %04x", addr, out);
+#endif
+
+#ifdef LOGHIGHACCESS
+ if (addr >= 0x1FF80000 && addr <= 0x1FF81FFF)
+ LOG("read16 %08x %04x", addr, out)
+#endif
+
+ return Success;
+}
+
+Result KMemoryMap::Read32(u32 addr, u32& out) {
+
+
+ u32 page = addr/PAGE_SIZE;
+ u32 offset = addr & PAGE_MASK;
+
+ // Cross-page reading.
+ if (unlikely(offset > (PAGE_MASK - 3) && !!((u8)(m_pages[page].state) == STATE_IO))) {
+ // XXX: Verify: TODO speedup
+ u8 B0, B1, B2, B3;
+
+ if (unlikely(Read8(addr, B0) != Success))
+ return -1;
+ if (unlikely(Read8(addr + 1, B1) != Success))
+ return -1;
+ if (unlikely(Read8(addr + 2, B2) != Success))
+ return -1;
+ if (unlikely(Read8(addr + 3, B3) != Success))
+ return -1;
+
+ out = (B0 << 24) | (B1 << 16) | (B2 << 8) | (B3 << 0);
+#ifdef LOGHIGHACCESS
+ if (addr >= 0x1FF80000 && addr <= 0x1FF81FFF)
+ LOG("read32 %08x %08x", addr, out)
+#endif
+#ifdef RWLOG
+ if (strcmp(LOGMODULE, m_process->GetName()) == 0)
+ LOG("read32 %08x %08x", addr, out)
+#endif
+
+ return Success;
+ }
+
+ if(unlikely(page > NUM_PAGES))
+ return -1;
+ if(unlikely(m_pages[page].state == STATE_FREE))
+ return -1;
+ if(!(notcritical(m_pages[page].perm) & PERMISSION_R))
+ return -1;
+ if (unlikely((u8)(m_pages[page].state) == STATE_IO))
+ {
+ if (m_pages[page].HW)
+ {
+ out = m_pages[page].HW->Read32(addr);
+ return Success;
+ }
+ }
+
+ out = *(u32*)&m_pages[page].data[offset];
+
+#ifdef RWLOG
+ if (strcmp(LOGMODULE, m_process->GetName()) == 0)
+ LOG("read32 %08x %08x", addr, out)
+#endif
+
+#ifdef LOGHIGHACCESS
+ if (addr >= 0x1FF80000 && addr <= 0x1FF81FFF)
+ LOG("read32 %08x %08x", addr, out)
+#endif
+
+ return Success;
+}
+Result KMemoryMap::Read64(u32 addr, u64& out) {
+ u32 V1,V2;
+ if (unlikely(Read32(addr, V1) != Success))
+ return -1;
+ if (unlikely(Read32(addr + 4, V2) != Success))
+ return -1;
+ out = ((u64)(V2) << 32) | (u64)V1;
+ return Success;
+}
+
+Result KMemoryMap::Write8(u32 addr, u8 val) {
+
+#ifdef LOGHIGHACCESS
+ if (addr >= 0x1FF80000 && addr <= 0x1FF81FFF)
+ LOG("write8 %08x %02x", addr, val)
+#endif
+#if defined(RWLOG) || defined(WLOG)
+ if (strcmp(LOGMODULE, m_process->GetName()) == 0)
+ LOG("write8 %08x %02x", addr, val)
+#endif
+
+ u32 page = addr/PAGE_SIZE;
+ u32 offset = addr & PAGE_MASK;
+
+ if(unlikely(page > NUM_PAGES))
+ return -1;
+ if(unlikely(m_pages[page].state == STATE_FREE))
+ return -1;
+ if(!(notcritical(m_pages[page].perm) & PERMISSION_W))
+ return -1;
+ if (unlikely((u8)(m_pages[page].state) == STATE_IO))
+ {
+ if (m_pages[page].HW)
+ {
+ m_pages[page].HW->Write8(addr, val);
+ return Success;
+ }
+ }
+
+ m_pages[page].data[addr & PAGE_MASK] = val;
+ return Success;
+}
+
+Result KMemoryMap::Write16(u32 addr, u16 val) {
+
+#ifdef LOGHIGHACCESS
+ if (addr >= 0x1FF80000 && addr <= 0x1FF81FFF)
+ LOG("write16 %08x %04x", addr, val)
+#endif
+
+#if defined(RWLOG) || defined(WLOG)
+ if (strcmp(LOGMODULE, m_process->GetName()) == 0)
+ LOG("write16 %08x %04x", addr, val)
+#endif
+ u32 page = addr/PAGE_SIZE;
+
+ u32 offset = addr & PAGE_MASK;
+
+ if (unlikely(offset == PAGE_MASK && !((u8)(m_pages[page].state) == STATE_IO))) {
+ // XXX: Verify:
+ u8 B0 = (u8)(val >> 8), B1 = (u8)val;
+
+ if (unlikely(Write8(addr, B0) != Success))
+ return -1;
+ if (unlikely(Write8(addr + 1, B1) != Success))
+ return -1;
+
+ return Success;
+ }
+
+ if(unlikely(page > NUM_PAGES))
+ return -1;
+ if(unlikely(m_pages[page].state == STATE_FREE))
+ return -1;
+ if(!(notcritical(m_pages[page].perm) & PERMISSION_W))
+ return -1;
+ if (unlikely((u8)(m_pages[page].state) == STATE_IO))
+ {
+ if (m_pages[page].HW)
+ {
+ m_pages[page].HW->Write16(addr, val);
+ return Success;
+ }
+ }
+
+ *(u16*) &m_pages[page].data[offset] = val;
+ return Success;
+}
+
+Result KMemoryMap::Write32(u32 addr, u32 val) {
+
+#ifdef LOGHIGHACCESS
+ if (addr >= 0x1FF80000 && addr <= 0x1FF81FFF)
+ LOG("write32 %08x %08x", addr, val)
+#endif
+#if defined(RWLOG) || defined(WLOG)
+ if (strcmp(LOGMODULE, m_process->GetName()) == 0)
+ LOG("write32 %08x %08x", addr, val)
+#endif
+ u32 page = addr/PAGE_SIZE;
+
+ u32 offset = addr & PAGE_MASK;
+
+ if(unlikely(offset > (PAGE_MASK-3))) {
+ // XXX: Verify: TODO speedup
+ u8 B0 = (u8)(val >> 24), B1 = (u8)(val >> 16), B2 = (u8)(val >> 8), B3 = (u8)val;
+
+ if (unlikely(Write8(addr, B0) != Success && !((u8)(m_pages[page].state) == STATE_IO)))
+ return -1;
+ if (unlikely(Write8(addr + 1, B1) != Success))
+ return -1;
+ if (unlikely(Write8(addr + 2, B2) != Success))
+ return -1;
+ if (unlikely(Write8(addr + 3, B3) != Success))
+ return -1;
+
+ return Success;
+ }
+
+ if(unlikely(page > NUM_PAGES))
+ return -1;
+ if(unlikely(m_pages[page].state == STATE_FREE))
+ return -1;
+ if(!(notcritical(m_pages[page].perm) & PERMISSION_W))
+ return -1;
+ if (unlikely((u8)(m_pages[page].state) == STATE_IO))
+ {
+ if (m_pages[page].HW)
+ {
+ m_pages[page].HW->Write32(addr, val);
+ return Success;
+ }
+ }
+ *(u32*) &m_pages[page].data[offset] = val;
+ return Success;
+}
+
+Result KMemoryMap::Write64(u32 addr, u64 val) {
+ if (unlikely(Write32(addr, (u32)(val >> 0)) != Success))
+ return -1;
+ if (unlikely(Write32(addr + 4, (u32)(val >> 32)) != Success))
+ return -1;
+ return Success;
+}
+
+Result KMemoryMap::IPCMap(u32 addr0, u32 addr1, u32 size, MemoryPermissions perm, KMemoryMap * mapto)
+{
+ // Verify that addr1 is normal heap.
+ if (VerifyRegionMaskState(addr1, size, STATE_IPC_ALLOWED) != 0)
+ return 0xE0A01BF5;
+ // Verify that addr0 is unmapped.
+ if (mapto->VerifyRegionState(addr0, size, STATE_FREE) != 0)
+ return -1; //this means we failed try again
+ if (AddMirror(addr0, addr1, size, perm, mapto) != 0)
+ return 0xE0A01BF5;
+ return Success;
+}
+
+Result KMemoryMap::ControlMemory(u32* addr_out, u32 addr0, u32 addr1, u32 size,
+ MemoryOperation op, MemoryPermissions perm)
+{
+ perm = perm & PERMISSION_RW;
+
+ switch(op & 0xFF) {
+ case OPERATION_FREE:
+ // Verify that addr0 is mapped heap/linear-heap.
+ if(VerifyRegionMaskState(addr0, size, STATE_FREE_ALLOWED) != 0)
+ return 0xE0A01BF5;
+ if(RemovePages(addr0, size) != 0)
+ return 0xE0A01BF5;
+
+ *addr_out = addr0;
+ break;
+
+ case OPERATION_COMMIT:
+ if(op & OPERATION_LINEAR) {
+ u32 addr =AllocFreeGSP(false, size);
+ addr0 = m_process->LINEAR_memory_virtual_address_userland + addr;
+ // Verify that addr0 is unmapped.
+ if (VerifyRegionState(addr0, size, STATE_FREE) != 0)
+ return 0xE0A01BF5;
+ // Create chunk.
+
+ MemChunk* chunk = (MemChunk*)malloc(sizeof(MemChunk));
+
+ if (chunk == NULL) {
+ return 0xE0A01BF5;
+ }
+
+ chunk->size = size;
+ chunk->data = &Mem_FCRAM[addr];
+ // Map it.
+ if (AddPages(addr0, size, chunk->data, chunk, perm, MEMTYPE_HEAP, NULL) != 0) {
+ free(chunk);
+ return 0xE0A01BF5;
+ }
+
+ *addr_out = addr0;
+ return 0;
+ }
+
+ MemChunk* chunk;
+
+ // Verify that addr0 is unmapped.
+ if(VerifyRegionState(addr0, size, STATE_FREE) != 0)
+ return 0xE0A01BF5;
+ // Create chunk.
+ if(CreateChunk(&chunk, size) != 0)
+ return 0xE0A01BF5;
+ // Map it.
+ if (AddPages(addr0, size, chunk->data, chunk, perm, MEMTYPE_HEAP, NULL) != 0) {
+ free(chunk);
+ return 0xE0A01BF5;
+ }
+
+ *addr_out = addr0;
+ break;
+
+ case OPERATION_MAP:
+ // XXX: Perm @ addr1 bit0-1 must not be both 0.
+
+ // Verify that addr1 is normal heap.
+ if(VerifyRegionState(addr1, size, MEMTYPE_HEAP) != 0)
+ return 0xE0A01BF5;
+ // Verify that addr0 is unmapped.
+ if(VerifyRegionState(addr0, size, STATE_FREE) != 0)
+ return 0xE0A01BF5;
+ if(AddMirror(addr0, addr1, size, perm) != 0)
+ return 0xE0A01BF5;
+
+ *addr_out = 0;
+ break;
+
+ case OPERATION_UNMAP:
+ if(RemoveMirror(addr0, addr1, size) != 0)
+ return 0xE0A01BF5;
+
+ *addr_out = 0;
+ break;
+
+ case OPERATION_PROTECT:
+ if(VerifyRegionMaskState(addr0, size, STATE_PROTECT_ALLOWED) != 0)
+ return 0xE0A01BF5;
+
+ if(Reprotect(addr0, size, perm) != 0)
+ return 0xE0A01BF5;
+
+ *addr_out = addr0; // XXX: This should be base_addr of MemQuery(addr1)
+ break;
+ }
+
+ return Success;
+}
+
+Result KMemoryMap::VerifyRegionState(u32 addr, u32 size, MemoryState state) {
+ MemoryInfo info;
+ Result ret;
+
+ ret = QueryMemory(&info, NULL, addr);
+
+ if(ret != 0)
+ return ret;
+ if(info.state != state)
+ return -1;
+ if((addr + size) > (info.base_address + info.size))
+ return -1;
+
+ return Success;
+}
+
+Result KMemoryMap::VerifyRegionMaskState(u32 addr, u32 size, MemoryState state)
+{
+ MemoryInfo info;
+ Result ret;
+
+ ret = QueryMemory(&info, NULL, addr);
+
+ if(ret != 0)
+ return ret;
+ if((info.state & state) != state)
+ return -1;
+ if((addr + size) > (info.base_address + info.size))
+ return -1;
+
+ return Success;
+}
+
+Result KMemoryMap::QueryMemory(MemoryInfo* mem_out, PageInfo* page_out,
+ u32 addr)
+{
+ addr /= PAGE_SIZE;
+
+ // Make sure we're within userspace memory.
+ if(addr >= NUM_PAGES)
+ return -1;
+
+ MemPage* page = &m_pages[addr];
+ MemPage* prev = page;
+ u32 size = 1;
+
+ while(1) {
+ if((addr+size) >= NUM_PAGES)
+ break;
+
+ if((prev->state == page->state) && (prev->perm == page->perm)) {
+ prev = page;
+ page++;
+ size++;
+ }
+ else break;
+ }
+
+ mem_out->base_address = addr * PAGE_SIZE;
+ mem_out->size = size * PAGE_SIZE;
+ mem_out->perm = prev->perm;
+ mem_out->state = prev->state;
+
+ if(page_out != NULL)
+ page_out->flags = 0;
+
+ return 0;
+}
+
+Result KMemoryMap::CreateChunk(MemChunk** chunk_out, u32 size) {
+ MemChunk* chunk = (MemChunk*) malloc(sizeof(MemChunk));
+ u8* data = (u8*) calloc(size,sizeof(u8));
+
+ if((chunk == NULL) || (data == NULL)) {
+ free(chunk);
+ free(data);
+ return -1;
+ }
+
+ chunk->size = size;
+ chunk->data = data;
+
+ *chunk_out = chunk;
+ return Success;
+}
+
+Result KMemoryMap::AddPages(u32 addr, u32 size, u8* data, MemChunk* chunk,
+ MemoryPermissions perm, MemoryState state, IOHW *HW)
+{
+ addr /= PAGE_SIZE;
+ size /= PAGE_SIZE;
+
+ // Fill in page-info.
+ for(u32 i=0; i<size; i++) {
+ m_pages[addr+i].data = data + (i * PAGE_SIZE);
+ m_pages[addr+i].chunk = chunk;
+ m_pages[addr+i].state = state;
+ m_pages[addr+i].perm = perm;
+ m_pages[addr+i].mirrored = 0;
+ m_pages[addr+i].HW = HW;
+ }
+
+ chunk->ref_count += size;
+ return Success;
+}
+
+Result KMemoryMap::RemovePages(u32 addr, u32 size)
+{
+ addr /= PAGE_SIZE;
+ size /= PAGE_SIZE;
+
+ // Fill in page-info.
+ for(u32 i=0; i<size; i++) {
+ m_pages[addr+i].chunk->ref_count--;
+
+ m_pages[addr+i].data = NULL;
+ m_pages[addr+i].chunk = NULL;
+ m_pages[addr+i].state = STATE_FREE;
+ m_pages[addr+i].perm = PERMISSION_NONE;
+ m_pages[addr+i].mirrored = 0;
+ }
+
+ return Success;
+}
+
+Result KMemoryMap::AddMirror(u32 mirror, u32 mirrored, u32 size,
+ MemoryPermissions perm)
+{
+ mirror /= PAGE_SIZE;
+ mirrored /= PAGE_SIZE;
+ size /= PAGE_SIZE;
+
+ for(u32 i=0; i<size; i++) {
+ // Fill in mirror pages.
+ m_pages[mirror+i].data = m_pages[mirrored+i].data;
+ m_pages[mirror+i].chunk = m_pages[mirrored+i].chunk;
+ m_pages[mirror+i].state = MEMTYPE_MIRROR;
+ m_pages[mirror+i].perm = perm;
+ m_pages[mirror+i].mirrored = (mirrored + i) * PAGE_SIZE;
+ m_pages[mirror+i].chunk->ref_count++;
+
+ // Mark mirrored pages as mirrored.
+ m_pages[mirrored+i].state = MEMTYPE_MIRRORED;
+ }
+
+ return Success;
+}
+Result KMemoryMap::AddMirror(u32 mirror, u32 mirrored, u32 size,
+ MemoryPermissions perm, KMemoryMap * mapto)
+{
+ mirror /= PAGE_SIZE;
+ mirrored /= PAGE_SIZE;
+ size += PAGE_SIZE - 1;
+ size /= PAGE_SIZE;
+
+ for (u32 i = 0; i<size; i++) {
+ // Fill in mirror pages.
+ if (m_pages[mirrored + i].state != STATE_FREE)
+ {
+ mapto->m_pages[mirror + i].data = m_pages[mirrored + i].data;
+ mapto->m_pages[mirror + i].chunk = m_pages[mirrored + i].chunk;
+ mapto->m_pages[mirror + i].state = MEMTYPE_MIRROR;
+ mapto->m_pages[mirror + i].perm = perm;
+ mapto->m_pages[mirror + i].mirrored = (mirrored + i) * PAGE_SIZE;
+ mapto->m_pages[mirror + i].chunk->ref_count++;
+
+ // Mark mirrored pages as mirrored.
+ mapto->m_pages[mirrored + i].state = MEMTYPE_MIRRORED;
+ }
+ }
+
+ return Success;
+}
+
+Result KMemoryMap::RemoveMirror(u32 mirror, u32 mirrored, u32 size) {
+ mirror /= PAGE_SIZE;
+ mirrored /= PAGE_SIZE;
+ size /= PAGE_SIZE;
+
+ // Make sure we're within userspace memory.
+ if((mirror + size) >= NUM_PAGES)
+ return -1;
+ if((mirrored + size) >= NUM_PAGES)
+ return -1;
+
+ // Check that the region is continously mapped.
+ for(u32 i=0; i<size; i++) {
+ if(m_pages[mirror+i].state != MEMTYPE_MIRROR)
+ return -1;
+ if(m_pages[mirrored+i].state != MEMTYPE_MIRRORED)
+ return -1;
+ if(m_pages[mirror+i].mirrored != (mirrored + i) * PAGE_SIZE)
+ return -1;
+ }
+
+ for(u32 i=0; i<size; i++) {
+ // Clear mirror pages.
+ m_pages[mirror+i].chunk->ref_count--;
+ memset(&m_pages[mirror+i], 0, sizeof(MemPage));
+
+ // Restore state on mirrored pages.
+ m_pages[mirrored+i].state = MEMTYPE_HEAP;
+ }
+
+ return Success;
+}
+
+Result KMemoryMap::Reprotect(u32 addr, u32 size, MemoryPermissions perm) {
+
+ addr /= PAGE_SIZE;
+ size /= PAGE_SIZE;
+
+ for(u32 i=0; i<size; i++)
+ m_pages[addr+i].perm = perm;
+
+ return Success;
+}
+
+Result KMemoryMap::AddCodeSegment(u32 addr, u32 size, u8* data,
+ MemoryPermissions perm)
+{
+ // Temporary implementation.
+ MemChunk* chunk;
+ if(CreateChunk(&chunk, size) != 0)
+ return -1;
+
+ memcpy(chunk->data, data, size);
+
+ if (AddPages(addr, size, chunk->data, chunk, perm, MEMTYPE_CODE, NULL) != 0)
+ return -1;
+
+ return 0;
+}
+Result KMemoryMap::MapIOData(u32 address, u32 size, u8*data, MemoryPermissions perm) {
+ // Temporary implementation.
+ MemChunk* chunk = (MemChunk*)malloc(sizeof(MemChunk));
+
+ if (chunk == NULL) {
+ return -1;
+ }
+
+ chunk->size = size;
+ chunk->data = data;
+
+ if (AddPages(address, size, chunk->data, chunk, perm, MEMTYPE_IO, NULL) != 0)
+ return -1;
+
+ return 0;
+}
+Result KMemoryMap::MapIOobj(u32 addr, u32 size, IOHW* obj, MemoryPermissions perm)
+{
+ // Temporary implementation.
+ MemChunk* chunk = (MemChunk*)malloc(sizeof(MemChunk));
+
+ if (chunk == NULL) {
+ return -1;
+ }
+
+ chunk->size = size;
+ chunk->data = NULL;
+
+ if (AddPages(addr, size, chunk->data, chunk, perm, MEMTYPE_IO, obj) != 0)
+ return -1;
+
+ return 0;
+}
+Result KMemoryMap::AddIOMem(u32 address, u32 size, MemoryPermissions perm)
+{
+ size &= ~0xFFF;
+ address &= ~0xFFF;
+ s32 ret = Success;
+ for (u32 i = address; i < address + size; i+= 0x1000)
+ {
+ switch (i)
+ {
+ case 0x1ec01000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_hash1, perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ec40000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_PDN, perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ec42000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_SPI0, perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ec43000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_SPI1, perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ec44000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_I2C2, perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ec46000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_HID, perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ec47000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_GPIO, perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ec48000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_I2C3, perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ec60000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_SPI2, perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ec61000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_I2C1, perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ec62000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_MIC, perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ec63000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_p9,perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ed03000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_DSP, perm) != Success)
+ ret = -1;
+ break;
+ case 0x1ee01000:
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_hash2, perm) != Success)
+ ret = -1;
+ break;
+ TtoTT(0x1ef00000)
+ TtoTT(0x1ef10000)
+ if (MapIOobj(i, 0x1000, m_process->m_Kernel->m_GPU, perm) != Success)
+ ret = -1;
+ break;
+ TtoTTT(0x1f000000)
+ TtoTTT(0x1f100000)
+ TtoTTT(0x1f200000)
+ TtoTTT(0x1f300000)
+ TtoTTT(0x1f400000)
+ TtoTTT(0x1f500000)
+ if (MapIOData(i, 0x1000, &Mem_VRAM[i - 0x1f000000], perm) != Success)
+ ret = -1;
+ break;
+
+ TtoTT(0x1ff00000)
+ TtoTT(0x1ff10000)
+ TtoTT(0x1ff20000)
+ TtoTT(0x1ff30000)
+ TtoTT(0x1ff40000)
+ TtoTT(0x1ff50000)
+ TtoTT(0x1ff60000)
+ TtoTT(0x1ff70000)
+ if (MapIOData(i, 0x1000, &Mem_DSP[i - 0x1ff00000], perm) != Success)
+ ret = -1;
+ break;
+
+ default:
+ ret = -1;
+ LOG("IO mem mapping of %08x is not yet supported",i);
+ break;
+ }
+ }
+ return ret;
+}
+Result KMemoryMap::AddTLS(u32* out_3DSAddr, u8** out_TLSpointer)
+{
+ //find a free slot
+ int i = 0; //can never hit the end because of ResourceLimit
+ for (; i < sizeof(m_TLSused) / sizeof(bool); i++)
+ {
+ if (!m_TLSused[i])
+ break;
+ }
+ m_TLSused[i] = true;
+
+ //allocate if not already done
+ if (m_TLSpointer[i / 8] == NULL)
+ {
+ m_TLSpointer[i / 8] = (u8*)calloc(0x1000, sizeof(u8));
+ if (m_TLSpointer[i / 8] == NULL)
+ {
+ return -1;
+ }
+ auto chunk = new MemChunk();
+ chunk->data = m_TLSpointer[i / 8];
+ chunk->size = 0x1000;
+ AddPages(i * 0x200 + TLS_OFFSET, chunk->size, chunk->data, chunk, PERMISSION_RW, MEMTYPE_TLS, NULL);
+ }
+
+ *out_TLSpointer = m_TLSpointer[i / 8] + (i % 0x8) * 0x200;
+ *out_3DSAddr = i * 0x200 + TLS_OFFSET;
+
+ return Success;
+}
+Result KMemoryMap::RemoveTLS(u32 DSAddr)
+{
+ u32 offset = (DSAddr - TLS_OFFSET) / 0x200;
+ if (m_TLSused[offset])
+ return -1;
+ m_TLSused[offset] = false;
+
+ //remove the Page if the Page is empty
+ bool empty = true;
+ for (int i = 0; i < 8; i++)
+ {
+ if (m_TLSused[(offset / 8) * 8 + i])
+ empty = false;
+ }
+ if (empty)
+ {
+ RemovePages((offset / 8) * 8 + TLS_OFFSET, 0x1000);
+ }
+ return Success;
+}
+s32 KMemoryMap::AllocFreeGSP(bool new3DS,u32 size)
+{
+ int start = new3DS ? 0x10000000 : 0x8000000;
+ int found = -1;
+ int needed = (size+0xFFF) / 0x1000;
+ for (; 0 < start; start -= 0x1000)
+ {
+ if (!MEM_FCRAM_Used[start / 0x1000])
+ {
+ if (needed == 0)
+ {
+ found = start;
+ break;
+ }
+ needed--;
+ }
+ else
+ needed = (size + 0xFFF) / 0x1000;
+ }
+ if (found != -1)
+ {
+ needed = (size + 0xFFF) / 0x1000;
+ for (int poi = found / 0x1000; needed > 0; needed--)
+ {
+ MEM_FCRAM_Used[poi] = true;
+ poi++;
+ }
+ }
+ return found;
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+
+//tools
+
+KMutex::KMutex(KProcess *owner, bool locked) : m_locked(locked), m_owner(owner), m_lockedThread(NULL)
+{
+
+}
+void KMutex::Release()
+{
+ m_Mutex.Lock();
+ KThread* found;
+ found = SynGetNextPrio();
+ if (found)
+ SynFree(0, found);
+ else
+ m_locked = false;
+ m_Mutex.Unlock();
+}
+bool KMutex::Synchronization(KThread* thread,u32 &error)
+{
+ m_Mutex.Lock();
+ if (m_locked)
+ {
+ if (m_lockedThread == thread) //todo check if this is true...
+ {
+ m_Mutex.Unlock();
+ return false;
+ }
+ m_Mutex.Unlock();
+ return true;
+ }
+ else
+ {
+ m_lockedThread = thread;
+ m_locked = true;
+ m_Mutex.Unlock();
+ return false;
+ }
+
+}
+
+bool KMutex::IsInstanceOf(ClassName name) {
+ if (name == KMutex::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+
+//tools
+
+
+KPort::KPort(char* name, u32 maxconnection) : m_Client(name, maxconnection, this), m_Server(name, maxconnection, this)
+{
+ strncpy(m_Name, name, 8);
+}
+KPort::~KPort()
+{
+
+}
+
+bool KPort::IsInstanceOf(ClassName name) {
+ if (name == KPort::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+
+//tools
+#define Read32(p) (p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24)
+
+#define dyncoresizestart (1024 * 1024 * 20)
+
+bool KProcess::Synchronization(KThread* thread, u32 &error)
+{
+ return true; //stall till the Process ends? TODO check that
+}
+KProcess::~KProcess()
+{
+ free(repretBuffer);
+}
+KProcess::KProcess(KCodeSet* codeset, u32 capabilities_num, u32* capabilities_ptr, KKernel* Kernel,bool is_firm_process)
+ : m_memory(this), m_limit(new KResourceLimit()), LINEAR_memory_virtual_address_userland(0x14000000) /*this is not like on the 3DS but it works with old and new NCCH*/
+{
+ m_ProcessID = Kernel->GetNextProcessID();
+ m_Kernel = Kernel;
+ m_Kernel->AddProcess(this, is_firm_process);
+ memset(m_AllowedInterrupt, 0, sizeof(m_AllowedInterrupt));
+ memset(m_systemcallmask, 0, sizeof(m_systemcallmask));
+
+ m_exheader_flags = 0;
+ m_codeset.SetObject(codeset);
+
+ ParseArm11KernelCaps(capabilities_num, capabilities_ptr);
+
+ KResourceLimit* limit = (KResourceLimit*) *m_limit;
+ // TODO: this is wrong but it should be overwritten after the boot anyway.
+ limit->SetMaxValue(0, 0x04);
+ limit->SetMaxValue(1, 0x01680000);
+ limit->SetMaxValue(2, 0xC5);
+ limit->SetMaxValue(3, 0xF5);
+ limit->SetMaxValue(4, 0x23);
+ limit->SetMaxValue(5, 0x3F);
+ limit->SetMaxValue(6, 0x2B);
+ limit->SetMaxValue(7, 0x1D);
+ limit->SetMaxValue(8, 0x2A);
+ limit->SetMaxValue(9, 0x3E8);
+
+ //map shared mem
+ m_memory.AddPages(0x1FF80000,
+ chunk_Configuration->size, chunk_Configuration->data, chunk_Configuration,
+ PERMISSION_R, MEMTYPE_SHAREDMEMORY, NULL);
+ m_memory.AddPages(0x1FF81000,
+ chunk_Shared->size, chunk_Shared->data, chunk_Shared,
+ (m_exheader_flags & 0x8) ? PERMISSION_RW : PERMISSION_R, MEMTYPE_SHAREDMEMORY, NULL);
+
+ codeset->MapInto(&m_memory, m_exheader_flags & (1 << 12));
+
+ repretBuffer = (char*)malloc(dyncoresizestart); //must be increasable later
+ repretBuffersize = dyncoresizestart;
+ repretBuffertop = 0;
+ CreamCache = new bb_map;
+
+}
+
+void KProcess::ParseArm11KernelCaps(u32 capabilities_num, u32* capabilities_ptr)
+{
+ u32 handletable_size= 0x200;
+
+ for (u32 i = 0; i < capabilities_num; i++)
+ {
+ u32 desc = Read32(((u8*)(&capabilities_ptr[i])));
+
+ // Allowed interrupts.
+ if ((desc & 0xF0000000) == 0xE0000000) {
+ for (int j = 0; j < 4; j++) {
+ int irq = (desc >> (j * 7)) & 0x7F;
+
+ if (irq != 0x7F)
+ m_AllowedInterrupt[irq] = true;
+ }
+ }
+ // Allowed syscalls.
+ else if ((desc & (0x1f << 27)) == (0x1e << 27)) {
+ int init = ((desc >> 24) & 7)*0x18;
+
+ for (int i = init; i < init + 0x18 && i < 0x80; i++) {
+ int offset = i - init;
+ m_systemcallmask[i] = (desc >> offset) & 0x1;
+ }
+ }
+ // Handle-table size.
+ else if ((desc & (0xff << 24)) == (0xfe << 24)) {
+ handletable_size= desc & 0x3FF;
+ }
+ // Flags
+ else if ((desc & (0x1ff << 23)) == (0x1fe << 23)) {
+ m_exheader_flags = desc;
+ }
+ // Mapping I/O.
+ else if ((desc & (0xfff << 20)) == (0xffe << 20)) {
+ m_memory.AddIOMem((desc & 0xFFFFF) << 12, 0x1000, PERMISSION_RW);
+ }
+ // Mapping STATIC.
+ else if ((desc & (0x7ff << 21)) == (0x7fc << 21)) {
+ u32 startaddr = (desc & 0xFFFFF) << 12;
+
+ if(i++ > capabilities_num) {
+ XDSERROR("Error while parsing descriptor for STATIC memory.\n");
+ continue;
+ }
+
+ u32 desc2 = Read32(((u8*)(&capabilities_ptr[i])));
+
+ if ((desc2 & 0xFFE00000) != (desc & 0xFFE00000)) {
+ XDSERROR("Error while parsing descriptor for STATIC memory (%08x %08x).\n",
+ desc, desc2);
+ continue;
+ }
+
+ u32 endaddr = (desc2 & 0xFFFFF) << 12;
+ m_memory.AddIOMem((desc & 0xFFFFF) << 12, endaddr - startaddr,
+ (desc & (1 << 20)) ? PERMISSION_R : PERMISSION_RW);
+ }
+ // do nothing
+ else if (desc == 0xFFFFFFFF){
+ }
+ else {
+ LOG("Unknown descriptor %x\n", desc);
+ }
+ }
+
+ m_handles = new KHandleTable(this, handletable_size);
+}
+
+void KProcess::AddQuickCode(u8* buf, size_t size) {
+ m_memory.AddCodeSegment(0x100000, size, buf, PERMISSION_RWX); // TEMP
+}
+
+Result KProcess::WaitSynchronization(s64 timeout) {
+ return -1; // TODO
+}
+
+void KProcess::Destroy() {
+ // Empty.
+}
+
+KMemoryMap* KProcess::getMemoryMap() {
+ return &m_memory;
+}
+
+void KProcess::AddThread(KThread * thread)
+{
+ //TODO start the thread
+ u32 out_3DSAddr;
+ u8* out_TLSpointer;
+ m_memory.AddTLS(&out_3DSAddr, &out_TLSpointer);
+ thread->m_TSL3DS = out_3DSAddr;
+ thread->m_TSLpointer = out_TLSpointer;
+ m_Threads.AddItem(thread);
+ m_Kernel->StartThread(thread);
+}
+
+const char* KProcess::GetName()
+{
+ return ((KCodeSet*)*m_codeset)->GetName();
+}
+
+u32 KProcess::GetProcessID()
+{
+ return m_ProcessID;
+}
+
+void KProcess::SetResourceLimit(KResourceLimit* lim)
+{
+ m_limit.SetObject(lim);
+}
+
+KResourceLimit* KProcess::GetResourceLimit()
+{
+ return (KResourceLimit*)*m_limit;
+}
+
+KHandleTable* KProcess::GetHandleTable()
+{
+ return m_handles;
+}
+
+bool KProcess::IsInstanceOf(ClassName name) {
+ if (name == KProcess::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Util.h"
+#include "Kernel.h"
+
+const char* g_ResourceNames[] =
+{
+ "Priority",
+ "Memory",
+ "Thread",
+ "Event",
+ "Mutex",
+ "Semaphore",
+ "Timer",
+ "Shared Memory",
+ "Address Arbiter",
+ "CPU Time"
+};
+
+bool KResourceLimit::UseResource(u32 resource_id, u32 num)
+{
+ m_Mutex.Lock();
+ m_CurrentUsedResource[resource_id] += num;
+
+ if (m_CurrentUsedResource[resource_id] > m_MaxResource[resource_id])
+ {
+ m_CurrentUsedResource[resource_id] -= num;
+ LOG("Out of resource '%s'", g_ResourceNames[resource_id]);
+ m_Mutex.Unlock();
+ return false;
+ }
+ m_Mutex.Unlock();
+ return true;
+}
+
+void KResourceLimit::FreeResource(u32 resource_id, u32 num)
+{
+ m_Mutex.Lock();
+ m_CurrentUsedResource[resource_id] -= num;
+ m_Mutex.Unlock();
+}
+
+s64 KResourceLimit::GetCurrentValue(u32 resource_id)
+{
+ return (s64)m_CurrentUsedResource[resource_id];
+}
+
+s64 KResourceLimit::GetMaxValue(u32 resource_id)
+{
+ return (s64)m_MaxResource[resource_id];
+}
+
+void KResourceLimit::SetMaxValue(u32 resource_id, s64 number)
+{
+ m_Mutex.Lock();
+ m_MaxResource[resource_id] = (s32)number;
+ m_Mutex.Unlock();
+}
+
+KResourceLimit::KResourceLimit() : m_Mutex()
+{
+ memset((void*)m_CurrentUsedResource, 0, sizeof(m_CurrentUsedResource));
+ memset((void*)m_MaxResource, 0, sizeof(m_MaxResource));
+}
+
+KResourceLimit::~KResourceLimit()
+{
+}
+
+bool KResourceLimit::IsInstanceOf(ClassName name) {
+ if (name == KResourceLimit::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+
+
+KThread* current_thread; // 0xFFFF9000
+KThread* current_core_first_thread; // *0xFFFF900C + 0xC
+SwitchContext* sc; // 0xFFF2D0A8 (can change occasionally)
+
+KScheduler::KScheduler()
+{
+
+}
+
+KScheduler::~KScheduler()
+{
+
+}
+
+void KScheduler::AddToScheduler(KThread* add_thread) // add current_scheduler-> in front of all members?
+{
+ s16 core = m_core_num;
+
+ if (m_schedule_entries[add_thread->m_thread_prio].next == NULL)
+ {
+ if (KScheduler::GetPrioType(add_thread->m_thread_prio) == PRIO_TYPE_HIGH) // highest bit of prio to differentiate between high prio and low prio
+ {
+ m_thread_high_prio |= 0x80000000UL >> KScheduler::GetPrioField(add_thread->m_thread_prio); // shift the top bit down to the right bit in the field
+ }
+ else
+ {
+ m_thread_low_prio |= 0x80000000UL >> KScheduler::GetPrioField(add_thread->m_thread_prio);
+ }
+ }
+
+ add_thread->m_prev = m_schedule_entries[add_thread->m_thread_prio].prev; // prev entry becomes previous for thread
+ add_thread->m_next = NULL; // this thread is the new last
+
+ if (m_schedule_entries[add_thread->m_thread_prio].prev == NULL) // if entry->prev is NULL, entry->next is the new thread
+ {
+ m_schedule_entries[add_thread->m_thread_prio].next = add_thread;
+ }
+ else
+ {
+ m_schedule_entries[add_thread->m_thread_prio].prev->m_next = add_thread; // otherwise, the next thread from entry->prev is the new thread
+ }
+
+ m_schedule_entries[add_thread->m_thread_prio].prev = add_thread; // then set entry->prev to the new thread
+
+ m_scheduler_thread_count++;
+
+ if (core == m_core_num) // if the core being scheduled has not changed since the beginning of this function, swap context
+ {
+ if (current_thread->m_thread_prio > add_thread->m_thread_prio) // and if the prio of the current thread (0xFFFF9000) is greater than the thread being added, we need to do some scheduling because this thread needs to run
+ {
+ m_switch_context = true;
+ }
+ }
+
+ if (core != m_core_num) // if the core being scheduled has changed, trigger interrupt 8 for the other core
+ {
+ m_swap_intr_core = true;
+ }
+
+}
+
+void KScheduler::RemoveFromScheduler(KThread* sub_thread) // add current_scheduler-> in front of all members?
+{
+ if (m_schedule_entries[sub_thread->m_thread_prio].next != NULL && m_schedule_entries[sub_thread->m_thread_prio].prev->m_thread_id == m_schedule_entries[sub_thread->m_thread_prio].next->m_thread_id) // if entry->next and entry->prev are non-zero and the same,
+ {
+ if (KScheduler::GetPrioType(sub_thread->m_thread_prio) == PRIO_TYPE_HIGH) // highest bit of prio to differentiate between high prio and low prio
+ {
+ bit32 r_mask = 0x80000000UL >> KScheduler::GetPrioField(sub_thread->m_thread_prio); // shift the top bit down to the right bit in the field
+ m_thread_high_prio &= ~r_mask; // clear the bit in the mask
+ }
+ else
+ {
+ bit32 r_mask = 0x80000000UL >> KScheduler::GetPrioField(sub_thread->m_thread_prio);
+ m_thread_low_prio &= ~r_mask;
+ }
+ }
+
+ if (sub_thread->m_prev == NULL) // if the old thread to be removed is the first
+ {
+ m_schedule_entries[sub_thread->m_thread_prio].next = sub_thread->m_next; // set entry-> next to the thread after the one being removed
+ }
+ else
+ {
+ sub_thread->m_prev->m_next = sub_thread->m_next; // set it to skip over the old thread
+ }
+
+ if (sub_thread->m_next == NULL) // link things together without the old thread in there
+ {
+ sub_thread->m_next->m_prev = sub_thread->m_prev;
+ }
+ else
+ {
+ m_schedule_entries[sub_thread->m_thread_prio].prev = sub_thread->m_next;
+ }
+
+ m_scheduler_thread_count--;
+
+ if (current_thread->m_thread_id == sub_thread->m_thread_id) // if the current thread is the one being removed, do another schedule and trigger intr 8 for the other core
+ {
+ m_switch_context = true;
+ m_swap_intr_core = true;
+ }
+}
+
+void KScheduler::AdjustScheduler(KThread* schedule_thread, bit8 scheduling_task) // scheduling_task is schedule_thread->m_scheduling_task read just before calling this function
+{
+ if (scheduling_task == schedule_thread->m_scheduling_task)
+ {
+ return;
+ }
+ else if (scheduling_task == TASK_PERFORM)
+ {
+ KScheduler::RemoveFromScheduler(schedule_thread);
+ }
+ else if (schedule_thread->m_scheduling_task == TASK_PERFORM)
+ {
+ KScheduler::AddToScheduler(schedule_thread);
+ }
+
+}
+
+KThread* KScheduler::ScheduleThreads(int z)
+{
+ int b;
+
+ if (z == 0)
+ {
+ b = 1;
+ }
+ else
+ {
+ b = 0;
+ }
+
+ if (m_need_post_intr_reschedule)
+ {
+ if (!(current_core_first_thread->m_scheduling_task & 0xF))
+ {
+ KScheduler::Reschedule(current_core_first_thread, 1);
+ }
+ }
+
+ if (m_unknown3)
+ {
+ // function here, but i don't think KScheduler+0x24 is ever set
+ }
+
+ if (z) // CMP R4, #0, again this is pretty much never set to anything besides 0 - every instance in the current kernel is 0
+ {
+
+ }
+
+ KThread* load_thread = NULL;
+
+ while (load_thread == NULL)
+ {
+ u32 zero_count = Common::CountLeadingZeros(m_thread_high_prio);
+
+ if (zero_count == PRIO_FIELD_COUNT) // did it count all the way through high prio thread bitfield without finding a 1?
+ {
+ zero_count = Common::CountLeadingZeros(m_thread_low_prio) + PRIO_FIELD_COUNT; // if so, do the same with the low prio thread mask
+ }
+
+ if (zero_count > PRIO_MAX)
+ {
+ load_thread = m_scheduler_thread;
+ }
+ else
+ {
+ load_thread = m_schedule_entries[zero_count].next;
+ }
+ }
+
+
+ return NULL;
+}
+
+void KScheduler::SwitchKernelContext(int z)
+{
+ if (!m_switch_context)
+ {
+ return;
+ }
+
+ m_switch_context = false;
+ m_scheduler_count = 1;
+
+ // enable intr
+
+ /* TODO: thread context pages (0xFF4xxxxx)
+ *
+ *
+ *
+ *
+ *
+ */
+
+ KThread* t_out = KScheduler::ScheduleThreads(z);
+
+ while (1)
+ {
+ // disable intr
+ if (t_out != NULL)
+ {
+ // load KThread context here
+ }
+
+ if (!m_switch_context)
+ {
+ m_scheduler_count = 0;
+ return;
+ }
+
+ m_switch_context = false;
+
+ // enable intr
+
+ // TODO
+
+ t_out = KScheduler::ScheduleThreads(0);
+ }
+}
+
+void KScheduler::Reschedule(KThread* schedule_thread, bit8 lower_scheduling_mask)
+{
+ KScheduler::SwitchThreadContext(sc);
+
+ bit8 t_mask = schedule_thread->m_scheduling_task;
+ schedule_thread->m_scheduling_task = (t_mask & 0xF0) | (lower_scheduling_mask & 0xF);
+
+ KScheduler::AdjustScheduler(schedule_thread, t_mask);
+
+ KScheduler::ReturnThreadContext(sc);
+}
+
+void KScheduler::SwitchThreadContext(SwitchContext* c)
+{
+ if (current_thread->m_thread_id == c->current_thread_on_switch->m_thread_id)
+ {
+ c->running_thread_switch_count++; // count goes up when it tries to switch to the currently running thread
+ return;
+ }
+
+ while (1)
+ {
+ //while (c->current_thread_on_switch != NULL)
+ {
+ // do nothing, just wait
+ }
+
+ m_scheduler_count++; // this is a ldrex/strex pair
+
+ if (c->current_thread_on_switch == NULL) // ldrex
+ {
+ c->current_thread_on_switch = current_thread; // strex, but this doesn't always happen, so force a strex either way to complete the ldrex/strex pair by writing back current value
+ // if this particular strex can't complete(not the one referenced above, this specific one), this is locked elsewhere and it's time to exit
+ // break;
+ }
+ else
+ {
+ //c->current_thread_on_switch = c->current_thread_on_switch; // the second strex, referenced above, to complete the pair IF c->current_thread_on_switch is NOT NULL
+ }
+
+ if (m_scheduler_count > 1)
+ {
+ if (m_scheduler_count != 1) // ldrex/strex
+ {
+ m_scheduler_count--;
+ continue;
+ }
+ else
+ {
+ // clrex
+ }
+ }
+
+ // save cpsr
+ // disable intr
+
+ m_scheduler_count--; // ldrex/strex
+
+ if (m_switch_context)
+ {
+ KScheduler::SwitchKernelContext(0);
+ }
+
+ // writeback cpsr
+ }
+
+ m_scheduler_count = 1;
+}
+
+void KScheduler::ReturnThreadContext(SwitchContext* c)
+{
+ if ((c->running_thread_switch_count -= 1) != 0)
+ {
+ return;
+ }
+
+ c->current_thread_on_switch = NULL;
+
+ if (m_scheduler_count > 1)
+ {
+ if (m_scheduler_count != 1)
+ {
+ return;
+ }
+
+ // clrex - if this ends up equal to 1 the second time, another context switch has happened somewhere else and there may be another lock - clear it
+ }
+
+ if (m_swap_intr_core)
+ {
+ KScheduler::OtherCoreTriggerIntr8();
+ }
+
+ // save cpsr
+ // disable intr
+
+ m_scheduler_count--; // ldrex/strex
+
+ if (m_switch_context)
+ {
+ KScheduler::SwitchKernelContext(0);
+ }
+
+ // enable intr
+}
+
+void KScheduler::SoftContextSwitch(SwitchContext* c)
+{
+ if ((c->running_thread_switch_count -= 1) != 0)
+ {
+ return;
+ }
+
+ c->current_thread_on_switch = NULL;
+
+ if (m_scheduler_count > 1)
+ {
+ if (m_scheduler_count != 1) // ldrex/strex
+ {
+ m_scheduler_count--;
+ return;
+ }
+
+ // clrex
+ }
+
+ // save cpsr
+ // disable intr
+
+ m_scheduler_count--;
+
+ if (m_switch_context)
+ {
+ KScheduler::SwitchKernelContext(0);
+ }
+
+ // enable intr
+}
+
+void KScheduler::OtherCoreTriggerIntr8()
+{
+
+}
+
--- /dev/null
+#include "Kernel.h"
+
+//tools
+
+
+bool KSemaphore::Synchronization(KThread* thread, u32 &error)
+{
+ m_Mutex.Lock();
+ if (m_count < m_maxcount)
+ {
+ m_count++;
+ m_Mutex.Unlock();
+ return false;
+ }
+ m_Mutex.Unlock();
+ return true;
+}
+s32 KSemaphore::ReleaseSemaphore(u32 releaseCount, u32 &count)
+{
+ m_Mutex.Lock();
+ if (m_count < releaseCount)
+ {
+ count = m_count;
+ m_count = 0;
+ }
+ else
+ {
+ count = releaseCount;
+ m_count -= releaseCount;
+ }
+ if (releaseCount > 0)
+ {
+ KThread *found;
+ do
+ {
+ found = SynGetNextPrio();
+ if (found)
+ {
+ SynFree(0, found);
+ m_count++;
+ }
+
+ } while (found && m_count < m_maxcount);
+ }
+
+ m_Mutex.Unlock();
+ return Success;
+}
+KSemaphore::KSemaphore(u32 count, u32 maxcount, KProcess *owner) :m_Mutex()
+{
+ m_count = count;
+ m_maxcount = maxcount;
+ m_owner = owner;
+}
+
+bool KSemaphore::IsInstanceOf(ClassName name) {
+ if (name == KSemaphore::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+
+//tools
+
+bool KServerPort::Synchronization(KThread* thread, u32 &error)
+{
+ if (m_sessionToTake.list) //start now
+ {
+ return false;
+ }
+ return true; //stall
+}
+
+KServerPort::KServerPort(char* name, u32 maxconnection, KPort *owner) : m_sessionToTake()
+{
+ m_owner = owner;
+}
+KServerPort::~KServerPort()
+{
+}
+KServerSession * KServerPort::AcceptSesion()
+{
+ KSession *s= m_sessionToTake.list->data;
+ m_sessionToTake.RemoveItem(m_sessionToTake.list);
+ if (s == NULL)
+ return NULL;
+ return &s->m_Server;
+}
+
+bool KServerPort::IsInstanceOf(ClassName name) {
+ if (name == KServerPort::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+
+//tools
+
+void KServerSession::Destroy() {
+ delete m_owner;
+ // Empty. Overridden.
+}
+bool KServerSession::Synchronization(KThread* thread, u32 &error)
+{
+ KThread * tnew = m_owner->m_Client.SynGetNextPrio();
+ if (tnew && !m_owner->m_Server.m_waitingForCmdResp)
+ {
+ m_owner->m_Server.m_waitingForCmdResp = tnew;
+ m_owner->m_Server.m_processingCmd = thread;
+ m_owner->Communicate(tnew, thread, false);
+ return false;
+ }
+
+ return true; //stall
+}
+
+KServerSession::KServerSession(KSession *owner)
+{
+ m_owner = owner;
+ m_processingCmd = NULL;
+ m_waitingForCmdResp = NULL;
+}
+KServerSession::~KServerSession()
+{
+
+}
+s32 KServerSession::reply(KThread * sender)
+{
+ if (!m_waitingForCmdResp)
+ {
+ LOG("error responding to no existing thread");
+ return -1;
+ }
+ if (!m_processingCmd)
+ {
+ LOG("error responding to no existing cmd");
+ return -1;
+ }
+
+ m_owner->Communicate(sender, m_waitingForCmdResp,true);
+
+ m_owner->m_Client.SynFree(0, m_waitingForCmdResp);
+
+ m_processingCmd = NULL;
+ m_waitingForCmdResp = NULL;
+ return 0;
+}
+bool KServerSession::IsInstanceOf(ClassName name) {
+ if (name == KServerSession::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+
+#define LOGCOMMUNICATION
+
+//tools
+
+
+KSession::KSession(KPort * owner) : m_Server(this), m_Client(this)
+{
+ m_owner = owner;
+}
+KSession::~KSession()
+{
+
+}
+bool KSession::IsInstanceOf(ClassName name) {
+ if (name == KSession::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
+s32 KSession::Communicate(KThread* sender, KThread* recver, bool IsResponse)
+{
+ u32* senddata = (u32*)(sender->m_TSLpointer + 0x80);
+ u32* recvdata = (u32*)(recver->m_TSLpointer + 0x80);
+#ifdef LOGCOMMUNICATION
+ if (IsResponse)
+ {
+ if (m_owner)
+ {
+ LOG("responding %s <- %s port %s", recver->m_owner->GetName(), sender->m_owner->GetName(), m_owner->m_Name);
+ }
+ else
+ {
+ LOG("responding %s <- %s", recver->m_owner->GetName(), sender->m_owner->GetName());
+ }
+ }
+ else
+ {
+ if (m_owner)
+ {
+ LOG("sending %s -> %s port %s", sender->m_owner->GetName(), recver->m_owner->GetName(), m_owner->m_Name);
+ }
+ else
+ {
+ LOG("sending %s -> %s", sender->m_owner->GetName(), recver->m_owner->GetName());
+ }
+ }
+#endif
+ u32 cmd = *senddata++;
+ *recvdata++ = cmd;
+ u32 translated = cmd & 0x3F;
+ u32 nomal = (cmd >> 6) & 0x3F;
+#ifdef LOGCOMMUNICATION
+ LOG("cmd %08x", cmd);
+#endif
+ for (u32 i = 0; i < nomal; i++)
+ {
+ u32 data = *senddata++;
+#ifdef LOGCOMMUNICATION
+ LOG("data %08x", data);
+#endif
+ *recvdata++ = data;
+ }
+ for (u32 i = 0; i < translated; )
+ {
+ u32 descriptor = *senddata++;
+ if (descriptor & 0x1)
+ {
+ LOG("descriptor with sec flag set %08x please send to XDS dev Team", descriptor);
+ }
+ switch (descriptor & 0xE)
+ {
+ case 0:
+ {
+ if (descriptor == 0x00000020)
+ {
+ u32 PID = sender->m_owner->GetProcessID();
+#ifdef LOGCOMMUNICATION
+ LOG("PID %08x", PID);
+#endif
+ i+= 2;
+ *recvdata++ = descriptor;
+ *recvdata++ = PID;
+ senddata++;
+ }
+ else if ((descriptor&0x03FFFFFF) == 0x00000000)
+ {
+ *recvdata++ = descriptor;
+ i++;
+ int size = (descriptor >> 26) + 1;
+ for (int j = 0; j < size && i < translated; j++)
+ {
+ u32 data = *senddata++;
+ KAutoObjectRef obj;
+ u32 newhand = 0;
+ s32 ret;
+ if (data == 0xFFFF8000)
+ {
+ obj.SetObject(sender);
+ ret = Success;
+ }
+ else if (data == 0xFFFF8001)
+ {
+ obj.SetObject(sender->m_owner);
+ ret = Success;
+ }
+ else
+ {
+ ret = sender->m_owner->GetHandleTable()->GetHandleObject(obj, data);
+ }
+ if (ret == Success)
+ {
+ recver->m_owner->GetHandleTable()->CreateHandle(newhand, *obj);
+ }
+
+#ifdef LOGCOMMUNICATION
+ LOG("handle %08x -> %08x", data, newhand);
+#endif
+ *recvdata++ = newhand;
+ i++;
+ }
+
+ }
+ else if ((descriptor & 0x03FFFFFF) == 0x00000010)
+ {
+ *recvdata++ = descriptor;
+ i++;
+ int size = (descriptor >> 26) + 1;
+ for (int j = 0; j < size && i < translated; j++)
+ {
+ u32 data = *senddata++;
+ KAutoObjectRef obj;
+ u32 newhand = 0;
+ s32 ret = sender->m_owner->GetHandleTable()->GetHandleObject(obj, data);
+ if (ret == Success)
+ {
+ recver->m_owner->GetHandleTable()->CreateHandle(newhand, *obj);
+ sender->m_owner->GetHandleTable()->CloseHandle(data);
+ }
+#ifdef LOGCOMMUNICATION
+ LOG("handle close %08x -> %08x", data, newhand);
+#endif
+ *recvdata++ = newhand;
+ i++;
+ }
+
+ }
+ else {
+ recvdata++;
+ *senddata = descriptor;
+ senddata++;
+ i++;
+ LOG("unknown descriptor %08x", descriptor);
+ }
+ }
+ break;
+ case 0x2:
+ {
+ *recvdata = descriptor;
+ recvdata++;
+
+ u32 srcaddr = *senddata;
+
+ u32* pointerlist = (u32*)(recver->m_TSLpointer + 0x180);
+
+ u32 id = (descriptor >> 10) & 0xF;
+ u32 sizewanted = (descriptor >> 14);
+
+ pointerlist += id * 2;
+ u32 tarsize = (*pointerlist++ >> 14);
+ u32 targed = *pointerlist;
+
+
+#ifdef LOGCOMMUNICATION
+ LOG("TLS cpy %08x (id %01x size %06x) (%08x %08x)", srcaddr, (descriptor >> 10) & 0xF, (descriptor >> 14), targed, tarsize);
+#endif
+ if (targed != 0 && tarsize >= sizewanted)
+ {
+ for (u32 i = 0; i < sizewanted; i++)
+ {
+ u8 data = 0;
+ s32 ret = sender->m_owner->getMemoryMap()->Read8(srcaddr + i, data);
+ if (ret != Success)
+ {
+ LOG("IPC Communicate error reading from %08x", srcaddr);
+ break;
+ }
+#ifdef LOGCOMMUNICATION
+ printf("%02x",data);
+#endif
+ recver->m_owner->getMemoryMap()->Write8(targed + i, data);
+ if (ret != Success)
+ {
+ LOG("IPC Communicate error writing from %08x", targed);
+ break;
+ }
+ }
+ *recvdata = targed;
+#ifdef LOGCOMMUNICATION
+ LOG("");
+#endif
+ }
+ else
+ {
+ *recvdata = 0;
+ }
+ recvdata++;
+ senddata++;
+ i += 2;
+ }
+ break;
+ case 0x4:
+ {
+ recver->m_owner->m_Kernel->m_IPCFIFOAdresses[(descriptor >> 4) & 0xF] = sender->m_owner->getMemoryMap();
+ recver->m_owner->m_Kernel->m_IPCFIFOAdressesRO[(descriptor >> 4) & 0xF] = false;
+
+ *recvdata = descriptor;
+ recvdata++;
+
+ u32 data = *senddata++;
+#ifdef LOGCOMMUNICATION
+ LOG("IPC translate RW %08x (id %01x size %06x)", data, (descriptor >> 4) & 0xF, (descriptor >> 8));
+#endif
+ *recvdata++ = data;
+ i += 2;
+ }
+ break;
+ case 0x6:
+ {
+ recver->m_owner->m_Kernel->m_IPCFIFOAdresses[(descriptor>>4)&0xF] = sender->m_owner->getMemoryMap();
+ recver->m_owner->m_Kernel->m_IPCFIFOAdressesRO[(descriptor >> 4) & 0xF] = true;
+
+ *recvdata = descriptor;
+ recvdata++;
+
+ u32 data = *senddata++;
+#ifdef LOGCOMMUNICATION
+ LOG("IPC translate RO %08x (id %01x size %06x)", data, (descriptor >> 4) & 0xF, (descriptor >> 8));
+#endif
+ *recvdata++ = data;
+ i += 2;
+ break;
+ }
+ case 0xA:
+ case 0xC:
+ case 0xE:
+ {
+ MemoryPermissions perm;
+ switch (descriptor & 0xE)
+ {
+ case 0xA:
+ perm = PERMISSION_R;
+ break;
+ case 0xC:
+ perm = PERMISSION_RW; //this is how it it else the fs fails
+ break;
+ case 0xE:
+ perm = PERMISSION_RW;
+ break;
+ }
+ u32 size = (descriptor >> 4);
+ *recvdata = descriptor;
+ recvdata++;
+ u32 data = *senddata++;
+ size = (((size + data + 0xFFF) / 0x1000) * 0x1000) - data;
+ int j = 0x04000000;
+ for (; j < 0x08000000; j += 0x1000)
+ {
+ if (sender->m_owner->getMemoryMap()->IPCMap(j, data, size, perm, recver->m_owner->getMemoryMap()) == Success)
+ break;
+ }
+ if (j >= 0x08000000)
+ LOG("OUT OF IPC MEM implement cleanup plz");
+
+#ifdef LOGCOMMUNICATION
+ switch (descriptor & 0xE)
+ {
+ case 0xA:
+ LOG("IPC map RO %08x (size %08x) to %08x", data, size, j);
+ break;
+ case 0xC:
+ LOG("IPC map WO %08x (size %08x) to %08x", data, size, j);
+ break;
+ case 0xE:
+ LOG("IPC map RW %08x (size %08x) to %08x", data, size, j);
+ break;
+ }
+#endif
+
+ *recvdata++ = j + (data &0xFFF);
+ i += 2;
+ break;
+ }
+ default:
+ LOG("unknown descriptor %08x", descriptor);
+ recvdata++;
+ *senddata = descriptor;
+ i++;
+ break;
+ }
+ }
+ return Success;
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+
+KSharedMemory::KSharedMemory(u32 addr, u32 size, u32 myperm, u32 otherpem, KProcess *owner) : m_addr(addr), m_owner(owner), m_myperm(myperm), m_otherpem(otherpem)
+{
+ m_size = ((size / 0x1000) + 1) * 0x1000; //round always up
+ if (addr == 0)
+ {
+ m_IsGSP = true;
+ m_addr = m_owner->getMemoryMap()->AllocFreeGSP(false, size);//todo find out if the system is a new 3DS
+ }
+ else
+ {
+ m_IsGSP = false;
+ }
+}
+s32 KSharedMemory::map(u32 addr, u32 myperm, u32 otherpem, KProcess *caller) //todo check perm
+{
+ if (addr == 0)
+ {
+ if (m_IsGSP)
+ {
+ if (caller == m_owner)
+ {
+ MemChunk* chunk = (MemChunk*)malloc(sizeof(MemChunk));
+
+ if (chunk == NULL) {
+ free(chunk);
+ return -1;
+ }
+
+ chunk->size = m_size;
+ chunk->data = &Mem_FCRAM[m_addr];
+ return caller->getMemoryMap()->AddPages(caller->LINEAR_memory_virtual_address_userland + m_addr, m_size, &Mem_FCRAM[m_addr], chunk, m_myperm & myperm, MEMTYPE_SHAREDMEMORY, NULL);
+ }
+ else
+ {
+ MemChunk* chunk = (MemChunk*)malloc(sizeof(MemChunk));
+
+ if (chunk == NULL) {
+ free(chunk);
+ return -1;
+ }
+
+ chunk->size = m_size;
+ chunk->data = &Mem_FCRAM[m_addr];
+ return caller->getMemoryMap()->AddPages(caller->LINEAR_memory_virtual_address_userland + m_addr, m_size, &Mem_FCRAM[m_addr], chunk, m_otherpem & myperm, MEMTYPE_SHAREDMEMORY, NULL);
+
+ }
+ }
+ else
+ {
+ return -1; //todo get the correct error
+ }
+ }
+ else
+ {
+ if (!m_IsGSP)
+ {
+ if (caller == m_owner)
+ {
+ return -1;
+ }
+ else
+ {
+ return m_owner->getMemoryMap()->IPCMap(addr,m_addr , m_size, m_otherpem & myperm, caller->getMemoryMap());
+ }
+ }
+ else
+ {
+ if (caller == m_owner)
+ {
+ MemChunk* chunk = (MemChunk*)malloc(sizeof(MemChunk));
+
+ if (chunk == NULL) {
+ free(chunk);
+ return -1;
+ }
+
+ chunk->size = m_size;
+ chunk->data = &Mem_FCRAM[m_addr];
+ return caller->getMemoryMap()->AddPages(addr, m_size, &Mem_FCRAM[m_addr], chunk, m_myperm & myperm, MEMTYPE_SHAREDMEMORY, NULL);
+ }
+ else
+ {
+ MemChunk* chunk = (MemChunk*)malloc(sizeof(MemChunk));
+
+ if (chunk == NULL) {
+ free(chunk);
+ return -1;
+ }
+
+ chunk->size = m_size;
+ chunk->data = &Mem_FCRAM[m_addr];
+ return caller->getMemoryMap()->AddPages(addr, m_size, &Mem_FCRAM[m_addr], chunk, m_otherpem & myperm, MEMTYPE_SHAREDMEMORY, NULL);
+ }
+ }
+ }
+ return 0;
+}
+
+bool KSharedMemory::IsInstanceOf(ClassName name) {
+ if (name == KSharedMemory::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+//todo correct ResourceLimmit attatch and use ResourceLimmit, timeout, the port stuff is not 100% correct
+
+#define SWILOG
+//#define SHOWCLEAREVENT
+
+struct CodeSetInfo
+{
+ u8 Name[8];
+ u16 unk1;
+ u16 unk2;
+ u32 unk3;
+ u32 TEXTAddr;
+ u32 TEXTSize;
+ u32 ROAddr;
+ u32 ROSize;
+ u32 RWAddr;
+ u32 RWSize;
+ u32 PagesTEXT;
+ u32 PagesRO;
+ u32 PagesRW;
+ u32 unk4;
+ u8 TitleID[8];
+};
+
+struct DmaSubConfig {
+ s8 peripheral_id; // @0 If not *_ALT_CFG set, this must be < 0x1E.
+ u8 type; // @1 Accepted values: 4=fixed_addr??, 8=increment_addr??, 12=lgy_fb_copy?, 15=userspace_copy?
+ s16 unk3; // @2 Must be 0 or multiple of 4?
+ s16 transfer_size; // @4 Must not be 0 if peripheral_id == 0xFF.
+ s16 unk4; // @6
+ s16 transfer_stride; // @8
+};
+
+struct DmaConfig {
+ s8 channel_sel; // @0 Selects which DMA channel to use: 0-7, -1 = don't care.
+ u8 endian_swap_size; // @1 Accepted values: 0=none, 2=16bit, 4=32bit, 8=64bit.
+ u8 flags; // @2 bit0: SRC_CFG, bit1: DST_CFG, bit2: SHALL_BLOCK, bit3: ???, bit6: SRC_ALT_CFG, bit7: DST_ALT_CFG
+ u8 padding;
+ DmaSubConfig src_cfg;
+ DmaSubConfig dst_cfg;
+};
+
+static u64 Read64(uint8_t p[8])
+{
+ u64 temp = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24 | (u64)(p[4]) << 32 | (u64)(p[5]) << 40 | (u64)(p[6]) << 48 | (u64)(p[7]) << 56;
+ return temp;
+}
+
+#define PAGE_SIZE 0x1000
+
+void ProcessSwi(u8 swi, u32 Reg[15], KThread * currentThread)
+{
+#ifdef SWILOG
+ //LOG("Process %s thread %d syscall %02X", currentThread->m_owner->GetName(), currentThread->m_thread_id, swi);
+#endif
+ if (!currentThread->m_owner->m_systemcallmask[swi])
+ {
+ XDSERROR("Process %s thread %u tryed to call syscall %02X but is not allowed to do that", currentThread->m_owner->GetName(), currentThread->m_thread_id, swi);
+ return; //TODO the 3DS would terminate the Process
+ }
+ switch (swi)
+ {
+ case 1://ControlMemory(u32* address, u32 addr0, u32 addr1, u32 size, <onstack> u32 operation, u32 permissions)
+ {
+ u32 op = Reg[0];
+ u32 addr0 = Reg[1];
+ u32 addr1 = Reg[2];
+ u32 size = Reg[3];
+ u32 perm = Reg[4];
+ u32 address = 0;
+ s32 ret = ControlMemory_swi(&address, addr0, addr1, size, op, perm, currentThread);
+ Reg[1] = address;
+ Reg[0] = ret;
+
+#ifdef SWILOG
+ LOG("Process %s thread %u ControlMemory (%08x %08x %08x %08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, op, addr0, addr1, size, perm, Reg[0], Reg[1]);
+#endif
+
+ return;
+ }
+ case 2://QueryMemory(MemoryInfo* info, PageInfo* out, u32 Addr)
+ {
+ u32 addr = Reg[2];
+ struct MemoryInfo Minfo;
+ struct PageInfo Pinfo;
+ s32 ret = currentThread->m_owner->getMemoryMap()->QueryMemory(&Minfo, &Pinfo, addr);
+ Reg[1] = Minfo.base_address;
+ Reg[2] = Minfo.size;
+ Reg[3] = Minfo.perm & 0x3;
+ Reg[4] = Minfo.state & 0xFF;
+ Reg[5] = Pinfo.flags;
+ Reg[0] = ret;
+#ifdef SWILOG
+ LOG("Process %s thread %u QueryMemory (%08x | %08x %08x %08x %08x %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, addr, Reg[0], Reg[1], Reg[2], Reg[3], Reg[4], Reg[5]);
+#endif
+ return;
+ }
+ case 0x5: //SetProcessAffinityMask(Handle process, u8* affinitymask, s32 processorcount)
+ {
+ u32 process = Reg[0];
+ u32 affinitymask = Reg[1];
+ u32 processorcount = Reg[2];
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetProcessAffinityMask stub (%08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, process, affinitymask, processorcount, Reg[0]);
+#endif
+ return;
+ }
+ case 0x7: //SetProcessIdealProcessor(Handle process, s32 idealprocessor)
+ {
+ u32 process = Reg[0];
+ u32 idealprocessor = Reg[1];
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetProcessAffinityMask stub (%08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, process, idealprocessor, Reg[0]);
+#endif
+ return;
+ }
+ case 0x8: //CreateThread(Handle* thread, func entrypoint, u32 arg, u32 stacktop, s32 threadpriority, s32 processorid)
+ {
+ u32 entrypoint = Reg[1];
+ u32 arg = Reg[2];
+ u32 stacktop = Reg[3];
+ u32 processorid = Reg[4];
+ s32 threadpriority = Reg[0];
+
+ KThread * thread = new KThread(processorid, currentThread->m_owner);
+ thread->m_context.reg_15 = entrypoint & ~0x1;
+ thread->m_context.cpu_registers[0] = arg;
+ thread->m_context.pc = entrypoint;
+ thread->m_context.sp = stacktop;
+ thread->m_thread_prio = threadpriority;
+
+ if (entrypoint & 0x1)
+ {
+ thread->m_context.cpsr = 0x30; //User,THUMB
+ }
+ else
+ {
+ thread->m_context.cpsr = 0x10; //User
+ }
+
+ u32 hand = 0;
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, thread);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateThread %u (%08x %08x %08x %08x %08x | %08x %08x)",
+ currentThread->m_owner->GetName(), currentThread->m_thread_id, thread->m_thread_id,
+ entrypoint, arg, stacktop, threadpriority, processorid,
+ Reg[0], Reg[1]);
+#endif
+ delete thread;
+ return;
+ }
+
+ currentThread->m_owner->AddThread(thread);
+
+ Reg[0] = 0;
+ Reg[1] = hand;
+
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateThread %u (%08x %08x %08x %08x %08x | %08x %08x)",
+ currentThread->m_owner->GetName(), currentThread->m_thread_id, thread->m_thread_id,
+ entrypoint, arg, stacktop, threadpriority, processorid,
+ Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x9: //ExitThread
+ {
+ currentThread->m_owner->m_Kernel->ReScheduler();
+ currentThread->stop();
+#ifdef SWILOG
+ LOG("Process %s thread %u ExitThread", currentThread->m_owner->GetName(), currentThread->m_thread_id);
+#endif
+ return;
+ }
+ case 0xA: //SleepThread(s64 nanoseconds)
+ {
+ u64 nanoseconds = Reg[0] | ((u64)Reg[1] << 32);
+#ifdef SWILOG
+ LOG("Process %s thread %u SleepThread %llu", currentThread->m_owner->GetName(), currentThread->m_thread_id, nanoseconds);
+#endif
+ KLinkedList<KSynchronizationObject> *list = new KLinkedList<KSynchronizationObject>();
+ list->AddItem(currentThread);
+ currentThread->SyncStall(list, true);
+
+ currentThread->m_owner->m_Kernel->m_Timedevent.AddItem(currentThread);
+ currentThread->m_owner->m_Kernel->FireNextTimeEvent(currentThread, nanoseconds + 1);
+
+ currentThread->m_owner->m_Kernel->ReScheduler();
+ return;
+ }
+ case 0xB: //GetThreadPriority(s32* priority, Handle thread)
+ {
+ u32 hand = Reg[1];
+ KThread* th;
+ if (hand == 0xffff8000)
+ th = currentThread;
+ else
+ th = (KThread*)*currentThread->m_owner->GetHandleTable()->GetHandle<KThread>(hand);
+ if (th == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetThreadPriority (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[1] = th->m_thread_prio;
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetThreadPriority (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0xC: //SetThreadPriority(Handle thread, s32 priority)
+ {
+ u32 hand = Reg[0];
+ KThread* th;
+ if (hand == 0xffff8000)
+ th = currentThread;
+ else
+ th = (KThread*)*currentThread->m_owner->GetHandleTable()->GetHandle<KThread>(hand);
+ if (th == NULL)
+ {
+
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetThreadPriority (%08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[1], Reg[0]);
+#endif
+ return;
+ }
+ th->m_thread_prio = Reg[1];
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetThreadPriority (%08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[1], Reg[0]);
+#endif
+ return;
+ }
+ case 0x12: //Run(Handle process, StartupInfo* info)
+ {
+ u32 handle = Reg[0];
+ u32 prio = Reg[1];
+ u32 stacksize = Reg[2];
+ u32 argc = Reg[3]; //not used
+ u32 argv = Reg[4]; //not used
+ u32 envp = Reg[5]; //not used
+ u32 unused;
+#ifdef SWILOG
+ LOG("Process %s thread %u Run (%08x %08x %08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, prio, stacksize, argc, argv, envp, Reg[0]);
+#endif
+ KProcess* process = (KProcess*)*currentThread->m_owner->GetHandleTable()->GetHandle<KProcess>(handle);
+ if (process == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u Run (%08x %08x %08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, prio, stacksize, argc, argv, envp, Reg[0]);
+#endif
+ return;
+ }
+ process->getMemoryMap()->ControlMemory(&unused, 0x10000000 - stacksize, 0, stacksize, OPERATION_COMMIT, PERMISSION_RW);
+
+ KThread * thread = new KThread(SERVICECORE, process); //todo find out when to use the other core
+ u32 startaddr = (process->m_exheader_flags & (1 << 12)) ? 0x14000000 : 0x00100000;
+ thread->m_context.reg_15 = startaddr;
+ thread->m_context.pc = startaddr;
+ thread->m_context.sp = 0x10000000;
+ thread->m_context.cpsr = 0x10; //User
+ process->AddThread(thread);
+
+ Reg[0] = 0;
+
+#ifdef SWILOG
+ LOG("Process %s thread %u Run (%08x %08x %08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, prio, stacksize, argc, argv, envp, Reg[0]);
+#endif
+ return;
+
+ }
+ case 0x13: //CreateMutex(Handle* mutex, bool initialLocked)
+ {
+ KMutex* lim = new KMutex(currentThread->m_owner, Reg[1]);
+ u32 hand = 0;
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, lim);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateMutex (| %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[0] = 0;
+ Reg[1] = hand;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateMutex (| %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x14: //ReleaseMutex(Handle mutex)
+ {
+ u32 hand = Reg[0];
+ KMutex* th = (KMutex*)*currentThread->m_owner->GetHandleTable()->GetHandle<KMutex>(hand);
+ if (th == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u ReleaseMutex (%08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0]);
+#endif
+ return;
+ }
+ th->Release();
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u ReleaseMutex (%08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0]);
+#endif
+ return;
+ }
+ case 0x15: //CreateSemaphore(Handle* semaphore, s32 initialCount, s32 maxCount)
+ {
+ u32 count = Reg[1];
+ u32 maxcount = Reg[2];
+ KSemaphore * sema = new KSemaphore(maxcount - count, maxcount, currentThread->m_owner);
+ u32 hand = 0;
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, sema);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateSemaphore (%08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, count, maxcount, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[0] = 0;
+ Reg[1] = hand;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateSemaphore (%08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, count, maxcount, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x16: //ReleaseSemaphore(s32* count, Handle semaphore, s32 releaseCount)
+ {
+ u32 hand = Reg[1];
+ s32 releaseCount = Reg[2];
+ KSemaphore* th = (KSemaphore*)*currentThread->m_owner->GetHandleTable()->GetHandle<KSemaphore>(hand);
+ if (th == NULL)
+ {
+
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u ReleaseSemaphore (%08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, releaseCount, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ u32 out_count = 0;
+ Reg[0] = th->ReleaseSemaphore(releaseCount, out_count);
+ Reg[1] = out_count;
+#ifdef SWILOG
+ LOG("Process %s thread %u ReleaseSemaphore (%08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, releaseCount, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x17: //CreateEvent(Handle* event, ResetType resettype)
+ {
+ u32 resettype = Reg[1];
+ KEvent* eve = new KEvent(0, resettype, currentThread->m_owner);
+ u32 hand = 0;
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, eve);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateEvent (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, resettype, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[0] = 0;
+ Reg[1] = hand;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateEvent (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, resettype, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x18: //SignalEvent(Handle event)
+ {
+ u32 hand = Reg[0];
+ KEvent* th = (KEvent*)*currentThread->m_owner->GetHandleTable()->GetHandle<KEvent>(hand);
+ if (th == NULL)
+ {
+
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u SignalEvent (%08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0]);
+#endif
+ return;
+ }
+ //free all and open
+ th->Triggerevent();
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u SignalEvent (%08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0]);
+#endif
+ return;
+ }
+ case 0x19: //ClearEvent(Handle event)
+ {
+ u32 hand = Reg[0];
+ KEvent* th = (KEvent*)*currentThread->m_owner->GetHandleTable()->GetHandle<KEvent>(hand);
+ if (th == NULL)
+ {
+
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u ClearEvent (%08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0]);
+#endif
+ return;
+ }
+ //free all and open
+ th->Clear();
+ Reg[0] = 0;
+#ifdef SWILOG
+#ifdef SHOWCLEAREVENT
+ LOG("Process %s thread %u ClearEvent (%08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0]);
+#endif
+#endif
+ return;
+ }
+ case 0x1A: //CreateTimer(Handle* timer, ResetType resettype)
+ {
+ u32 resettype = Reg[1];
+ KTimer* time = new KTimer(currentThread->m_owner, resettype);
+ u32 hand = 0;
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, time);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateTimer (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, resettype, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[0] = 0;
+ Reg[1] = hand;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateTimer (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, resettype, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x1B://SetTimer(Handle timer, s64 initial, s64 interval)
+ {
+ u32 hand = Reg[0];
+ s64 initial = ((u64)Reg[3] << 32) | Reg[2];
+ s64 interval = ((u64)Reg[4] << 32) | Reg[1];
+
+ KTimer* th = (KTimer*)*currentThread->m_owner->GetHandleTable()->GetHandle<KTimer>(hand);
+ if (th == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetTimer (%08x %I64d %I64d | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, initial, interval, Reg[0]);
+#endif
+ return;
+ }
+ Reg[0] = th->SetTimer(initial, interval);
+#ifdef SWILOG
+ LOG("Process %s thread %u SetTimer (%08x %I64d %I64d | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, initial, interval, Reg[0]);
+#endif
+ return;
+ }
+ case 0x1C://CancelTimer(Handle timer)
+ {
+ u32 hand = Reg[0];
+
+ KTimer* th = (KTimer*)*currentThread->m_owner->GetHandleTable()->GetHandle<KTimer>(hand);
+ if (th == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CancelTimer (%08x| %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0]);
+#endif
+ return;
+ }
+ th->Cancel();
+ Reg[0] = Success;
+#ifdef SWILOG
+ LOG("Process %s thread %u CancelTimer (%08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0]);
+#endif
+ return;
+ }
+ case 0x1E: //CreateMemoryBlock(Handle* memblock, u32 addr, u32 size, u32 mypermission, u32 otherpermission)
+ {
+ u32 otherperm = Reg[0];
+ u32 addr = Reg[1];
+ u32 size = Reg[2];
+ u32 myperm = Reg[3];
+ KSharedMemory* smem = new KSharedMemory(addr,size , myperm, otherperm, currentThread->m_owner);
+ u32 hand = 0;
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, smem);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateMemoryBlock (%08x %08x %08x %08x| %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, otherperm, addr, size, myperm, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[0] = 0;
+ Reg[1] = hand;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateMemoryBlock (%08x %08x %08x %08x| %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, otherperm, addr, size, myperm, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x1F: //MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherpermission)
+ {
+ u32 handle = Reg[0];
+ u32 addr = Reg[1];
+ u32 myperm = Reg[2];
+ u32 otherperm = Reg[3];
+ KSharedMemory* th = (KSharedMemory*)*currentThread->m_owner->GetHandleTable()->GetHandle<KSharedMemory>(handle);
+ if (th == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u MapMemoryBlock (%08x %08x %08x %08x| %08x) stub", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, addr, myperm, otherperm, Reg[0]);
+#endif
+ return;
+ }
+ Reg[0] = th->map(addr,myperm,otherperm,currentThread->m_owner);
+#ifdef SWILOG
+ LOG("Process %s thread %u MapMemoryBlock (%08x %08x %08x %08x| %08x) stub", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, addr, myperm, otherperm, Reg[0]);
+#endif
+ return;
+ }
+ case 0x21: //CreateAddressArbiter(Handle* arbiter)
+ {
+ KAddressArbiter* lim = new KAddressArbiter(currentThread->m_owner);
+ u32 hand = 0;
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, lim);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateAddressArbiter (| %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[0] = 0;
+ Reg[1] = hand;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateAddressArbiter (| %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x22: //ArbitrateAddress(Handle arbiter, u32 addr, ArbitrationType type, s32 value, s64 nanoseconds)
+ {
+ u32 arbiter = Reg[0];
+ u32 addr = Reg[1];
+ u32 type = Reg[2];
+ s32 value = Reg[3];
+ s64 time = Reg[4] | ((u64)Reg[3] << 32);
+
+ KAddressArbiter* th = (KAddressArbiter*)*currentThread->m_owner->GetHandleTable()->GetHandle<KAddressArbiter>(arbiter);
+ if (th == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u ArbitrateAddress (%08x %08x %08x %08x| %08x) stub", currentThread->m_owner->GetName(), currentThread->m_thread_id, arbiter, addr, type, value, Reg[0]);
+#endif
+ return;
+ }
+ if (addr & 3) {
+ Reg[0] = 0xD8E007F1;
+#ifdef SWILOG
+ LOG("Process %s thread %u ArbitrateAddress (%08x %08x %08x %08x| %08x) stub", currentThread->m_owner->GetName(), currentThread->m_thread_id, arbiter, addr, type, value, Reg[0]);
+#endif
+ return;
+ }
+ Reg[0] = th->ArbitrateAddress(addr,type, value, time, currentThread);
+
+
+#ifdef SWILOG
+ LOG("Process %s thread %u ArbitrateAddress (%08x %08x %08x %08x| %08x) stub", currentThread->m_owner->GetName(), currentThread->m_thread_id, arbiter, addr, type, value, Reg[0]);
+#endif
+ return;
+ }
+ case 0x23: //CloseHandle(Handle handle)
+ {
+ u32 handle = Reg[0];
+ s32 ret = currentThread->m_owner->GetHandleTable()->CloseHandle(handle); //TODO if this closes the Process the Process gets terminated?
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CloseHandle (%08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, Reg[0]);
+#endif
+ }
+ else
+ {
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u CloseHandle (%08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, Reg[0]);
+#endif
+ }
+ return;
+ }
+ case 0x24://WaitSynchronization1(Handle handle, s64 nanoseconds)
+ {
+ u32 handle = Reg[0];
+ u64 timeout = Reg[2] | ((u64)Reg[3] << 32);
+
+ KSynchronizationObject* th = (KSynchronizationObject*)*currentThread->m_owner->GetHandleTable()->GetHandle<KSynchronizationObject>(handle);
+ if (th == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u WaitSynchronization1 (%08x %"PRIx64" | % 08x % 08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, timeout, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+
+ KLinkedList<KSynchronizationObject> *list = new KLinkedList<KSynchronizationObject>();
+ list->AddItem(th);
+ currentThread->SyncStall(list, true);
+#ifdef SWILOG
+ LOG("Process %s thread %u WaitSynchronization1 (%08x %"PRIx64") stub", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, timeout);
+#endif
+ return;
+ }
+ case 0x25: //WaitSynchronizationN(s32* out, Handle* handles, s32 handlecount, bool waitAll, s64 nanoseconds)
+ {
+ u32 pointer = Reg[1];
+ s32 handleCount = Reg[2];
+ u32 waitall = Reg[3];
+ u64 timeout = Reg[0] | ((u64)Reg[4] << 32);
+
+ KLinkedList<KSynchronizationObject> *list = new KLinkedList<KSynchronizationObject>();
+ for (int i = 0; i < handleCount; i++)
+ {
+ u32 handle;
+ if (currentThread->m_owner->getMemoryMap()->Read32(pointer + i * 4, handle) != Success)
+ {
+ Reg[0] = -1;
+#ifdef SWILOG
+ LOG("Process %s thread %u WaitSynchronizationN (%08x %08x %08x | %08x %08x) stub + not 100 correct", currentThread->m_owner->GetName(), currentThread->m_thread_id, pointer, handleCount, waitall, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ KSynchronizationObject* th = (KSynchronizationObject*)*currentThread->m_owner->GetHandleTable()->GetHandle<KSynchronizationObject>(handle);
+#ifdef SWILOG
+ LOG("handle: %08x", handle);
+#endif
+ if (th) //todo send error message
+ list->AddItem(th);
+ }
+ currentThread->SyncStall(list, waitall);
+#ifdef SWILOG
+ LOG("Process %s thread %u WaitSynchronizationN (%08x %08x %08x | %08x %08x) stub + not 100 correct", currentThread->m_owner->GetName(), currentThread->m_thread_id, pointer, handleCount, waitall, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+
+ case 0x27: //DuplicateHandle(Handle* out, Handle original)
+ {
+ s32 ret;
+ KAutoObjectRef obj;
+ u32 original = Reg[1];
+ if (original == 0xFFFF8000)
+ {
+ obj.SetObject(currentThread);
+ }
+ else if (original == 0xFFFF8001)
+ {
+ obj.SetObject(currentThread->m_owner);
+ }
+ else
+ {
+ ret = currentThread->m_owner->GetHandleTable()->GetHandleObject(obj, original);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u DuplicateHandle (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, original, Reg[0],Reg[1]);
+#endif
+ return;
+ }
+ }
+ u32 hand = 0;
+ ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, *obj);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u DuplicateHandle (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, original, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[0] = 0;
+ Reg[1] = hand;
+#ifdef SWILOG
+ LOG("Process %s thread %u DuplicateHandle (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, original, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x28:
+ {
+ Reg[0] = currentThread->m_core->Getticks();
+ Reg[1] = currentThread->m_core->Getticks() >> 32;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetSystemTick ( | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, Reg[0], Reg[1]);
+#endif
+ }
+
+ case 0x2A: //GetSystemInfo(u64*out,Handle process, ProcessInfoType type)
+ {
+ u32 type = Reg[1];
+ u32 param = Reg[2];
+ switch (type)
+ {
+ case 26:
+ Reg[0] = 0;
+ Reg[1] = currentThread->m_owner->m_Kernel->m_numbFirmProcess;
+ Reg[2] = 0;
+ break;
+ default:
+ XDSERROR("unknown GetSystemInfo enum %u", type);
+ Reg[0] = 0;
+ break;
+ }
+#ifdef SWILOG
+ LOG("Process %s thread %u GetSystemInfo (%08x %08x | %08x %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, type, param, Reg[0], Reg[1], Reg[2]);
+#endif
+ return;
+ }
+
+ case 0x2B: //GetProcessInfo(u64*out,Handle process, ProcessInfoType type)
+ {
+ u32 hand = Reg[1];
+ u32 type = Reg[2];
+ switch (type)
+ {
+ case 2: //this gets the size of the data used by the Process
+ LOG("get size ? used ? -stub- enum %u", type);
+ Reg[0] = 0;
+ Reg[1] = 0x20000;
+ Reg[2] = 0;
+ break;
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ Reg[0] = SVCERROR_INVALID_ENUM_VALUE;
+ break;
+ case 20:
+ Reg[0] = 0;
+ Reg[1] = 0x20000000 - currentThread->m_owner->LINEAR_memory_virtual_address_userland;
+ Reg[2] = 0;
+ break;
+ default:
+ XDSERROR("unknown GetProcessInfo enum %u",type);
+ Reg[0] = SVCERROR_INVALID_ENUM_VALUE;
+ break;
+ }
+#ifdef SWILOG
+ LOG("Process %s thread %u GetProcessInfo (%08x %08x | %08x %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, type, Reg[0], Reg[1], Reg[2]);
+#endif
+ return;
+ }
+ case 0x2C: //GetThreadInfo(s64* out, Handle thread, ThreadInfoType type)
+ {
+ u32 hand = Reg[0];
+ u32 type = Reg[1];
+ KThread* th = (KThread*)*currentThread->m_owner->GetHandleTable()->GetHandle<KThread>(hand);
+ if (th == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetThreadInfo (%08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, type, Reg[0]);
+#endif
+ return;
+ }
+ Reg[0] = SVCERROR_INVALID_ENUM_VALUE;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetThreadInfo (%08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, type, Reg[0]);
+#endif
+ return;
+ }
+
+ case 0x2D: //ConnectToPort(Handle* out, const char* portName)
+ {
+ u32 pointer = Reg[1];
+ Reg[0] = 0xd88007fa; //obj was not found
+ char name[9];
+ for (int i = 0; i < 8; i++)
+ {
+ u8 data = 0;
+ s32 ret = currentThread->m_owner->getMemoryMap()->Read8(pointer + i, data);
+ if (ret != Success)
+ {
+ Reg[0] = -1;
+ //just a error
+ }
+ if (data == NULL)
+ {
+ name[i] = 0;
+ break;
+ }
+ name[i] = data;
+ }
+ name[8] = 0;
+
+ size_t name_len = strlen(name);
+ //search for the correct handle
+ KLinkedListNode<KPort> *temp = currentThread->m_owner->m_Kernel->m_Portlist.list;
+ while (temp)
+ {
+ if (memcmp(temp->data->m_Name, name, strlen(temp->data->m_Name)) == 0)
+ {
+ if(strlen(temp->data->m_Name) == name_len)
+ {
+ //found it
+ KClientSession* ses;
+ u32 hand;
+ if (temp->data->m_Client.connect(ses) == Success)
+ {
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, ses);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+ }
+ else
+ {
+ Reg[1] = hand;
+ Reg[0] = 0;
+ }
+ }
+ else
+ {
+ Reg[0] = -1; //todo the correct error
+ }
+ break;
+ }
+ }
+ temp = temp->next;
+ }
+#ifdef SWILOG
+ LOG("Process %s thread %u ConnectToPort (%08x | %08x %08x) stub", currentThread->m_owner->GetName(), currentThread->m_thread_id, pointer, Reg[0], Reg[1]);
+ for (int i = 0; i < 8; i++)
+ {
+ u8 data = 0;
+ currentThread->m_owner->getMemoryMap()->Read8(pointer + i, data);
+ if (data == NULL)
+ break;
+ printf("%c", data);
+ }
+ printf("\n");
+#endif
+ return;
+ }
+ case 0x32: //SendSyncRequest
+ {
+ u32 handle = Reg[0];
+ KClientSession* KSession = (KClientSession*)*currentThread->m_owner->GetHandleTable()->GetHandle<KClientSession>(handle);
+ if (KSession == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u SendSyncRequest (%08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, Reg[0]);
+#endif
+ return;
+ }
+ KLinkedList<KSynchronizationObject> *list = new KLinkedList<KSynchronizationObject>();
+ list->AddItem(KSession);
+ currentThread->SyncStall(list, true);
+ Reg[0] = Success;
+#ifdef SWILOG
+ LOG("Process %s thread %u SendSyncRequest (%08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, Reg[0]);
+#endif
+
+ return;
+ }
+ case 0x33: //OpenProcess(Handle* process, u32 processId)
+ {
+ u32 ID = Reg[1];
+
+ u32 outhand = 0;
+ u32 hand;
+
+ KLinkedListNode<KProcess>* proc = currentThread->m_owner->m_Kernel->m_processes.list;
+
+ KProcess * found = NULL;
+ while (proc)
+ {
+ if (proc->data->m_ProcessID == ID)
+ {
+ found = proc->data;
+ break;
+ }
+ proc = proc->next;
+ }
+ if (!proc)
+ {
+ Reg[0] = -1;
+#ifdef SWILOG
+ LOG("Process %s thread %u OpenProcess (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, ID, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, found);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u OpenProcess (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, ID, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[1] = hand;
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u OpenProcess (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, ID, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x35: //GetProcessId(u32* processId, Handle process)
+ {
+ u32 hand = Reg[1];
+ KProcess* th;
+ if (hand == 0xFFFF8001)
+ {
+ th = currentThread->m_owner;
+ }
+ else
+ {
+ th = (KProcess*)*currentThread->m_owner->GetHandleTable()->GetHandle<KProcess>(hand);
+ }
+ if (th == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetProcessId (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[1] = th->GetProcessID();
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetProcessId (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x37: //GetThreadId(u32* threadId, Handle thread)
+ {
+ u32 hand = Reg[1];
+ KThread* pr;
+ if (hand == 0xffff8000)
+ pr = currentThread;
+ else
+ pr = (KThread*)*currentThread->m_owner->GetHandleTable()->GetHandle<KThread>(hand);
+ if (pr == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetThreadId (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[0] = 0;
+ Reg[1] = pr->m_thread_id;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetThreadId (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x38: //GetResourceLimit(Handle* resourceLimit, Handle process)
+ {
+ u32 hand = Reg[0];
+
+ KProcess* pr = (KProcess*)*currentThread->m_owner->GetHandleTable()->GetHandle<KProcess>(hand);
+ if (pr == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimit (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0],Reg[1]);
+#endif
+ return;
+ }
+ u32 outhand = 0;
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, currentThread->m_owner->GetResourceLimit());
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimit (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[1] = outhand;
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimit (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, hand, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x39: //GetResourceLimitLimitValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount)
+ {
+ u32 values_ptr = Reg[0];
+ u32 handleResourceLimit = Reg[1];
+ u32 names_ptr = Reg[2];
+ u32 nameCount = Reg[3];
+
+ if (names_ptr == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_POINTER;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimitLimitValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+ if (nameCount <= 0)
+ {
+ Reg[0] = SVCERROR_OUT_OF_RANGE;
+ return;
+ }
+
+ KResourceLimit* pr = (KResourceLimit*)*currentThread->m_owner->GetHandleTable()->GetHandle<KResourceLimit>(handleResourceLimit);
+ if (pr == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimitLimitValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+
+
+ for (u32 i = 0; i < nameCount; i++)
+ {
+ u32 data;
+
+ s32 ret = currentThread->m_owner->getMemoryMap()->Read32(names_ptr + i * 4, data); //read from user mode
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_INVALID_PARAMS;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimitLimitValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+ if (data >= 0xA)
+ {
+ Reg[0] = SVCERROR_OUT_OF_RANGE;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimitLimitValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+
+ s64 out_data = pr->GetMaxValue(data); //gets data from KResourceLimitobj + 0x8
+#ifdef SWILOG
+ LOG("%08x -> %08x", data, (u32)out_data);
+#endif
+ currentThread->m_owner->getMemoryMap()->Write64(names_ptr + i * 8, out_data);
+ }
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimitLimitValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ Reg[0] = 0;
+ return;
+ }
+ case 0x3A: //GetResourceLimitCurrentValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount)
+ {
+ u32 values_ptr = Reg[0];
+ u32 handleResourceLimit = Reg[1];
+ u32 names_ptr = Reg[2];
+ u32 nameCount = Reg[3];
+
+ if (names_ptr == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_POINTER;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimitCurrentValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+ if (nameCount <= 0)
+ {
+ Reg[0] = SVCERROR_OUT_OF_RANGE;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimitCurrentValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+
+ KResourceLimit* pr = (KResourceLimit*)*currentThread->m_owner->GetHandleTable()->GetHandle<KResourceLimit>(handleResourceLimit);
+ if (pr == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimitCurrentValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+
+
+ for (u32 i = 0; i < nameCount; i++)
+ {
+ u32 data;
+
+ s32 ret = currentThread->m_owner->getMemoryMap()->Read32(names_ptr + i * 4, data); //read from user mode
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_INVALID_PARAMS;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimitCurrentValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+ if (data >= 0xA)
+ {
+ Reg[0] = SVCERROR_OUT_OF_RANGE;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimitCurrentValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+
+ s64 out_data = pr->GetCurrentValue(data);
+#ifdef SWILOG
+ LOG("%08x -> %08x", data, (u32)out_data);
+#endif
+ currentThread->m_owner->getMemoryMap()->Write64(names_ptr + i * 8, out_data);
+ }
+
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetResourceLimitCurrentValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+ case 0x3D:
+ {
+ LOG("Process %s thread %u DEBUG output:", currentThread->m_owner->GetName(), currentThread->m_thread_id);
+ for (size_t sz = Reg[1]; sz > 0; sz--)
+ {
+ u8 data;
+ if (currentThread->m_owner->getMemoryMap()->Read8(Reg[0]++, data))
+ break;
+ putchar(data);
+ }
+ LOG("");
+ Reg[0] = 0; //there are some cases where this errors but we don't emulate them
+ return;
+ }
+ case 0x47: //CreatePort(Handle* portServer, Handle* portClient, const char* name, s32 maxSessions)
+ {
+ u32 pointer = Reg[2];
+ u32 maxhandle = Reg[3];
+ Reg[0] = 0xd88007fa; //obj was not found
+
+ char name[9];
+ for (int i = 0; i < 8; i++)
+ {
+ u8 data = 0;
+ s32 ret = currentThread->m_owner->getMemoryMap()->Read8(pointer + i, data);
+ if (ret != Success)
+ {
+ Reg[0] = -1;
+ //just a error
+ }
+ if (data == NULL)
+ {
+ name[i] = 0;
+ break;
+ }
+ name[i] = data;
+ }
+ name[8] = 0;
+ KPort *port = new KPort(name, maxhandle);
+ currentThread->m_owner->m_Kernel->m_Portlist.AddItem(port);
+
+ u32 hand1 = 0;
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand1, &port->m_Server);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreatePort (%08x %08x | %08x %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, pointer, maxhandle, Reg[0], Reg[1], Reg[2]);
+ for (int i = 0; i < 8; i++)
+ {
+ u8 data = 0;
+ currentThread->m_owner->getMemoryMap()->Read8(pointer + i, data);
+ if (data == NULL)
+ break;
+ printf("%c", data);
+ }
+ printf("\n");
+#endif
+ return;
+ }
+ u32 hand2 = 0;
+ ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand2, &port->m_Client);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreatePort (%08x %08x | %08x %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, pointer, maxhandle, Reg[0], Reg[1], Reg[2]);
+ for (int i = 0; i < 8; i++)
+ {
+ u8 data = 0;
+ currentThread->m_owner->getMemoryMap()->Read8(pointer + i, data);
+ if (data == NULL)
+ break;
+ printf("%c", data);
+ }
+ printf("\n");
+#endif
+ return;
+ }
+ Reg[0] = 0;
+ Reg[1] = hand1;
+ Reg[2] = hand2;
+
+#ifdef SWILOG
+ LOG("Process %s thread %u CreatePort (%08x %08x | %08x %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, pointer, maxhandle, Reg[0], Reg[1], Reg[2]);
+ for (int i = 0; i < 8; i++)
+ {
+ u8 data = 0;
+ currentThread->m_owner->getMemoryMap()->Read8(pointer + i, data);
+ if (data == NULL)
+ break;
+ printf("%c", data);
+ }
+ printf("\n");
+#endif
+ return;
+ }
+
+ case 0x48: //CreateSessionToPort(Handle* session, Handle port)
+ {
+ u32 handle = Reg[1];
+ KClientPort* SPort = (KClientPort*)*currentThread->m_owner->GetHandleTable()->GetHandle<KClientPort>(handle);
+ if (SPort == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateSessionToPort (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+
+ //found it
+ KClientSession* ses;
+ u32 hand;
+ if (SPort->connect(ses) == Success)
+ {
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, ses);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+ }
+ else
+ {
+ Reg[1] = hand;
+ Reg[0] = 0;
+ }
+ }
+ else
+ {
+ Reg[0] = -1; //todo the correct error
+ }
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateSessionToPort (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x49: //CreateSession(Handle* sessionServer, Handle* sessionClient)
+ {
+ KSession* sesi = new KSession();
+ u32 hand1,hand2;
+
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand1, &sesi->m_Server);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateSession (| %08x %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, Reg[0], Reg[1], Reg[2]);
+#endif
+ return;
+ }
+ ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand2, &sesi->m_Client);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateSession (| %08x %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, Reg[0], Reg[1], Reg[2]);
+#endif
+ return;
+ }
+
+ Reg[0] = 0;
+ Reg[1] = hand1;
+ Reg[2] = hand2;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateSession (| %08x %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, Reg[0], Reg[1], Reg[2]);
+#endif
+ return;
+ }
+ case 0x4A: //AcceptSession(Handle* session, Handle port)
+ {
+ u32 handle = Reg[1];
+ KServerPort* SPort = (KServerPort*)*currentThread->m_owner->GetHandleTable()->GetHandle<KServerPort>(handle);
+ if (SPort == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u AcceptSession (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ u32 hand = 0;
+ KServerSession* ses = SPort->AcceptSesion();
+
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, ses);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u AcceptSession (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+
+ Reg[0] = 0;
+ Reg[1] = hand;
+#ifdef SWILOG
+ LOG("Process %s thread %u AcceptSession (%08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x4F: //ReplyAndReceive(s32* index, Handle* handles, s32 handleCount, Handle replyTarget)
+ {
+ u32 pointer = Reg[1];
+ s32 handleCount = Reg[2];
+ u32 replyTarget = Reg[3];
+
+ if (replyTarget != NULL)
+ {
+ KServerSession* SSession = (KServerSession*)*currentThread->m_owner->GetHandleTable()->GetHandle<KServerSession>(replyTarget);
+ if (SSession == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u ReplyAndReceive (%08x %08x %08x | %08x %08x) stub + not 100 correct", currentThread->m_owner->GetName(), currentThread->m_thread_id, pointer, handleCount, replyTarget, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ s32 ret = SSession->reply(currentThread);
+ /*if (ret != Success)
+ {
+ Reg[0] = ret;
+#ifdef SWILOG
+ LOG("Process %s thread %u ReplyAndReceive (%08x %08x %08x | %08x %08x) stub + not 100 correct", currentThread->m_owner->GetName(), currentThread->m_thread_id, pointer, handleCount, replyTarget, Reg[0], Reg[1]);
+#endif
+ return;
+ }*/ //don't do anything here
+ }
+
+ KLinkedList<KSynchronizationObject> *list = new KLinkedList<KSynchronizationObject>();
+ for (int i = 0; i < handleCount; i++)
+ {
+ u32 handle;
+ if (currentThread->m_owner->getMemoryMap()->Read32(pointer + i * 4, handle) != Success)
+ {
+ Reg[0] = -1;
+#ifdef SWILOG
+ LOG("Process %s thread %u ReplyAndReceive (%08x %08x %08x | %08x %08x) stub + not 100 correct", currentThread->m_owner->GetName(), currentThread->m_thread_id, pointer, handleCount, replyTarget, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ KSynchronizationObject* th = (KSynchronizationObject*)*currentThread->m_owner->GetHandleTable()->GetHandle<KSynchronizationObject>(handle);
+#ifdef SWILOG
+ LOG("handle: %08x", handle);
+#endif
+ if (th) //todo send error message
+ list->AddItem(th);
+ }
+ currentThread->SyncStall(list, false);
+#ifdef SWILOG
+ LOG("Process %s thread %u ReplyAndReceive (%08x %08x %08x | %08x %08x) stub + not 100 correct", currentThread->m_owner->GetName(), currentThread->m_thread_id, pointer, handleCount, replyTarget, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x50: //BindInterrupt(Interrupt name, Handle syncObject, s32 priority, bool isManualClear)
+ {
+ s32 name = Reg[0];
+ Handle syncObject = Reg[1];
+ s32 priority = Reg[2];
+ u32 isManualClear = Reg[3];
+
+ KSynchronizationObject* obj = (KSynchronizationObject*)*currentThread->m_owner->GetHandleTable()->GetHandle<KSynchronizationObject>(syncObject);
+ if (obj == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u BindInterrupt (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, name, syncObject, priority, isManualClear, Reg[0]);
+#endif
+ return;
+ }
+ Reg[0] = currentThread->m_owner->m_Kernel->RegisterInterrupt(name, obj, priority, isManualClear);
+#ifdef SWILOG
+ LOG("Process %s thread %u BindInterrupt (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, name, syncObject, priority, isManualClear, Reg[0]);
+#endif
+ return;
+ }
+ case 0x51: //UnbindInterrupt(Interrupt name, Handle syncObject)
+ {
+ s32 name = Reg[0];
+ Handle syncObject = Reg[1];
+
+ KSynchronizationObject* obj = (KSynchronizationObject*)*currentThread->m_owner->GetHandleTable()->GetHandle<KSynchronizationObject>(syncObject);
+ if (obj == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u UnbindInterrupt (%08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, name, syncObject, Reg[0]);
+#endif
+ return;
+ }
+ Reg[0] = currentThread->m_owner->m_Kernel->UnRegisterInterrupt(name, obj);
+#ifdef SWILOG
+ LOG("Process %s thread %u UnbindInterrupt (%08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, name, syncObject, Reg[0]);
+#endif
+ return;
+ }
+ case 0x53: //StoreProcessDataCache
+ {
+ u32 addr = Reg[1];
+ u32 size = Reg[2];
+ Reg[0] = 0;
+
+#ifdef SWILOG
+ LOG("Process %s thread %u StoreProcessDataCache (%08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, addr, size, Reg[0]);
+#endif
+ return;
+ }
+ case 0x54: //FlushProcessDataCache
+ {
+ u32 addr = Reg[1];
+ u32 size = Reg[2];
+ Reg[0] = 0;
+
+#ifdef SWILOG
+ LOG("Process %s thread %u FlushProcessDataCache (%08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, addr, size, Reg[0]);
+#endif
+ return;
+ }
+ case 0x55://StartInterProcessDma(Handle* dma, Handle dstProcess, void* dst, Handle srcProcess, const void* src, u32 size, const DmaConfig& config)
+ {
+ u32 srcAddress = Reg[0];
+ Handle srcProcessID = Reg[1];
+ u32 dstAddress = Reg[2];
+ Handle dstProcessID = Reg[3];
+ u32 size = Reg[4];
+ u32 config = Reg[5];
+
+ KProcess * dstProcess;
+ KProcess * srcProcess;
+ if (dstProcessID == 0xFFFF8001)
+ {
+ dstProcess = currentThread->m_owner;
+ }
+ else
+ {
+ dstProcess = (KProcess*)*currentThread->m_owner->GetHandleTable()->GetHandle<KProcess>(dstProcessID);
+ }
+
+ if (srcProcessID == 0xFFFF8001)
+ {
+ srcProcess = currentThread->m_owner;
+ }
+ else
+ {
+ srcProcess = (KProcess*)*currentThread->m_owner->GetHandleTable()->GetHandle<KProcess>(srcProcessID);
+ }
+
+ struct DmaConfig dmaConfig;
+ currentThread->m_owner->getMemoryMap()->ReadN(config, (u8*)&dmaConfig, sizeof(struct DmaConfig));
+
+ if (!dstProcess || !srcProcess)
+ {
+ Reg[0] = -1;
+#ifdef SWILOG
+ LOG("Process %s thread %u StartInterProcessDma failed (%08x %08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, dstProcessID, dstAddress, srcProcessID, srcAddress, size, config);
+#endif
+ return;
+ }
+
+ //TODO: Init with params from DmaConfig data
+ KDmaObject* lim = new KDmaObject(0,2,currentThread->m_owner);
+ u32 dma_hand = 0;
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(dma_hand, lim);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u StartInterProcessDma (%s %08x %s %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, dstProcess->GetName(), dstAddress, srcProcess->GetName(), srcAddress, size, config);
+#endif
+ return;
+ }
+
+#ifdef SWILOG
+ LOG("Process %s thread %u StartInterProcessDma (%s %08x %s %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, dstProcess->GetName(), dstAddress, srcProcess->GetName(), srcAddress, size, config);
+ LOG("DmaConfig:");
+ LOG(" channel_sel: %d", dmaConfig.channel_sel);
+ LOG(" endian_swap_size: %d (0x%02X)", dmaConfig.endian_swap_size, dmaConfig.endian_swap_size);
+ LOG(" flags: %d (0x%02X)", dmaConfig.flags, dmaConfig.flags);
+ LOG(" padding: %d (0x%02X)", dmaConfig.padding, dmaConfig.padding);
+ LOG(" SubDmaConfig Destination:");
+ LOG(" peripheral_id: %d (0x%02X)", dmaConfig.dst_cfg.peripheral_id, dmaConfig.dst_cfg.peripheral_id);
+ LOG(" type: %d (0x%02X)", dmaConfig.dst_cfg.type, dmaConfig.dst_cfg.type);
+ LOG(" unk3: %d (0x%04X)", dmaConfig.dst_cfg.unk3, dmaConfig.dst_cfg.unk3);
+ LOG(" transfer_size: %d (0x%04X)", dmaConfig.dst_cfg.transfer_size, dmaConfig.dst_cfg.transfer_size);
+ LOG(" unk4: %d (0x%04X)", dmaConfig.dst_cfg.unk4, dmaConfig.dst_cfg.unk4);
+ LOG(" transfer_stride: %d (0x%04X)", dmaConfig.dst_cfg.transfer_stride, dmaConfig.dst_cfg.transfer_stride);
+ LOG(" SubDmaConfig Source:");
+ LOG(" peripheral_id: %d (0x%02X)", dmaConfig.src_cfg.peripheral_id, dmaConfig.src_cfg.peripheral_id);
+ LOG(" type: %d (0x%02X)", dmaConfig.src_cfg.type, dmaConfig.src_cfg.type);
+ LOG(" unk3: %d (0x%04X)", dmaConfig.src_cfg.unk3, dmaConfig.src_cfg.unk3);
+ LOG(" transfer_size: %d (0x%04X)", dmaConfig.src_cfg.transfer_size, dmaConfig.src_cfg.transfer_size);
+ LOG(" unk4: %d (0x%04X)", dmaConfig.src_cfg.unk4, dmaConfig.src_cfg.unk4);
+ LOG(" transfer_stride: %d (0x%04X)", dmaConfig.src_cfg.transfer_stride, dmaConfig.src_cfg.transfer_stride);
+
+#endif
+ for (u32 i = 0; i < size;)
+ {
+ u8 val8;
+ u16 val16;
+ u32 val32;
+ switch (dmaConfig.dst_cfg.type)
+ {
+ case 1:
+ srcProcess->getMemoryMap()->Read8(srcAddress + i, val8);
+ dstProcess->getMemoryMap()->Write8(dstAddress + (i % dmaConfig.dst_cfg.transfer_stride), val8);
+ i++;
+ break;
+ case 2:
+ srcProcess->getMemoryMap()->Read16(srcAddress + i, val16);
+ dstProcess->getMemoryMap()->Write16(dstAddress + (i % dmaConfig.dst_cfg.transfer_stride), val16);
+ i+=2;
+ break;
+ case 4:
+ srcProcess->getMemoryMap()->Read32(srcAddress + i, val32);
+ dstProcess->getMemoryMap()->Write32(dstAddress + (i % dmaConfig.dst_cfg.transfer_stride), val32);
+ i += 4;
+ break;
+ default:
+ LOG("error dmaConfig.dst_cfg.type 0x%02X not supported yet", dmaConfig.dst_cfg.type);
+ i++;
+ break;
+ }
+ }
+
+ Reg[1] = dma_hand;
+ Reg[0] = 0;
+
+ return;
+ }
+ case 0x56: //StopDma
+ {
+ u32 handle = Reg[0];
+
+ KDmaObject *dmaObject = (KDmaObject*)*currentThread->m_owner->GetHandleTable()->GetHandle<KDmaObject>(handle);
+
+ if(!dmaObject)
+ {
+ Reg[0] = 0xD8E007F7;
+#ifdef SWILOG
+ LOG("Process %s thread %u StopDma failed (%08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle);
+#endif
+ return;
+ }
+
+ Reg[0] = 0;
+
+#ifdef SWILOG
+ LOG("Process %s thread %u StopDma (%08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle);
+#endif
+ return;
+ }
+ case 0x57: //GetDmaState
+ {
+ u32 handle = Reg[1];
+
+ KDmaObject *dmaObject = (KDmaObject*)*currentThread->m_owner->GetHandleTable()->GetHandle<KDmaObject>(handle);
+
+ if (!dmaObject)
+ {
+ Reg[0] = 0xD8E007F7;
+#ifdef SWILOG
+ LOG("Process %s thread %u GetDmaState failed (%08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle);
+#endif
+ return;
+ }
+
+ Reg[0] = 0;
+ Reg[1] = dmaObject->GetState();
+
+#ifdef SWILOG
+ LOG("Process %s thread %u GetDmaState (%08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handle, Reg[1]);
+#endif
+ return;
+ }
+ case 0x73: //CreateCodeSet(Handle* handle_out, struct CodeSetInfo, u32 code_ptr, u32 ro_ptr, u32 data_ptr)
+ {
+ u32 CodeSetInfo = Reg[1];
+ u32 code_ptr = Reg[2];
+ u32 ro_ptr = Reg[3];
+ u32 data_ptr = Reg[0];
+ u32 hand = 0;
+
+ struct CodeSetInfo cs;
+ currentThread->m_owner->getMemoryMap()->ReadN(CodeSetInfo, (u8*)&cs, sizeof(struct CodeSetInfo));
+
+ u8* codebuffer = (u8*)malloc(cs.TEXTSize*PAGE_SIZE); //todo check if the alloc worked
+ u8* robuffer = (u8*)malloc(cs.ROSize * PAGE_SIZE);
+ u8* databuffer = (u8*)malloc(cs.RWSize * PAGE_SIZE);
+
+ currentThread->m_owner->getMemoryMap()->ReadN(code_ptr, codebuffer, cs.TEXTSize*PAGE_SIZE);
+ currentThread->m_owner->getMemoryMap()->ReadN(ro_ptr, robuffer, cs.ROSize * PAGE_SIZE);
+ currentThread->m_owner->getMemoryMap()->ReadN(data_ptr, databuffer, cs.RWSize * PAGE_SIZE);
+
+ char othername[9];
+ strncpy(othername, (char*)cs.Name, 8);
+ KCodeSet* CSet = new KCodeSet(codebuffer, cs.PagesTEXT, robuffer, cs.PagesRO, databuffer, cs.RWSize,cs.PagesRW - cs.RWSize, Read64(cs.TitleID), othername); //todo check if the buffer are big enough
+
+ if (currentThread->m_owner->getMemoryMap()->RemovePages(code_ptr, cs.TEXTSize*PAGE_SIZE) != 0)
+ {
+ Reg[0] = 0xE0A01BF5;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateCodeSet (%08x %08x %08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, CodeSetInfo, code_ptr, ro_ptr, data_ptr, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ if (currentThread->m_owner->getMemoryMap()->RemovePages(ro_ptr, cs.ROSize*PAGE_SIZE) != 0)
+ {
+ Reg[0] = 0xE0A01BF5;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateCodeSet (%08x %08x %08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, CodeSetInfo, code_ptr, ro_ptr, data_ptr, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ if (currentThread->m_owner->getMemoryMap()->RemovePages(data_ptr, cs.RWSize*PAGE_SIZE) != 0)
+ {
+ Reg[0] = 0xE0A01BF5;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateCodeSet (%08x %08x %08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, CodeSetInfo, code_ptr, ro_ptr, data_ptr, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+
+ free(codebuffer);
+ free(robuffer);
+ free(databuffer);
+
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, CSet);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateCodeSet (%08x %08x %08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, CodeSetInfo, code_ptr, ro_ptr, data_ptr, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+
+ Reg[0] = 0;
+ Reg[1] = hand;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateCodeSet (%08x %08x %08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, CodeSetInfo, code_ptr, ro_ptr, data_ptr, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x75: //CreateProcess(Handle* handle_out, Handle codeset_handle, u32 arm11kernelcaps_ptr, u32 arm11kernelcaps_num)
+ {
+ u32 arm11kernelcapsfild[0x80];
+ u32 codeset_handle = Reg[1];
+ u32 arm11kernelcaps_ptr = Reg[2];
+ u32 arm11kernelcaps_num = Reg[3];
+ u32 hand = 0;
+
+ KCodeSet* Codeset = (KCodeSet*)*currentThread->m_owner->GetHandleTable()->GetHandle<KCodeSet>(codeset_handle);
+ if (Codeset == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateProcess (%08x %08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, codeset_handle, arm11kernelcaps_ptr, arm11kernelcaps_num, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ //todo this is a workaround
+ currentThread->m_owner->getMemoryMap()->ReadN(arm11kernelcaps_ptr, (u8*)arm11kernelcapsfild, sizeof(arm11kernelcapsfild));
+ KProcess* Pro = new KProcess(Codeset, arm11kernelcaps_num, arm11kernelcapsfild, currentThread->m_owner->m_Kernel, false);
+
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, Pro);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateProcess (%08x %08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, codeset_handle, arm11kernelcaps_ptr, arm11kernelcaps_num, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+
+ Reg[0] = 0;
+ Reg[1] = hand;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateProcess (%08x %08x %08x | %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, codeset_handle, arm11kernelcaps_ptr, arm11kernelcaps_num, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+
+ case 0x77: //SetProcessResourceLimits(Handle KProcess, Handle KResourceLimit)
+ {
+ u32 handleResourceLimit = Reg[1];
+ u32 handleProcess = Reg[0];
+
+ KResourceLimit* lim = (KResourceLimit*)*currentThread->m_owner->GetHandleTable()->GetHandle<KResourceLimit>(handleResourceLimit);
+ if (lim == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetProcessResourceLimits (%08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handleResourceLimit, handleProcess, Reg[0]);
+#endif
+ return;
+ }
+
+ KProcess* pr = (KProcess*)*currentThread->m_owner->GetHandleTable()->GetHandle<KProcess>(handleProcess);
+ if (pr == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetProcessResourceLimits (%08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handleResourceLimit, handleProcess, Reg[0]);
+#endif
+ return;
+ }
+ pr->SetResourceLimit(lim);
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetProcessResourceLimits (%08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, handleResourceLimit, handleProcess, Reg[0]);
+#endif
+ return;
+ }
+ case 0x78: //CreateResourceLimit(Handle *KResourceLimit)
+ {
+ KResourceLimit* lim = new KResourceLimit();
+ u32 hand = 0;
+ s32 ret = currentThread->m_owner->GetHandleTable()->CreateHandle(hand, lim);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_CREATE_HANLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateResourceLimit (| %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ Reg[0] = 0;
+ Reg[1] = hand;
+#ifdef SWILOG
+ LOG("Process %s thread %u CreateResourceLimit (| %08x %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, Reg[0], Reg[1]);
+#endif
+ return;
+ }
+ case 0x79: //SetResourceLimitValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount)
+ {
+ u32 handleResourceLimit = Reg[0];
+ u32 names_ptr = Reg[1];
+ u32 values_ptr = Reg[2];
+ u32 nameCount = Reg[3];
+
+ if (names_ptr == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_POINTER;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetResourceLimitValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+ if (nameCount <= 0)
+ {
+ Reg[0] = SVCERROR_OUT_OF_RANGE;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetResourceLimitValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+
+ KResourceLimit* lim = (KResourceLimit*)*currentThread->m_owner->GetHandleTable()->GetHandle<KResourceLimit>(handleResourceLimit);
+ if (lim == NULL)
+ {
+ Reg[0] = SVCERROR_INVALID_HANDLE;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetResourceLimitValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+
+
+ for (u32 i = 0; i < nameCount; i++)
+ {
+ u32 data;
+
+ s32 ret = currentThread->m_owner->getMemoryMap()->Read32(names_ptr + i * 4, data); //read from user mode
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_INVALID_PARAMS;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetResourceLimitValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+ if (data >= 0xA)
+ {
+ Reg[0] = SVCERROR_OUT_OF_RANGE;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetResourceLimitValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+ u64 value;
+ ret = currentThread->m_owner->getMemoryMap()->Read64(values_ptr + i * 8, value);
+ if (ret != Success)
+ {
+ Reg[0] = SVCERROR_INVALID_PARAMS;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetResourceLimitValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+#ifdef SWILOG
+ LOG("%08x -> %"PRIx64, data, value);
+#endif
+ lim->SetMaxValue(data, value);
+
+ }
+
+ Reg[0] = 0;
+#ifdef SWILOG
+ LOG("Process %s thread %u SetResourceLimitValues (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, values_ptr, handleResourceLimit, names_ptr, nameCount, Reg[0]);
+#endif
+ return;
+ }
+ case 0x7C: // KernelSetState(unsigned int Type, unsigned int Param0, unsigned int Param1, unsigned int Param2)
+ {
+ u32 type = Reg[0];
+ u32 param0 = Reg[1];
+ u32 param1 = Reg[2];
+ u32 param2 = Reg[3];
+ if (type == 3 && param0 == 0) //map the firm laod param
+ {
+
+ MemChunk* chunk = (MemChunk*)malloc(sizeof(MemChunk));
+
+ chunk->size = 0x1000;
+ chunk->data = currentThread->m_owner->m_Kernel->m_FIRM_Launch_Parameters;
+
+ currentThread->m_owner->getMemoryMap()->AddPages(param1, 0x1000, chunk->data, chunk, PERMISSION_RW, MEMTYPE_MIRROR, NULL); //todo is type MEMTYPE_MIRROR realy correct don't think so
+ Reg[0] = 0;
+ }
+ else
+ {
+ LOG("Process %s thread %u KernelSetState unknown (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, type, param0, param1, param2, Reg[0]);
+ Reg[0] = -1;
+ }
+#ifdef SWILOG
+ LOG("Process %s thread %u KernelSetState (%08x %08x %08x %08x | %08x)", currentThread->m_owner->GetName(), currentThread->m_thread_id, type, param0, param1, param2, Reg[0]);
+#endif
+ return;
+ }
+ default:
+ XDSERROR("Process %s thread %u tryed to call syscall %02X but that syscall is not implemented", currentThread->m_owner->GetName(), currentThread->m_thread_id, swi);
+
+ Reg[0] = 0xF8C007F4;//TODO the 3DS would terminate the Process if it is realy unknown but some are just stubs
+ if (swi == 0x3C)
+ {
+ fflush(stdout);
+ while (1);
+ }
+ break;
+ }
+
+}
+
+u32 ControlMemory_swi(u32* address, u32 addr0, u32 addr1, u32 size, u32 op, u32 permissions, KThread * currentThread)
+{
+ if (addr0 & 0xFFF)
+ return SVCERROR_ALIGN_ADDR;
+ if (addr1 & 0xFFF)
+ return SVCERROR_ALIGN_ADDR;
+ if (size & 0xFFF)
+ return SVCERROR_INVALID_SIZE;
+
+ if (op & 0x10000) { // FFF680A4 //this was if(op == 0x10003) in Version < 5.0
+ if (addr0 == 0) { // FFF680C4
+ if (addr1 != 0)
+ return SVCERROR_INVALID_PARAMS;
+ }
+ else if (size == 0) { // FFF680D0
+ if (addr0 < 0x14000000)
+ return SVCERROR_INVALID_PARAMS;
+ if ((addr0 + size) >= 0x1C000000)
+ return SVCERROR_INVALID_PARAMS;
+ if (addr1 != 0)
+ return SVCERROR_INVALID_PARAMS;
+ }
+ else {
+ if (addr0 < 0x14000000)
+ return SVCERROR_INVALID_PARAMS;
+ if (addr0 >= 0x1C000000)
+ return SVCERROR_INVALID_PARAMS;
+ if (addr1 != 0)
+ return SVCERROR_INVALID_PARAMS;
+ }
+ }
+ else if (op == 1) {
+ if (size == 0) { // FFF68110
+ if (addr0 < 0x08000000) // FFF68130
+ return SVCERROR_INVALID_PARAMS;
+ if (addr0 >= 0x1C000000)
+ return SVCERROR_INVALID_PARAMS;
+ }
+ else {
+ if (addr0 < 0x08000000)
+ return SVCERROR_INVALID_PARAMS;
+ if ((addr0 + size) >= 0x1C000000)
+ return SVCERROR_INVALID_PARAMS;
+ }
+ }
+ else {
+ if (size == 0) { // FFF68148
+ if (addr0 < 0x08000000)
+ return SVCERROR_INVALID_PARAMS;
+ if (addr0 >= 0x14000000)
+ return SVCERROR_INVALID_PARAMS;
+ }
+ else {
+ if (addr0 < 0x08000000)
+ return SVCERROR_INVALID_PARAMS;
+ if ((addr0 + size) >= 0x14000000)
+ return SVCERROR_INVALID_PARAMS;
+ }
+
+ if (op == 4 || op == 5) { // FFF680E8
+ if (size == 0) {
+ if (addr1 < 0x100000) // FFF681CC
+ return SVCERROR_INVALID_PARAMS;
+ if (addr1 >= 0x14000000)
+ return SVCERROR_INVALID_PARAMS;
+ }
+ if (addr1 < 0x100000)
+ return SVCERROR_INVALID_PARAMS;
+
+ if ((addr1 + size) >= 0x14000000)
+ return SVCERROR_INVALID_PARAMS;
+ }
+ }
+
+ switch (op & 0xff) {
+ case 1:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ break;
+ default:
+ return SVCERROR_INVALID_OPERATION;
+ }
+
+
+ if (((op & 0xFF) != 1) && (permissions & 0xFF) > 0x4) //NONE R-- W-- RW- dose not matter for free
+ {
+ return SVCERROR_INVALID_OPERATION; //Invalid combination
+ }
+ op = op & 0xFFFFFF;
+ if (currentThread->m_owner->GetProcessID() != 1) //loader is privileged to use all the mem regardless of the ResourceLimmit
+ {
+ if ((op & 0xFF) == 3)
+ {
+ bool ret = currentThread->m_owner->GetResourceLimit()->UseResource(RESOURCE_COMMIT, size);
+ if (!ret)
+ return SVCERROR_RESOURCE_LIMIT;
+ }
+ if (op & 0xF00)
+ {
+ op = (op & ~0xF00) | (currentThread->m_owner->m_exheader_flags & 0xF00); //force to be in the correct arear
+ }
+ }
+
+ s32 ret = currentThread->m_owner->getMemoryMap()->ControlMemory(address, addr0, addr1, size, op, permissions);
+ if (ret == Success && (op & 0xFF) == 1)
+ {
+ //free the Resource
+ currentThread->m_owner->GetResourceLimit()->FreeResource(RESOURCE_COMMIT, size);
+ }
+ return ret;
+}
--- /dev/null
+#include "Kernel.h"
+
+KSynchronizationObject::KSynchronizationObject() : waiting(), m_killed(false)
+{
+
+}
+
+void KSynchronizationObject::SynFreeAll(u32 errorCode) {
+
+ while (waiting.list != NULL)
+ {
+ KThread* thr = waiting.list->data;
+ thr->SyncFree(errorCode, this);
+ //waiting.RemoveItem(waiting.list);
+ }
+}
+
+void KSynchronizationObject::SynFree(u32 errorCode, KThread* thread)
+{
+ KLinkedListNode<KThread>* current = waiting.list;
+ while (current != NULL)
+ {
+ if (current->data == thread)
+ {
+ KThread* thr = current->data;
+ waiting.RemoveItem(current);
+ thr->SyncFree(errorCode, this);
+ }
+ current = current->next;
+ }
+}
+void KSynchronizationObject::SynRemove(KThread* thread)
+{
+ KLinkedListNode<KThread>* current = waiting.list;
+ while (current != NULL)
+ {
+ if (current->data == thread)
+ {
+ KThread* thr = waiting.list->data;
+ waiting.RemoveItem(current);
+ }
+ current = current->next;
+ }
+}
+
+bool KSynchronizationObject::Syn(KThread* thread, u32 &error)
+{
+ waiting.AddItem(thread);
+ if (Synchronization(thread, error) == true)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+KThread* KSynchronizationObject::SynGetNextPrio() //the threads have 2 prioritys don't know what the different is we only use one here so TODO change that also what happens when 2 have the same prio
+{
+ KThread* found = NULL;
+ KLinkedListNode<KThread>* current = waiting.list;
+ while (current)
+ {
+ KThread* thr = waiting.list->data;
+ if (found)
+ {
+ if (found->m_thread_prio < thr->m_thread_prio)
+ found = thr;
+ }
+ else
+ {
+ found = thr;
+ }
+ current = current->next;
+ }
+ return found;
+}
+
+bool KSynchronizationObject::IsInstanceOf(ClassName name) {
+ if (name == KSynchronizationObject_Class)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+
+KThread::~KThread()
+{
+
+}
+
+KThread::KThread(s32 core, KProcess *owner) : m_running(true)
+{
+ m_owner = owner;
+ memset(&m_context, 0, sizeof(m_context));
+ m_context.mode = RESUME;
+
+ m_thread_id = owner->m_Kernel->GetNextThreadID();
+
+ Threadwaitlist = NULL;
+ m_corenumb = core;
+}
+void KThread::stop()
+{
+ SynFreeAll(0);
+ m_running = false;
+}
+void KThread::trigger_event()
+{
+ KLinkedListNode<KTimeedEvent> *t = m_owner->m_Kernel->m_Timedevent.list;
+ while (t)
+ {
+ if (t->data == this)
+ m_owner->m_Kernel->m_Timedevent.RemoveItem(t);
+ t = t->next;
+ }
+ SynFree(0, this);//the system is waiting for itself so free it
+}
+bool KThread::IsInstanceOf(ClassName name) {
+ if (name == KThread::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
+
+void KThread::Destroy() {
+ // temporary
+}
+
+bool KThread::Synchronization(KThread* thread, u32 &error)
+{
+ return m_running;
+}
+
+void KThread::SyncStall(KLinkedList<KSynchronizationObject>* objects, bool waitAll) //todo stop if a error happen
+{
+ if (objects->list)
+ {
+ int sorce = 0; //check if the obj is not desolate
+ KLinkedListNode<KSynchronizationObject>* current = objects->list;// get the last
+ while (current->next != NULL)
+ {
+ current = current->next;
+ }
+ while (current != NULL) //check if this is correct but it looks like it is TODO
+ {
+ if (current->data->m_killed == true)
+ {
+ //this must search for the core
+ if (m_core)
+ {
+ m_core->SetRegister(1, sorce);
+ m_core->SetRegister(0, 0xC920181A); //error closed
+ return;
+ }
+ else
+ {
+ m_context.cpu_registers[1] = sorce;
+ m_context.cpu_registers[0] = 0xC920181A; //error closed
+ }
+ }
+ sorce++;
+ current = current->prev;
+ }
+
+ m_waitAll = waitAll;
+ m_owner->m_Kernel->ReScheduler();
+
+ if (Threadwaitlist)
+ {
+ delete Threadwaitlist;
+ Threadwaitlist = NULL;
+ }
+ Threadwaitlist = objects;
+
+ KLinkedList<KSynchronizationObject> free;
+
+
+ u32 errorCode = 0;
+ //check for 0
+
+ current = objects->list;
+ while (current->next != NULL)
+ {
+ current = current->next;
+ }
+ while (current != NULL) //check if this is correct but it looks like it is TODO
+ {
+ bool locked = current->data->Syn(this, errorCode);
+ if (!locked)
+ {
+ free.AddItem(current->data);
+ if (!waitAll)
+ break;
+ }
+ current = current->prev;
+ }
+ while (free.list)
+ {
+ free.list->data->SynFree(errorCode, this);
+ free.RemoveItem(free.list);
+ }
+ }
+
+}
+void KThread::SyncFree(s32 errorCode, KSynchronizationObject* obj)
+{
+
+ //find src
+ u32 sorce = 0;
+
+ KLinkedListNode<KSynchronizationObject>* current;// get the last
+ if (Threadwaitlist->list) //only if the thread is waiting
+ {
+ current = Threadwaitlist->list;
+ bool found = false;
+
+ while (current->prev != NULL)
+ {
+ current = current->prev;
+ }
+
+ while (current != NULL)
+ {
+ if (found)
+ sorce++;
+ if (current->data == obj)
+ found = true;
+
+ if (m_waitAll)
+ Threadwaitlist->RemoveItem(current);
+ current = current->next;
+ }
+
+ if (!m_waitAll)
+ {
+ while (Threadwaitlist->list != NULL)
+ {
+ Threadwaitlist->list->data->SynRemove(this);
+ Threadwaitlist->RemoveItem(Threadwaitlist->list);
+ }
+ }
+ if (!Threadwaitlist->list || errorCode) //check if the error code stuff is correct TODO
+ {
+ //this must search for the core
+ if (m_core)
+ {
+ if (errorCode || !m_waitAll)
+ m_core->SetRegister(1,sorce);
+ m_core->SetRegister(0, errorCode);
+ }
+ else
+ {
+ if (errorCode || !m_waitAll)
+ m_context.cpu_registers[1] = sorce;
+ m_context.cpu_registers[0] = errorCode;
+ }
+ LOG("free %s thread %d %08x %08x", m_owner->GetName(), m_thread_id, errorCode, sorce);
+ //todo speek to Scheduler here
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+
+//tools
+
+KTimer::KTimer(KProcess *owner, u32 resettype) : m_ResetType(resettype), m_owner(owner), m_Enabled(false), m_locked(true)
+{
+ m_owner->m_Kernel->m_Timedevent.AddItem(this);
+}
+KTimer::~KTimer()
+{
+ KLinkedListNode<KTimeedEvent> *t = m_owner->m_Kernel->m_Timedevent.list;
+ while (t)
+ {
+ if (t->data == this)
+ m_owner->m_Kernel->m_Timedevent.RemoveItem(t);
+ t = t->next;
+ }
+
+}
+void KTimer::trigger_event()
+{
+ KThread* found;
+ found = SynGetNextPrio();
+ if (found)
+ SynFree(0, found);
+ else
+ if (m_ResetType != 2) //pulse
+ m_locked = false;
+ m_owner->m_Kernel->FireNextTimeEvent(this, m_Interval);
+}
+Result KTimer::SetTimer(s64 initial, s64 interval)
+{
+ m_Enabled = true;
+ m_Initial = initial;
+ m_Interval = interval;
+ m_owner->m_Kernel->FireNextTimeEvent(this, m_Initial + 1);
+ return Success;
+}
+void KTimer::Cancel()
+{
+ m_Enabled = false;
+ num_cycles_remaining = 0;
+}
+bool KTimer::Synchronization(KThread* thread, u32 &error)
+{
+
+ bool temp = m_locked;
+ if (m_ResetType != 1)
+ m_locked = true;
+ return temp;
+}
+
+bool KTimer::IsInstanceOf(ClassName name) {
+ if (name == KTimer::name)
+ return true;
+
+ return super::IsInstanceOf(name);
+}
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+
+#define LOGP9COM
+
+Process9::Process9(KKernel* kernel) :HWIPC(kernel), m_FS(this), m_PM(this), m_PS(this), m_MC(this), m_AM(this)
+{
+ m_IPCSYNCP9 = 1; // init step
+ m_IntiHadData = false;
+ m_datarecved = 0;
+}
+Process9::~Process9()
+{
+}
+void Process9::FIFOReadBackCall()
+{
+ if (m_IPCSYNCP9 == 1 && m_IntiHadData) // initing
+ {
+ if (m_RECVFIFOSTAT_ERROR & 0x1) //empty
+ {
+ m_IPCSYNCP9 = 2; // ready for data
+ }
+ if (!(m_SENDFIFOSTAT & 0x1))
+ {
+ u32 temp = FIFOp9read();
+ FIFOp9write(temp);
+ }
+ }
+ else if(m_IPCSYNCP9 == 2) //this is send data
+ {
+ u32 translated = m_datasend[1] & 0x3F;
+ u32 nomal = (m_datasend[1] >> 6) & 0x3F;
+ u32 size = translated + nomal;
+ while (!(m_SENDFIFOSTAT & 0x1) && m_datasended <= size + 1)
+ {
+ FIFOp9write(m_datasend[m_datasended++]);
+ }
+ }
+}
+void Process9::FIFOWriteBackCall()
+{
+ if (m_IPCSYNCP9 == 1) //init
+ {
+ if (!(m_RECVFIFOSTAT_ERROR & 0x2))
+ {
+ u32 temp = FIFOp9read();
+ FIFOp9write(temp);
+ }
+ else
+ {
+ m_IntiHadData = true;
+ }
+ }
+ else if (m_IPCSYNCP9 == 2)
+ {
+ //print what you get
+ u32 temp = FIFOp9read();
+ m_datarecv[m_datarecved] = temp;
+ m_datarecved++;
+ if (m_datarecved > 1)
+ {
+ u32 translated = m_datarecv[1] & 0x3F;
+ u32 nomal = (m_datarecv[1] >> 6) & 0x3F;
+ u32 size = translated + nomal;
+ if (m_datarecved > 1 + size)
+ {
+#ifdef LOGP9COM
+ for (u32 i = 1; i < m_datarecved; i++)
+ LOG("recv: %08x", m_datarecv[i]);
+#endif
+ switch (m_datarecv[0])
+ {
+ case 0:
+ m_MC.Command(&m_datarecv[1], m_datarecv[0]);
+ break;
+ case 1:
+#ifdef LOGP9COM
+ LOG("P9 FS0");
+#endif
+ m_FS.Command(&m_datarecv[1], m_datarecv[0]);
+ break;
+ case 2:
+#ifdef LOGP9COM
+ LOG("P9 FS1");
+#endif
+ m_FS.Command(&m_datarecv[1], m_datarecv[0]);
+ break;
+ case 3:
+#ifdef LOGP9COM
+ LOG("P9 FS2");
+#endif
+ m_FS.Command(&m_datarecv[1], m_datarecv[0]);
+ break;
+ case 4:
+#ifdef LOGP9COM
+ LOG("P9 FS3");
+#endif
+ m_FS.Command(&m_datarecv[1], m_datarecv[0]);
+ break;
+ case 5:
+ m_PM.Command(&m_datarecv[1], m_datarecv[0]);
+ break;
+ case 7:
+ m_AM.Command(&m_datarecv[1], m_datarecv[0]);
+ break;
+ case 8:
+ m_PS.Command(&m_datarecv[1], m_datarecv[0]);
+ break;
+ default:
+ LOG("P9 data to unknown %08x", m_datarecv[0]);
+ }
+
+ m_datarecved = 0;
+ }
+ }
+ }
+}
+void Process9::FIFOIRQ()
+{
+
+}
+void Process9::FIFOIRQOLD() //this is unused on the 3DS
+{
+
+}
+void Process9::Sendresponds(u32 myid, u32 data[])
+{
+ u32 translated = data[0] & 0x3F;
+ u32 nomal = (data[0] >> 6) & 0x3F;
+ u32 size = translated + nomal;
+ m_datasended = 0;
+ m_datasend[0] = myid;
+
+ memcpy(&m_datasend[1], data, (size + 1)*sizeof(u32));
+ while (!(m_RECVFIFOSTAT_ERROR & 0x2) && m_datasended <= size + 1)
+ {
+ FIFOp9write(m_datasend[m_datasended++]);
+ }
+
+ m_kernel->FireInterrupt(0x50); //send the pix everything ready to read
+}
+
+u64 Process9::GetTitleFromPM(u64 handle)
+{
+ return m_PM.GetTitle(handle);
+}
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "Bootloader.h"
+
+P9AM::P9AM(Process9* owner) : m_owner(owner)
+{
+
+}
+P9AM::~P9AM()
+{
+
+}
+
+
+void P9AM::Command(u32 data[], u32 numb)
+{
+ u32 resdata[0x200];
+ memset(resdata, 0, sizeof(resdata));
+
+ u16 cmd = (data[0] >> 16);
+ switch (cmd)
+ {
+ case 0x1:
+ LOG("GetTitleCount %02x", data[1] & 0xFF);
+ resdata[0] = 0x00010080;
+ resdata[1] = 0;
+ resdata[2] = 0;
+ break;
+ case 0x3F:
+ LOG("unk3F %02x", data[1]&0xFF);
+ resdata[0] = 0x003F0080;
+ resdata[1] = 0;
+ resdata[2] = 0; //u8
+ break;
+ default:
+ LOG("unknown AM cmd %08x", data[0]);
+ break;
+ }
+ m_owner->Sendresponds(numb, resdata);
+}
+
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "process9/archive.h"
+
+Archive1234567b::Archive1234567b(Process9* owner, LowPath *lowpath) : Archive(owner, lowpath)
+{
+ char string[0x200];
+ char string2[0x200];
+
+ memcpy(string2, lowpath->getraw(), lowpath->GetSize());
+ snprintf(string, 0x200, "NAND/data/00000000000000000000000000000000/extdata/%08x/%08x", *(u32*)(m_lowpath.getraw() + 8), *(u32*)(m_lowpath.getraw() + 4));
+
+ if (0 != access(string, 0x00)) {
+ if (ENOENT == errno) {
+ throw 0xc8804464;
+ }
+ if (ENOTDIR == errno) {
+ throw 0xc8804464;
+ }
+ }
+}
+P9File* Archive1234567b::OpenFile(LowPath* lowpath, u32 flags, u32 attributes, u32* result)
+{
+
+ char string[0x200];
+ char string2[0x200];
+
+ memcpy(string2, lowpath->getraw(), lowpath->GetSize());
+ snprintf(string, 0x200, "NAND/data/00000000000000000000000000000000/extdata/%08x/%08x%s", *(u32*)(m_lowpath.getraw() + 8), *(u32*)(m_lowpath.getraw() + 4), string2);
+
+ LOG(" path: %s", string2);
+ char mode[10];
+ FILE * fd = Common::fopen_mkdir(string, P9File::FlagsToMode(flags, mode)); //TODO get proper openflags
+ if (!fd)
+ return NULL;
+
+ fseek64(fd, 0, SEEK_END);
+ u64 size = ftell64(fd);
+ fseek64(fd, 0, SEEK_SET);
+
+ u8 *hash = new u8[0x20];
+ memset(hash, 0, 0x20); //TODO: Implement SHA256 hashing
+ P9File * f = new P9File(m_owner, m_lowpath, *lowpath, fd, 0, size, hash, 0x1234567b);
+ return f;
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "process9/archive.h"
+
+extern FILE* openapp(u32 titlehigh, u32 titlelow); //this is from Bootloader.cpp
+extern s64 FindTableOffset(FILE* fd, char* name, u64 &out_size, u8* hash_out);
+
+Archive1234567c::Archive1234567c(Process9* owner, LowPath *lowpath) : Archive(owner, lowpath)
+{
+}
+
+P9File* Archive1234567c::OpenFile(LowPath* lowpath, u32 flags, u32 attributes, u32* result)
+{
+ LOG(" path: sysmodule = %08x", *(u32*)lowpath->getraw());
+
+ char string[0x100];
+ snprintf(string, 0x100, "NAND/data/00000000000000000000000000000000/sysdata/%08x/00000000", *(u32*)lowpath->getraw());
+
+ char mode[10];
+ FILE * fd = Common::fopen_mkdir(string, P9File::FlagsToMode(flags, mode)); //TODO get proper openflags
+ if (!fd)
+ return NULL;
+
+ fseek64(fd, 0, SEEK_END);
+ u64 size = ftell64(fd);
+ fseek64(fd, 0, SEEK_SET);
+
+ u8 *hash = new u8[0x20];
+ memset(hash, 0, 0x20); //TODO: Implement SHA256 hashing
+ P9File * f = new P9File(m_owner, m_lowpath, *lowpath, fd, 0, size, hash, 0x1234567c);
+ return f;
+}
+
+void Archive1234567c::DeleteFile(LowPath* lowpath, u32* result)
+{
+ LOG(" path: sysmodule = %08x", *(u32*)lowpath->getraw());
+
+ char string[0x100];
+ snprintf(string, 0x100, "NAND/data/00000000000000000000000000000000/sysdata/%08x/00000000", *(u32*)lowpath->getraw());
+
+ *result = remove(string);
+};
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "process9/archive.h"
+
+Archive1234567d::Archive1234567d(Process9* owner, LowPath *lowpath) : Archive(owner, lowpath)
+{
+}
+P9File* Archive1234567d::OpenFile(LowPath* lowpath, u32 flags, u32 attributes, u32* result)
+{
+
+ char string[0x200];
+ char string2[0x200];
+
+ memcpy(string2, lowpath->getraw(), lowpath->GetSize());
+ Common::U16ToASCII(string2);
+ LOG(" path: RW = %s", string2);
+ snprintf(string, 0x200, "NAND/rw/%s", string2);
+
+ char mode[10];
+ FILE * fd = Common::fopen_mkdir(string, P9File::FlagsToMode(flags, mode)); //TODO get proper openflags
+ if (!fd)
+ return NULL;
+
+ fseek64(fd, 0, SEEK_END);
+ u64 size = ftell64(fd);
+ fseek64(fd, 0, SEEK_SET);
+
+ u8 *hash = new u8[0x20];
+ memset(hash, 0, 0x20); //TODO: Implement SHA256 hashing
+ P9File * f = new P9File(m_owner, m_lowpath, *lowpath, fd, 0, size, hash, 0x1234567d);
+ return f;
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "process9/archive.h"
+
+Archive1234567e::Archive1234567e(Process9* owner, LowPath *lowpath) : Archive(owner, lowpath)
+{
+}
+P9File* Archive1234567e::OpenFile(LowPath* lowpath, u32 flags, u32 attributes, u32* result)
+{
+ char string[0x200];
+ char string2[0x200];
+ memcpy(string2, lowpath->getraw(), lowpath->GetSize());
+ Common::U16ToASCII(string2);
+ LOG(" path: RO = %s", string2);
+ snprintf(string, 0x200, "NAND/ro/%s", string2);
+
+ char mode[10];
+ FILE * fd = Common::fopen_mkdir(string, P9File::FlagsToMode(flags, mode)); //TODO get proper openflags
+ if (!fd)
+ return NULL;
+
+ fseek64(fd, 0, SEEK_END);
+ u64 size = ftell64(fd);
+ fseek64(fd, 0, SEEK_SET);
+
+ u8 *hash = new u8[0x20];
+ memset(hash, 0, 0x20); //TODO: Implement SHA256 hashing
+ P9File * f = new P9File(m_owner, m_lowpath, *lowpath, fd, 0, size, hash, 0x1234567e);
+ return f;
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "process9/archive.h"
+#include "Bootloader.h"
+extern FILE* openapp(u32 titlehigh, u32 titlelow); //this is from Bootloader.cpp
+extern s64 FindTableOffset(FILE* fd, char* name, u64 &out_size, u8* hash_out);
+
+//utill
+static u32 Read32(uint8_t p[4])
+{
+ u32 temp = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+ return temp;
+}
+
+Archive2345678a::Archive2345678a(Process9* owner, LowPath *lowpath) : Archive(owner, lowpath)
+{
+}
+
+P9File* Archive2345678a::OpenFile(LowPath* lowpath, u32 flags, u32 attributes, u32* result)
+{
+ FILE* fd = openapp(*(u32*)(m_lowpath.getraw() + 4), (*(u32*)m_lowpath.getraw() & 0xFFFFFF));
+ if (!fd)
+ return NULL;
+
+
+ fseek(fd, 0, SEEK_SET);
+ if (fd == NULL)
+ {
+ return NULL;
+ }
+
+
+ //open the container
+ ctr_ncchheader loader_h;
+ u32 ncch_off = 0;
+
+ // Read header.
+ if (fread(&loader_h, sizeof(loader_h), 1, fd) != 1) {
+ XDSERROR("failed to read header.");
+ return NULL;
+ }
+ // Load NCCH
+ if (memcmp(&loader_h.magic, "NCCH", 4) != 0) {
+ XDSERROR("invalid magic.. wrong file?");
+ return NULL;
+ }
+
+ u32 fs_off = Read32(loader_h.romfsoffset) * 0x200;
+ u32 fs_sz = Read32(loader_h.romfssize) * 0x200;
+
+ fseek64(fd, 0, SEEK_END);
+ u64 size = ftell64(fd);
+ fseek64(fd, 0, SEEK_SET);
+
+ u8 *hash = new u8[0x20];
+ memset(hash, 0, 0x20); //TODO: Implement SHA256 hashing
+ P9File * f = new P9File(m_owner, m_lowpath, *lowpath, fd, fs_off, fs_sz, hash, 0x2345678a);
+ return f;
+}
\ No newline at end of file
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "process9/archive.h"
+
+extern FILE* openapp(u32 titlehigh, u32 titlelow); //this is from Bootloader.cpp
+extern s64 FindTableOffset(FILE* fd, char* name, u64 &out_size, u8* hash_out);
+extern s64 FindRomFSOffset(FILE* fd, char* name, u64 &out_size, u8* hash_out);
+
+#define strcpy_s(a,b,c) strncpy(a,c,b)
+
+Archive2345678e::Archive2345678e(Process9* owner, LowPath *lowpath) : Archive(owner, lowpath)
+{
+ m_title = owner->GetTitleFromPM(lowpath->GetHandle());
+}
+P9File* Archive2345678e::OpenFile(LowPath* lowpath, u32 flags, u32 attributes, u32* result)
+{
+ char path[9];
+ strcpy_s(path,8, (char*)lowpath->getraw() + 4);
+ LOG(" path: type = %08x, str = %s", *(u32*)lowpath->getraw(), path);
+ FILE * fd = openapp(m_title >> 32, (u32)m_title);
+ u64 size;
+ u8 *hash = new u8[0x20];
+ s64 offset;
+ switch (*(u32*)lowpath->getraw())
+ {
+ case 0:
+ offset = FindRomFSOffset(fd, path, size, hash);
+ break;
+ case 1:
+ offset = FindTableOffset(fd, path, size, hash);
+ break;
+ default:
+ LOG("error unknown src");
+ for (u32 i = 0; i < lowpath->GetSize(); i++)
+ printf("%02x", lowpath->getraw()[i]);
+ LOG("");
+ return NULL;
+ }
+ P9File * f = new P9File(m_owner, m_lowpath, *lowpath, fd, offset, size, hash, 0x2345678e);
+ return f;
+}
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "process9/archive.h"
+
+Archive567890b0::Archive567890b0(Process9* owner, LowPath *lowpath) : Archive(owner,lowpath)
+{
+}
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "process9/archive.h"
+
+Archive::Archive(Process9* owner, LowPath *lowpath) :m_owner(owner), m_lowpath(*lowpath)
+{
+ LOG("path: type = %s, str = %s, size = %d", lowpath->TypeToString(), lowpath->GetPath().c_str(), lowpath->GetSize());
+}
+
+Archive::~Archive()
+{
+}
--- /dev/null
+#include "Common.h"
+
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "process9/archive.h"
+
+P9File::P9File(Process9* owner, LowPath lowpath, LowPath highpath, u32 achivetype) : m_owner(owner), m_lowpath(lowpath), m_highpath(highpath), m_achivetype(achivetype)
+{
+
+}
+P9File::P9File(Process9* owner, LowPath lowpath, LowPath highpath, FILE* fs, u64 offset, u64 size, u8* hash, u32 achivetype) : m_owner(owner), m_lowpath(lowpath), m_highpath(highpath), m_fs(fs), m_offset(offset), m_size(size), m_hash(hash), m_achivetype(achivetype)
+{
+
+}
+P9File::~P9File() {
+ delete m_hash;
+ if (m_fs)
+ fclose(m_fs);
+}
+
+u64 P9File::getsize()
+{
+ return m_size;
+}
+
+u8* P9File::GetHashPtr()
+{
+ return m_hash;
+}
+
+s32 P9File::read(u8 *buffer,u32 size,u64 file_offset,u32 &out_sizeread)
+{
+ fseek64(m_fs, file_offset + m_offset, SEEK_SET);
+ out_sizeread = fread(buffer, 1, size, m_fs);
+ if ((m_achivetype == 0x1234567c || m_achivetype == 0x1234567b) && file_offset == 0 && size >= 0x100) //MAC AESEnginePatch
+ {
+ memset(buffer, 0x11, 0x100);
+ }
+ return 0;
+}
+
+s32 P9File::write(u8 *buffer, u32 size, u64 file_offset, u32 &out_sizewritten)
+{
+ fseek64(m_fs, file_offset + m_offset, SEEK_SET);
+ out_sizewritten = fwrite(buffer, 1, size, m_fs);
+ fflush(m_fs);
+ return 0;
+}
+s32 P9File::setsize(u64 size)
+{
+ m_size = size;
+ if (ftruncate(fileno(m_fs), size) == -1) {
+ LOG("ftruncate failed.\n");
+ return -1;
+ }
+ return 0;
+}
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "process9/archive.h"
+
+#define LOGFS
+
+P9FS::P9FS(Process9* owner) : m_owner(owner)
+{
+}
+P9FS::~P9FS()
+{
+
+}
+void P9FS::Command(u32 data[], u32 numb)
+{
+ u32 resdata[0x200];
+ memset(resdata, 0, sizeof(resdata));
+ u16 cmd = (data[0] >> 16);
+ resdata[0] = 0x00000040;
+ resdata[1] = 0x00000000;
+ switch (cmd)
+ {
+ case 0x01: //OpenFile
+ {
+ u32 transaction = data[1];
+ u64 handle = (data[2] >> 0) | ((u64)(data[3]) << 32);
+ u32 file_lowpath_type = data[4];
+ u32 file_lowpath_sz = data[5];
+ u32 flags = data[6];
+ u32 attr = data[7];
+ u32 file_lowpath_desc = data[8];
+ u32 file_lowpath_ptr = data[9];
+
+#ifdef LOGFS
+ char tmp[256];
+ LOG("FS OpenFile");
+ LOG(" archive_handle=%"PRIx64, handle);
+ LOG(" flags = %s", P9File::FlagsToString(flags, tmp));
+#endif
+
+ auto a = m_open.list;
+ while (a)
+ {
+ if (a->data->id == handle)
+ break;
+ a = a->next;
+ }
+
+ u64 p9file_handle = 0;
+ P9File* P9file = 0;
+ u32 result = 0xc8804464; //TODO: Find proper error code
+
+ if (a)
+ {
+ //This is freed in the destructor of LowPath
+ u8 *lowpath_data = new u8[file_lowpath_sz];
+ for (u32 i = 0; i < file_lowpath_sz; i++)
+ {
+ u8 sdata = 0;
+ s32 ret = m_owner->m_kernel->m_IPCFIFOAdresses[(file_lowpath_desc >> 4) & 0xF]->Read8(i + file_lowpath_ptr, sdata);
+ lowpath_data[i] = sdata;
+ }
+
+ LowPath lowpath(file_lowpath_type, file_lowpath_sz, file_lowpath_desc, lowpath_data);
+ P9file = a->data->Archobj->OpenFile(&lowpath, flags, attr, &result);
+ /*delete lowpath_data;
+ delete lowpath;*/ //todo fix me plz
+ }
+ if (P9file)
+ {
+ s_fsFileentry *a = new s_fsFileentry;
+ a->id = lastID++;
+ p9file_handle = a->id;
+ a->Archobj = P9file;
+ m_fopen.AddItem(a);
+ result = 0;
+ }
+ else
+ {
+ LOG("error opening file");
+ }
+
+#ifdef LOGFS
+ LOG(" p9file_handle=%"PRIx64, p9file_handle);
+#endif
+
+ resdata[0] = 0x000100C1;
+ resdata[1] = result;
+ resdata[2] = (u32)p9file_handle;
+ resdata[3] = (u32)(p9file_handle >> 32);
+ resdata[4] = 0x4; //this is needed
+ break;
+ }
+ case 0x02: //DeleteFile
+ {
+ u32 transaction = data[1];
+ u64 handle = (data[2] >> 0) | ((u64)(data[3]) << 32);
+ u32 file_lowpath_type = data[4];
+ u32 file_lowpath_sz = data[5];
+ u32 file_lowpath_desc = data[6];
+ u32 file_lowpath_ptr = data[7];
+
+#ifdef LOGFS
+ LOG("FS DeleteFile");
+ LOG(" archive_handle=%"PRIx64, handle);
+#endif
+
+ resdata[0] = 0x00020142;
+ resdata[1] = 0xFFFFFFFF; //todo get the correct error
+
+ auto a = m_open.list;
+ while (a)
+ {
+ if (a->data->id == handle)
+ break;
+ a = a->next;
+ }
+
+ if (a)
+ {
+ //This is freed in the destructor of LowPath
+ u8 *lowpath_data = new u8[file_lowpath_sz];
+ for (u32 i = 0; i < file_lowpath_sz; i++)
+ {
+ u8 sdata = 0;
+ s32 ret = m_owner->m_kernel->m_IPCFIFOAdresses[(file_lowpath_desc >> 4) & 0xF]->Read8(i + file_lowpath_ptr, sdata);
+ lowpath_data[i] = sdata;
+ }
+
+ auto lowpath = new LowPath(file_lowpath_type, file_lowpath_sz, file_lowpath_desc, lowpath_data);
+
+ u32 result = 0;
+ a->data->Archobj->DeleteFile(lowpath, &result);
+ delete lowpath;
+
+ if (result == -1)
+ result = 0xC8804478; //ENOENT
+
+ resdata[0] = 0x00020041;
+ resdata[1] = result;
+ }
+ break;
+ }
+ case 0x09: //ReadFile
+ {
+ u64 handle = (data[1] >> 0) | ((u64)(data[2]) << 32);
+ u64 file_offset = (data[3] >> 0) | ((u64)(data[4]) << 32);
+ u32 size = data[5];
+ u32 desc_read = data[6];
+ u32 ptr_read = data[7];
+
+#ifdef LOGFS
+ LOG("FS ReadFile %08x %08x %08x", size, desc_read, ptr_read);
+ LOG(" file_handle=%"PRIx64, handle);
+ LOG(" file_offset=%"PRIx64, file_offset);
+#endif
+ resdata[0] = 0x00090142;
+ resdata[1] = 0xFFFFFFFF; //todo get the correct error
+
+ u32 buffsize = size;
+ u8 *buffer = new u8[buffsize];
+ memset(buffer, 0, buffsize);
+
+ auto a = m_fopen.list;
+ while (a)
+ {
+ if (a->data->id == handle)
+ break;
+ a = a->next;
+ }
+ if (a)
+ {
+ u32 out_size = 0;
+ a->data->Archobj->read(buffer, size, file_offset, out_size);
+
+ for (u32 i = 0; i < out_size; i++)
+ {
+ s32 ret = m_owner->m_kernel->m_IPCFIFOAdresses[(desc_read >> 4) & 0xF]->Write8(i + ptr_read, buffer[i]);
+ }
+
+ resdata[0] = 0x00090081;
+ resdata[1] = 0;
+ resdata[2] = out_size;
+ resdata[3] = 0x4; //this is needed
+ }
+ break;
+ }
+ case 0xA: //CalculateFileHashSHA256
+ {
+ resdata[0] = 0x000A0041; //0x000A00C2
+ resdata[1] = 0xFFFFFFFF; //todo get the correct error
+ u64 handle = (data[1] >> 0) | ((u64)(data[2]) << 32);
+ u32 size_hashtable = data[3];
+ u32 desc_hashtable = data[4];
+ u32 ptr_hashtable = data[5];
+#ifdef LOGFS
+ LOG("FS CalculateFileHashSHA256");
+ LOG(" file_handle=%"PRIx64, handle);
+ LOG(" size=%08X", size_hashtable);
+ LOG(" ptr=%08X", ptr_hashtable);
+#endif
+ auto a = m_fopen.list;
+ while (a)
+ {
+ if (a->data->id == handle)
+ break;
+ a = a->next;
+ }
+ if (a)
+ {
+ resdata[0] = 0x000A0041;
+ resdata[1] = 0x0;
+ resdata[2] = 4;
+ u8* hash = a->data->Archobj->GetHashPtr();
+
+ for (u32 i = 0; i < size_hashtable; i++)
+ {
+ s32 ret = m_owner->m_kernel->m_IPCFIFOAdresses[(desc_hashtable >> 4) & 0xF]->Write8(i + ptr_hashtable, hash[i]);
+ }
+ }
+ break;
+ }
+ case 0x0B: //WriteFile
+ {
+ u64 handle = (data[1] >> 0) | ((u64)(data[2]) << 32);
+ u64 file_offset = (data[3] >> 0) | ((u64)(data[4]) << 32);
+ u32 size = data[5];
+ u32 unk = data[6];
+ u32 desc_write = data[7];
+ u32 ptr_write = data[8];
+
+#ifdef LOGFS
+ LOG("FS WriteFile %08x %08x %08x", size, desc_write, ptr_write);
+ LOG(" file_handle=%08X", handle);
+ LOG(" file_offset=%"PRIx64, file_offset);
+#endif
+ resdata[0] = 0x000B0182;
+ resdata[1] = 0xFFFFFFFF; //todo get the correct error
+
+ u32 buffsize = size;
+ u8 *buffer = new u8[buffsize];
+ memset(buffer, 0, buffsize);
+
+ auto a = m_fopen.list;
+ while (a)
+ {
+ if (a->data->id == handle)
+ break;
+ a = a->next;
+ }
+ if (a)
+ {
+ for (u32 i = 0; i < size; i++)
+ {
+ s32 ret = m_owner->m_kernel->m_IPCFIFOAdresses[(desc_write >> 4) & 0xF]->Read8(i + ptr_write, buffer[i]);
+ }
+
+ u32 out_size = 0;
+ a->data->Archobj->write(buffer, size, file_offset, out_size);
+
+ resdata[0] = 0x000B0081;
+ resdata[1] = 0;
+ resdata[2] = out_size;
+ resdata[3] = 0x4; //this is needed
+ }
+ break;
+ }
+ case 0xC: //CalcSavegameMAC
+ {
+ resdata[0] = 0x000C0104;
+ resdata[1] = 0xFFFFFFFF; //todo get the correct error
+
+ u64 handle = (data[1] >> 0) | ((u64)(data[2]) << 32);
+ u32 out_size = data[3];
+ u32 in_size = data[4];
+ u32 in_desc = data[5];
+ u32 in_ptr = data[6];
+ u32 out_desc = data[7];
+ u32 out_ptr = data[8];
+#ifdef LOGFS
+ LOG("FS CalcSavegameMAC");
+ LOG(" file_handle=%"PRIx64, handle);
+ LOG(" in size=%08X, ptr=%08X", in_size, in_ptr);
+ LOG(" out size=%08X, ptr=%08X", out_size, out_ptr);
+#endif
+ for (u32 i = 0; i < in_size; i++)
+ {
+ u8 data;
+ m_owner->m_kernel->m_IPCFIFOAdresses[(in_desc >> 4) & 0xF]->Read8(in_ptr + i, data);
+ printf("%02x ", data);
+ }
+ LOG("");
+ for (u32 i = 0; i < out_size; i++)
+ {
+ //s32 ret = m_owner->m_kernel->m_IPCFIFOAdresses[(out_desc >> 4) & 0xF]->Write8(i + out_ptr, rand() % 255);
+ s32 ret = m_owner->m_kernel->m_IPCFIFOAdresses[(out_desc >> 4) & 0xF]->Write8(i + out_ptr, 0x11);
+ }
+
+ resdata[0] = 0x000C0041;
+ resdata[1] = 0;
+ break;
+ }
+ case 0xD: //GetFileSize
+ {
+ resdata[0] = 0x000D0040;
+ resdata[1] = 0xFFFFFFFF; //todo get the correct error
+ u64 handle = (data[1] >> 0) | ((u64)(data[2]) << 32);
+#ifdef LOGFS
+ LOG("FS GetFileSize=%"PRIx64, handle);
+#endif
+ auto a = m_fopen.list;
+ while (a)
+ {
+ if (a->data->id == handle)
+ break;
+ a = a->next;
+ }
+ if (a)
+ {
+ resdata[0] = 0x000D00C0;
+ resdata[1] = 0x0;
+ u64 size = a->data->Archobj->getsize();
+ resdata[2] = (u32)size;
+ resdata[3] = size>>32;
+ }
+ break;
+ }
+ case 0xE: //SetFileSize
+ {
+ resdata[0] = 0x000E0100; //0x000A00C2
+ resdata[1] = 0xFFFFFFFF; //todo get the correct error
+ u64 size = (data[1] >> 0) | ((u64)(data[2]) << 32);
+ u64 handle = (data[3] >> 0) | ((u64)(data[4]) << 32);
+#ifdef LOGFS
+ LOG("FS SetFileSize");
+ LOG(" file_handle=%"PRIx64, handle);
+ LOG(" size=%"PRIx64, size);
+#endif
+ auto a = m_fopen.list;
+ while (a)
+ {
+ if (a->data->id == handle)
+ break;
+ a = a->next;
+ }
+ if (a)
+ {
+ resdata[0] = 0x000E0040;
+ resdata[1] = a->data->Archobj->setsize(size);
+ }
+ break;
+ }
+ case 0xF: //CloseFile
+ {
+ resdata[0] = 0x000F0040;
+ resdata[1] = 0xFFFFFFFF; //todo get the correct error
+ u64 handle = (data[1] >> 0) | ((u64)(data[2]) << 32);
+ LOG("CloseFile=%"PRIx64, handle);
+ auto a = m_fopen.list;
+ while (a)
+ {
+ if (a->data->id == handle)
+ break;
+ a = a->next;
+ }
+ if (a)
+ {
+ delete a->data->Archobj;
+ m_fopen.RemoveItem(a);
+ resdata[1] = 0;
+ }
+ break;
+ }
+ case 0x12: //OpenArchive
+ {
+ resdata[0] = 0x00120040;
+ resdata[1] = 0xFFFFFFFF; //todo get the correct error
+
+ u32 file_lowpath_type = data[2];
+ u32 file_lowpath_sz = data[3];
+ u32 file_lowpath_desc = data[4];
+ u32 file_lowpath_ptr = data[5];
+
+ //This is freed in the destructor of LowPath
+ u8 *lowpath_data = new u8[file_lowpath_sz];
+ for(u32 i = 0; i < file_lowpath_sz; i++)
+ {
+ u8 sdata = 0;
+ s32 ret = m_owner->m_kernel->m_IPCFIFOAdresses[(file_lowpath_desc >> 4) & 0xF]->Read8(i + file_lowpath_ptr, sdata);
+ lowpath_data[i] = sdata;
+ }
+
+ auto lowpath = new LowPath(file_lowpath_type, file_lowpath_sz, file_lowpath_desc, lowpath_data);
+
+ s_fsArchiveEntry *a = new s_fsArchiveEntry;
+ m_open.AddItem(a);
+ a->id = lastID++;
+
+#ifdef LOGFS
+ LOG("FS OpenArchive stub %08x %08x %08x %08x", data[1], data[2], data[3], data[4]);
+ LOG(" archive_handle=%"PRIx64, a->id);
+#endif
+ a->Archobj = NULL;
+ resdata[1] = 0;
+ try
+ {
+ switch (data[1])
+ {
+ case 0x1234567b: // ExtSaveData, and ExtSaveData for BOSS
+ a->Archobj = new Archive1234567b(this->m_owner, lowpath);
+ break;
+ case 0x1234567c: // SystemSaveData
+ a->Archobj = new Archive1234567c(this->m_owner, lowpath);
+ break;
+ case 0x1234567d: // NAND RW
+ a->Archobj = new Archive1234567d(this->m_owner, lowpath);
+ break;
+ case 0x1234567e: // NAND RO
+ a->Archobj = new Archive1234567e(this->m_owner, lowpath);
+ break;
+ case 0x2345678a: //User/GameCard SaveData (for check), and other uses (FS can only mount the latter) (lo hi mediatype reserved)
+ a->Archobj = new Archive2345678a(this->m_owner, lowpath);
+ break;
+ case 0x2345678e: // SaveData, ExeFS, and RomFS (For fs:LDR, only ExeFS)
+ a->Archobj = new Archive2345678e(this->m_owner, lowpath);
+ break;
+ case 0x567890B0: // NAND CTR FS
+ a->Archobj = new Archive567890b0(this->m_owner, lowpath);
+ break;
+ default:
+ throw 0xc8804464;
+ break;
+ }
+ }
+ catch (u32 val)
+ {
+ resdata[1] = val;
+ }
+ resdata[0] = 0x001200c1;
+ resdata[2] = (u32)a->id;
+ resdata[3] = (u32)(a->id >> 32);
+ resdata[4] = 0x4; //this is needed
+ delete lowpath;
+ break;
+ }
+ case 0x13:
+ {
+ LOG("FS update Quota.dat stub Archive: %08x%08x unk: %08x size: %08x", data[1], data[2], data[3], data[4]);
+
+ char* str = new char[data[4] + 1];
+ memset(str, 0, data[4] + 1);
+ for (u32 i = 0; i < data[4]; i++) //Quota.dat
+ {
+ u8 sdata = 0;
+ m_owner->m_kernel->m_IPCFIFOAdresses[(data[5] >> 4) & 0xF]->Read8(i + data[6], sdata);
+ str[i] = sdata;
+ }
+ LOG("%s",str);
+ resdata[0] = 0x00130040;
+ resdata[1] = 0x00000000;
+ resdata[2] = 0x00000000; //u8
+ delete str;
+ break;
+ }
+ case 0x16: //CloseArchive
+ {
+ resdata[0] = 0x00160080;
+ resdata[1] = 0xFFFFFFFF; //todo get the correct error
+
+ u64 handle = (data[1] >> 0) | ((u64)(data[2]) << 32);
+
+#ifdef LOGFS
+ LOG("FS CloseArchive - stubbed");
+ LOG(" archive_handle=%"PRIx64, handle);
+#endif
+
+ auto a = m_open.list;
+ while (a)
+ {
+ if (a->data->id == handle)
+ break;
+ a = a->next;
+ }
+
+ if (a)
+ {
+ //TODO: Should we really delete this or just ignore this call as kernel calls ReopenArchive with same handleid soon after closing it
+ m_open.RemoveItem(a);
+ delete a->data->Archobj;
+ delete a->data;
+ }
+
+ resdata[0] = 0x00160040;
+ resdata[1] = 0;
+ break;
+ }
+ case 0x17: //Unk17
+ {
+ u64 handle = (data[1] >> 0) | ((u64)(data[2]) << 32);
+#ifdef LOGFS
+ LOG("FS Unk17 - stubbed");
+ LOG(" archive_handle=%"PRIx64, handle);
+#endif
+ resdata[0] = 0x00170080;
+ resdata[1] = 0;
+ resdata[2] = 1; //this is a unsigned byte
+ break;
+ }
+ case 0x18: //GetCardType
+ {
+#ifdef LOGFS
+ LOG("GetCardType - stubbed");
+#endif
+ resdata[0] = 0x00180080;
+ resdata[1] = 0;
+ resdata[2] = 0; //this is a unsigned byte
+ break;
+ }
+ case 0x1C: //GetSdmcDetected
+ {
+#ifdef LOGFS
+ LOG("GetSdmcDetected - stubbed");
+#endif
+ resdata[0] = 0x001C0080;
+ resdata[1] = 0;
+ resdata[2] = 0; //no
+ break;
+ }
+ case 0x1D: //GetSdmcWritable
+ {
+#ifdef LOGFS
+ LOG("GetSdmcWritable - stubbed");
+#endif
+ resdata[0] = 0x001D0080;
+ resdata[1] = 0;
+ resdata[2] = 0; //no
+ break;
+ }
+ case 0x26: //GetCardSlotInserted
+ {
+#ifdef LOGFS
+ LOG("GetCardSlotInserted - stubbed");
+#endif
+ resdata[0] = 0x00260080;
+ resdata[1] = 0;
+ resdata[2] = 0; //no
+ break;
+ }
+ case 0x4D: //ReadFileWrapper
+ {
+ //This probably actually just checks that the hash is what it should be during read if not returns different error code
+ u64 handle = (data[1] >> 0) | ((u64)(data[2]) << 32);
+ u64 file_offset = (data[3] >> 0) | ((u64)(data[4]) << 32);
+ u32 size = data[5];
+ u32 alinement = data[6];
+ u32 size_hashtable = data[7];
+ u32 desc_hashtable = data[8];
+ u32 ptr_hashtable = data[9];
+ u32 desc_read = data[10];
+ u32 ptr_read = data[11];
+
+#ifdef LOGFS
+ LOG("FS ReadFileWrapper (SHA-256 not implemented) %08x %08x %08x", size, alinement, size_hashtable);
+ LOG(" file_handle=%"PRIx64, handle);
+ LOG(" file_offset=%"PRIx64, file_offset);
+ LOG(" pointer=%08x", ptr_read);
+ LOG(" modulename=%s", m_owner->m_kernel->m_IPCFIFOAdresses[(desc_read >> 4) & 0xF]->m_process->GetName());
+#endif
+ resdata[0] = 0x004D0040;
+ resdata[1] = 0xFFFFFFFF; //todo get the correct error
+
+ u32 buffsize = size;
+ if (size % alinement)
+ buffsize = ((buffsize + alinement) / alinement)*alinement;
+ u8 *buffer = new u8[buffsize];
+ memset(buffer, 0, buffsize);
+
+ auto a = m_fopen.list;
+ while (a)
+ {
+ if (a->data->id == handle)
+ break;
+ a = a->next;
+ }
+ if (a)
+ {
+ u32 out_size = 0;
+ a->data->Archobj->read(buffer, size, file_offset, out_size);
+
+ for (u32 i = 0; i < out_size; i++)
+ {
+ s32 ret = m_owner->m_kernel->m_IPCFIFOAdresses[(desc_read >> 4) & 0xF]->Write8(i + ptr_read, buffer[i]);
+ }
+
+ resdata[0] = 0x004D0081;
+ resdata[1] = 0;
+ resdata[2] = out_size;
+ resdata[3] = 0x4; //this is needed
+ }
+ break;
+ }
+ case 0x4E: //WriteFileWrapper
+ {
+ u64 handle = (data[1] >> 0) | ((u64)(data[2]) << 32);
+ u64 file_offset = (data[3] >> 0) | ((u64)(data[4]) << 32);
+ u32 size = data[5];
+ u32 unk = data[6];
+ u32 size_hashtable = data[7];
+ u32 unk2 = data[8];
+ u32 desc_write = data[9];
+ u32 ptr_write = data[10];
+ u32 desc_hash = data[11];
+ u32 ptr_hash = data[12];
+
+#ifdef LOGFS
+ LOG("FS WriteFileWrapper %08x %08x %08x %08x %08x", size, desc_write, ptr_write, unk, size_hashtable);
+ LOG(" file_handle=%08X", handle);
+ LOG(" file_offset=%"PRIx64, file_offset);
+#endif
+ resdata[0] = 0x000B0182;
+ resdata[1] = 0xFFFFFFFF; //todo get the correct error
+
+ u32 buffsize = size;
+ u8 *buffer = new u8[buffsize];
+ memset(buffer, 0, buffsize);
+
+ auto a = m_fopen.list;
+ while (a)
+ {
+ if (a->data->id == handle)
+ break;
+ a = a->next;
+ }
+ if (a)
+ {
+ for (u32 i = 0; i < size; i++)
+ {
+ s32 ret = m_owner->m_kernel->m_IPCFIFOAdresses[(desc_write >> 4) & 0xF]->Read8(i + ptr_write, buffer[i]);
+ }
+
+ u32 out_size = 0;
+ a->data->Archobj->write(buffer, size, file_offset, out_size);
+
+ resdata[0] = 0x000B0081;
+ resdata[1] = 0;
+ resdata[2] = out_size;
+ resdata[3] = 0x4; //this is needed
+ }
+ break;
+ }
+ case 0x4F: //0x004F0080 only data[1] used stored in seperate container (Bit(0 - 12) at 080949f0 + 0xC, Bit(12-24) 080949f0 + 0xE) nothing else done here
+#ifdef LOGFS
+ LOG("FS unk 0x4F stub (uppern 8 Bit unused)%08x (unused) %08x", data[1], data[2]);
+#endif
+ resdata[0] = 0x004F0040;
+ resdata[1] = 0x00000000;
+ break;
+ case 0x50: //dose nothing
+#ifdef LOGFS
+ LOG("FS init stub %u", data[1]);
+#endif
+ resdata[0] = 0x00500040;
+ resdata[1] = 0x00000000;
+ break;
+
+ default:
+ LOG("unknown FS cmd %08x (%08x,%08x,%08x,%08x,%08x,%08x)", data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
+ break;
+ }
+ m_owner->Sendresponds(numb, resdata);
+}
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "Bootloader.h"
+
+#define LOGMC
+
+P9MC::P9MC(Process9* owner) : m_owner(owner)
+{
+
+}
+P9MC::~P9MC()
+{
+
+}
+
+
+void P9MC::Command(u32 data[], u32 numb)
+{
+ u32 resdata[0x200];
+ memset(resdata, 0, sizeof(resdata));
+
+ u16 cmd = (data[0] >> 16);
+ switch (cmd)
+ {
+ case 0xA: //unkA
+ LOG("unk MC stub %08x %08x %08x"
+ , data[1], data[2], data[3]);
+ resdata[0] = 0x000A0040;
+ resdata[1] = 0;
+ break;
+ default:
+ LOG("unknown MC cmd %08x", data[0]);
+ break;
+ }
+ m_owner->Sendresponds(numb, resdata);
+}
+
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "Bootloader.h"
+
+#define LOGPM
+
+P9PM::P9PM(Process9* owner) :m_open(), m_owner(owner), handlecount(0x100000001)
+{
+
+}
+P9PM::~P9PM()
+{
+
+}
+
+extern FILE* openapp(u32 titlehigh, u32 titlelow); //this is from Bootloader.cpp
+
+void P9PM::Command(u32 data[],u32 numb)
+{
+ u32 resdata[0x200];
+ memset(resdata, 0, sizeof(resdata));
+
+ u16 cmd = (data[0] >> 16);
+ switch (cmd)
+ {
+ case 1:
+ {
+ u64 handle = (data[2] >> 0) | ((u64)(data[1]) << 32);
+ resdata[0] = 0x00020040;
+ auto a = m_open.list;
+ while (a)
+ {
+ if (a->data->handle == handle)
+ break;
+ a = a->next;
+ }
+ if (a)
+ {
+ LOG("pm getexheader handle=%"PRIx64", titleid=%"PRIx64, handle, a->data->title);
+ KMemoryMap* map = m_owner->m_kernel->m_IPCFIFOAdresses[(data[3] >> 4) &0xF];
+ resdata[1] = 0xE0000000;
+ FILE * fd = openapp(a->data->title >> 32, (u32)a->data->title);
+ if (fd)
+ {
+ //open the container
+ char ex[0x400];
+ ctr_ncchheader loader_h;
+ u32 ncch_off = 0;
+
+ // Read header.
+ if (fread(&loader_h, sizeof(loader_h), 1, fd) != 1) {
+ XDSERROR("failed to read header.");
+ break;
+ }
+ // Load NCCH
+ if (memcmp(&loader_h.magic, "NCCH", 4) != 0) {
+ XDSERROR("invalid magic.. wrong file?");
+ break;
+ }
+
+ // Read Exheader.
+ if (fread(&ex, 0x400, 1, fd) != 1) { //this is fixed
+ XDSERROR("failed to read exheader.");
+ break;
+ }
+ for (int i = 0; i < sizeof(ex); i++)
+ {
+ if (map->Write8(data[4] + i, ex[i]) != Success)
+ {
+ break;
+ }
+ }
+ resdata[1] = 0;
+
+ }
+ }
+ else
+ {
+#ifdef LOGPM
+ LOG("pm getexheader handle=%"PRIx64, handle);
+#endif
+ resdata[1] = 0xE0000000; //todo correct error
+ }
+ }
+ break;
+ case 2:
+ {
+ u64 title = (data[1] >> 0) | ((u64)(data[2]) << 32);
+#ifdef LOGPM
+ LOG("pm register %08x %08x", data[1], data[2]);
+#endif
+ if ((data[3] >> 24) != 0)
+ LOG("register flags %02x %02x", (data[3] >> 24), (data[3 + 4] >> 24));
+ if (data[1] != data[1 + 4] && data[2] != data[2 + 4])
+ LOG("register secound %08x %08x", data[1 + 4], data[2 + 4]);
+
+ struct PMOpenprocess * neone = (PMOpenprocess*)malloc(sizeof(struct PMOpenprocess));
+ neone->handle = handlecount++;
+ neone->title = title;
+ m_open.AddItem(neone);
+ resdata[0] = 0x000200C0;
+ resdata[1] = 0x0;
+ resdata[2] = neone->handle >> 32;
+ resdata[3] = (u32)neone->handle;
+ }
+ break;
+ default:
+ LOG("unknown PM cmd %08x", data[0]);
+ break;
+ }
+ m_owner->Sendresponds(numb, resdata);
+}
+
+u64 P9PM::GetTitle(u64 handle)
+{
+ auto a = m_open.list;
+ while(a)
+ {
+ if(a->data->handle == handle)
+ break;
+ a = a->next;
+ }
+
+ if(a)
+ {
+ return a->data->title;
+ }
+
+ return -1;
+}
--- /dev/null
+#include "Kernel.h"
+#include "Hardware.h"
+#include "Process9.h"
+#include "Bootloader.h"
+
+#define LOGPM
+
+P9PS::P9PS(Process9* owner) : m_owner(owner)
+{
+
+}
+P9PS::~P9PS()
+{
+
+}
+
+
+void P9PS::Command(u32 data[], u32 numb)
+{
+ u32 resdata[0x200];
+ memset(resdata, 0, sizeof(resdata));
+
+ u16 cmd = (data[0] >> 16);
+ switch (cmd)
+ {
+ case 0x3: //VerifyRsaSha256
+ LOG("VerifyRsaSha256 stub %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x"
+ , data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10]);
+ resdata[0] = 0x00030040;
+ resdata[1] = 0;
+ break;
+ default:
+ LOG("unknown PS cmd %08x", data[0]);
+ break;
+ }
+ m_owner->Sendresponds(numb, resdata);
+}
+
--- /dev/null
+#include "Util.h"
+#include "Platform.h"
+
+
+PMutex::PMutex() {
+ m_locked = false;
+#if EMU_PLATFORM == PLATFORM_LINUX
+ while(pthread_mutex_init(&m_mutex, NULL) != 0);
+#elif EMU_PLATFORM == PLATFORM_WINDOWS
+ while((m_mutex = CreateMutex(NULL, FALSE, NULL)) == NULL);
+#endif
+}
+
+PMutex::~PMutex() {
+#if EMU_PLATFORM == PLATFORM_LINUX
+ while(pthread_mutex_destroy(&m_mutex) != 0);
+#elif EMU_PLATFORM == PLATFORM_WINDOWS
+ CloseHandle(m_mutex);
+#endif
+}
+
+void PMutex::Lock() {
+#if EMU_PLATFORM == PLATFORM_LINUX
+ pthread_mutex_lock(&m_mutex);
+#elif EMU_PLATFORM == PLATFORM_WINDOWS
+ WaitForSingleObject(m_mutex, INFINITE);
+#endif
+ m_locked = true;
+}
+
+bool PMutex::IsLocked() {
+ return m_locked;
+}
+
+void PMutex::Unlock() {
+ m_locked = false;
+#if EMU_PLATFORM == PLATFORM_LINUX
+ pthread_mutex_unlock(&m_mutex);
+#elif EMU_PLATFORM == PLATFORM_WINDOWS
+ ReleaseMutex(m_mutex);
+#endif
+}
--- /dev/null
+#include "Common.h"
+#include "Util.h"
+
+#include <stdarg.h>
+
+#ifdef _MSC_VER
+#include <Windows.h>
+#include <codecvt>
+#else
+#include <iconv.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+
+namespace Common {
+ char * U16ToASCII(char* data)
+ {
+ char* data2 = data;
+ while (*data2 != 0)
+ {
+ data2+= 2;
+ data++;
+ *data = *data2;
+ }
+ return data;
+ }
+ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args)
+ {
+ int writtenCount;
+
+#ifdef _MSC_VER
+ // You would think *printf are simple, right? Iterate on each character,
+ // if it's a format specifier handle it properly, etc.
+ //
+ // Nooooo. Not according to the C standard.
+ //
+ // According to the C99 standard (7.19.6.1 "The fprintf function")
+ // The format shall be a multibyte character sequence
+ //
+ // Because some character encodings might have '%' signs in the middle of
+ // a multibyte sequence (SJIS for example only specifies that the first
+ // byte of a 2 byte sequence is "high", the second byte can be anything),
+ // printf functions have to decode the multibyte sequences and try their
+ // best to not screw up.
+ //
+ // Unfortunately, on Windows, the locale for most languages is not UTF-8
+ // as we would need. Notably, for zh_TW, Windows chooses EUC-CN as the
+ // locale, and completely fails when trying to decode UTF-8 as EUC-CN.
+ //
+ // On the other hand, the fix is simple: because we use UTF-8, no such
+ // multibyte handling is required as we can simply assume that no '%' char
+ // will be present in the middle of a multibyte sequence.
+ //
+ // This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l.
+ static _locale_t c_locale = nullptr;
+ if (!c_locale)
+ c_locale = _create_locale(LC_ALL, ".1252");
+ writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args);
+#else
+ writtenCount = vsnprintf(out, outsize, format, args);
+#endif
+
+ if (writtenCount > 0 && writtenCount < outsize)
+ {
+ out[writtenCount] = '\0';
+ return true;
+ }
+ else
+ {
+ out[outsize - 1] = '\0';
+ return false;
+ }
+ }
+
+ std::string StringFromFormat(const char* format, ...)
+ {
+ va_list args;
+ char *buf = nullptr;
+#ifdef _WIN32
+ int required = 0;
+
+ va_start(args, format);
+ required = _vscprintf(format, args);
+ buf = new char[required + 1];
+ CharArrayFromFormatV(buf, required + 1, format, args);
+ va_end(args);
+
+ std::string temp = buf;
+ delete[] buf;
+#else
+ va_start(args, format);
+ if (vasprintf(&buf, format, args) < 0)
+ LOG("Unable to allocate memory for string");
+ va_end(args);
+
+ std::string temp = buf;
+ free(buf);
+#endif
+ return temp;
+ }
+
+ unsigned int CountLeadingZeros(unsigned int num)
+ {
+ unsigned int x = 0x80000000;
+ unsigned int zero_count = 0;
+
+ for (int i = 0; i < 32; i++)
+ {
+ if ((x & num) == 0)
+ {
+ zero_count++;
+ x >>= 1;
+ continue;
+ }
+ break;
+ }
+
+ return zero_count;
+ }
+
+ /* Opens the file, creating directories in its pathspec if necessary */
+ FILE* fopen_mkdir(const char* name, const char* mode) {
+ char* mname = strdup(name);
+ int i;
+ for (i = 0; mname[i] != '\0'; i++)
+ {
+ if (i>0 && (mname[i] == '\\' || mname[i] == '/')) {
+ char slash = mname[i];
+ mname[i] = '\0';
+
+#ifdef _WIN32
+ int ret = _mkdir(mname);
+#else
+ int ret = mkdir(p, 0777);
+#endif
+ if (ret >0 && ret != EEXIST)
+ {
+ break;
+ }
+ mname[i] = slash;
+ }
+ }
+ free(mname);
+ FILE* temp = fopen(name, mode);
+ /*if (temp == NULL) //if file does not exist, create it
+ {
+ temp = fopen(name, "wb+"); //always use "wb+" to ensure it can read and write to the file
+ }*/
+
+ return temp;
+ }
+}
--- /dev/null
+#include "Util.h"
+#include "Common.h"
+
+LowPath::LowPath(u32 type, u32 size, u32 desc, u8* ptr) : m_type(type), m_size(size), m_desc(desc)
+{
+ m_ptr = new u8[m_size];
+ memcpy(m_ptr, ptr, m_size);
+}
+LowPath::LowPath(LowPath &pat) : m_type(pat.m_type), m_size(pat.m_size), m_desc(pat.m_desc)
+{
+ m_ptr = new u8[pat.m_size];
+ memcpy(m_ptr, pat.m_ptr, pat.m_size);
+}
+LowPath::~LowPath() {
+ delete[] m_ptr;
+}
--- /dev/null
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xds", "xds\xds.vcxproj", "{09D06FBC-3840-460A-A62F-915E9976D3DD}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {09D06FBC-3840-460A-A62F-915E9976D3DD}.Debug|Win32.ActiveCfg = Debug|Win32
+ {09D06FBC-3840-460A-A62F-915E9976D3DD}.Debug|Win32.Build.0 = Debug|Win32
+ {09D06FBC-3840-460A-A62F-915E9976D3DD}.Debug|x64.ActiveCfg = Debug|x64
+ {09D06FBC-3840-460A-A62F-915E9976D3DD}.Debug|x64.Build.0 = Debug|x64
+ {09D06FBC-3840-460A-A62F-915E9976D3DD}.Release|Win32.ActiveCfg = Release|Win32
+ {09D06FBC-3840-460A-A62F-915E9976D3DD}.Release|Win32.Build.0 = Release|Win32
+ {09D06FBC-3840-460A-A62F-915E9976D3DD}.Release|x64.ActiveCfg = Release|x64
+ {09D06FBC-3840-460A-A62F-915E9976D3DD}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal