// Copyright (C) 2010 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. // 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/ #ifndef _DSP_JIT_UTIL_H #define _DSP_JIT_UTIL_H #include "../DSPMemoryMap.h" #include "../DSPHWInterface.h" #include "../DSPEmitter.h" #include "x64Emitter.h" #include "ABI.h" using namespace Gen; // Performs the hashing required by increment/increase/decrease_addr_reg void DSPEmitter::ToMask(X64Reg value_reg, X64Reg temp_reg) { MOV(16, R(temp_reg), R(value_reg)); SHR(16, R(temp_reg), Imm8(8)); OR(16, R(value_reg), R(temp_reg)); MOV(16, R(temp_reg), R(value_reg)); SHR(16, R(temp_reg), Imm8(4)); OR(16, R(value_reg), R(temp_reg)); MOV(16, R(temp_reg), R(value_reg)); SHR(16, R(temp_reg), Imm8(2)); OR(16, R(value_reg), R(temp_reg)); MOV(16, R(temp_reg), R(value_reg)); SHR(16, R(temp_reg), Imm8(1)); OR(16, R(value_reg), R(temp_reg)); } // HORRIBLE UGLINESS, someone please fix. // See http://code.google.com/p/dolphin-emu/source/detail?r=3125 // EAX = g_dsp.r[reg] // EDX = g_dsp.r[DSP_REG_WR0 + reg] void DSPEmitter::increment_addr_reg(int reg) { // s16 tmp = g_dsp.r[reg]; MOV(16, R(EAX), M(&g_dsp.r[reg])); MOV(16, R(EDX), M(&g_dsp.r[DSP_REG_WR0 + reg])); //ToMask(WR0), calculating it into EDI MOV(16, R(EDI), R(EDX)); ToMask(EDI); // if ((tmp & tmb) == tmb) MOV(16, R(ESI), R(EAX)); AND(16, R(ESI), R(EDI)); CMP(16, R(ESI), R(EDI)); FixupBranch not_equal = J_CC(CC_NE); // tmp ^= wr_reg XOR(16, R(EAX), R(EDX)); FixupBranch end = J(); SetJumpTarget(not_equal); // else tmp++ ADD(16, R(EAX), Imm16(1)); SetJumpTarget(end); // g_dsp.r[reg] = tmp; MOV(16, M(&g_dsp.r[reg]), R(EAX)); } // See http://code.google.com/p/dolphin-emu/source/detail?r=3125 // EAX = g_dsp.r[reg] // EDX = g_dsp.r[DSP_REG_WR0 + reg] void DSPEmitter::decrement_addr_reg(int reg) { // s16 tmp = g_dsp.r[reg]; MOV(16, R(EAX), M(&g_dsp.r[reg])); MOV(16, R(EDX), M(&g_dsp.r[DSP_REG_WR0 + reg])); // if ((tmp & g_dsp.r[DSP_REG_WR0 + reg]) == 0) TEST(16, R(EAX), R(EDX)); FixupBranch not_equal = J_CC(CC_NZ); // tmp |= g_dsp.r[DSP_REG_WR0 + reg]; OR(16, R(EAX), R(EDX)); FixupBranch end = J(); SetJumpTarget(not_equal); // tmp--; SUB(16, R(EAX), Imm16(1)); SetJumpTarget(end); // g_dsp.r[reg] = tmp; MOV(16, M(&g_dsp.r[reg]), R(EAX)); } // Increase addr register according to the correspond ix register // EAX = g_dsp.r[reg] // ECX = g_dsp.r[DSP_REG_IX0 + reg] // EDX = g_dsp.r[DSP_REG_WR0 + reg] // EDI = tomask(EDX) void DSPEmitter::increase_addr_reg(int reg) { MOV(16, R(ECX), M(&g_dsp.r[DSP_REG_IX0 + reg])); //IX0 == 0, bail out TEST(16, R(ECX), R(ECX)); FixupBranch end = J_CC(CC_Z); MOV(16, R(EAX), M(&g_dsp.r[reg])); MOV(16, R(EDX), M(&g_dsp.r[DSP_REG_WR0 + reg])); //IX0 > 0 FixupBranch neg = J_CC(CC_L); //ToMask(WR0), calculating it into EDI MOV(16, R(EDI), R(EDX)); ToMask(EDI); //dsp_increment JumpTarget loop_pos = GetCodePtr(); // if ((tmp & tmb) == tmb) MOV(16, R(ESI), R(EAX)); AND(16, R(ESI), R(EDI)); CMP(16, R(ESI), R(EDI)); FixupBranch pos_neq = J_CC(CC_NE); // tmp ^= wr_reg XOR(16, R(EAX), R(EDX)); FixupBranch pos_eq = J(); SetJumpTarget(pos_neq); // else tmp++ ADD(16, R(EAX), Imm16(1)); SetJumpTarget(pos_eq); SUB(16, R(ECX), Imm16(1)); // value-- CMP(16, R(ECX), Imm16(0)); // value > 0 J_CC(CC_G, loop_pos); FixupBranch end_pos = J(); //else, IX0 < 0 SetJumpTarget(neg); JumpTarget loop_neg = GetCodePtr(); //dsp_decrement // if ((tmp & wr_reg) == 0) TEST(16, R(EAX), R(EDX)); FixupBranch neg_nz = J_CC(CC_NZ); // tmp |= wr_reg; OR(16, R(EAX), R(EDX)); FixupBranch neg_z = J(); SetJumpTarget(neg_nz); // else tmp-- SUB(16, R(EAX), Imm16(1)); SetJumpTarget(neg_z); ADD(16, R(ECX), Imm16(1)); // value++ CMP(16, R(ECX), Imm16(0)); // value < 0 J_CC(CC_L, loop_neg); SetJumpTarget(end_pos); // g_dsp.r[reg] = tmp; MOV(16, M(&g_dsp.r[reg]), R(EAX)); SetJumpTarget(end); } // Decrease addr register according to the correspond ix register // EAX = g_dsp.r[reg] // ECX = g_dsp.r[DSP_REG_IX0 + reg] // EDX = g_dsp.r[DSP_REG_WR0 + reg] // EDI = tomask(EDX) void DSPEmitter::decrease_addr_reg(int reg) { MOV(16, R(ECX), M(&g_dsp.r[DSP_REG_IX0 + reg])); //IX0 == 0, bail out TEST(16, R(ECX), R(ECX)); FixupBranch end = J_CC(CC_Z); MOV(16, R(EAX), M(&g_dsp.r[reg])); MOV(16, R(EDX), M(&g_dsp.r[DSP_REG_WR0 + reg])); //IX0 > 0 FixupBranch neg = J_CC(CC_L); JumpTarget loop_pos = GetCodePtr(); //dsp_decrement // if ((tmp & wr_reg) == 0) TEST(16, R(EAX), R(EDX)); FixupBranch neg_nz = J_CC(CC_NZ); // tmp |= wr_reg; OR(16, R(EAX), R(EDX)); FixupBranch neg_z = J(); SetJumpTarget(neg_nz); // else tmp-- SUB(16, R(EAX), Imm16(1)); SetJumpTarget(neg_z); SUB(16, R(ECX), Imm16(1)); // value-- CMP(16, R(ECX), Imm16(0)); // value > 0 J_CC(CC_G, loop_pos); FixupBranch end_pos = J(); //else, IX0 < 0 SetJumpTarget(neg); //ToMask(WR0), calculating it into EDI MOV(16, R(EDI), R(EDX)); ToMask(EDI); JumpTarget loop_neg = GetCodePtr(); //dsp_increment // if ((tmp & tmb) == tmb) MOV(16, R(ESI), R(EAX)); AND(16, R(ESI), R(EDI)); CMP(16, R(ESI), R(EDI)); FixupBranch pos_neq = J_CC(CC_NE); // tmp ^= wr_reg XOR(16, R(EAX), R(EDX)); FixupBranch pos_eq = J(); SetJumpTarget(pos_neq); // else tmp++ ADD(16, R(EAX), Imm16(1)); SetJumpTarget(pos_eq); ADD(16, R(ECX), Imm16(1)); // value++ CMP(16, R(ECX), Imm16(0)); // value < 0 J_CC(CC_L, loop_neg); SetJumpTarget(end_pos); // g_dsp.r[reg] = tmp; MOV(16, M(&g_dsp.r[reg]), R(EAX)); SetJumpTarget(end); } // EAX - destination address (g_dsp.r[dest]) // ECX - value (g_dsp.r[src]) // ESI - the upper bits of the address (>> 12) void DSPEmitter::ext_dmem_write(u32 dest, u32 src) { // u16 addr = g_dsp.r[dest]; MOVZX(32, 16, EAX, M(&g_dsp.r[dest])); // u16 val = g_dsp.r[src]; MOVZX(32, 16, ECX, M(&g_dsp.r[src])); // u16 saddr = addr >> 12; MOV(32, R(ESI), R(EAX)); SHR(16, R(ESI), Imm8(12)); // if (saddr == 0) TEST(16, R(ESI), R(ESI)); FixupBranch ifx = J_CC(CC_NZ); // g_dsp.dram[addr & DSP_DRAM_MASK] = val; AND(16, R(EAX), Imm16(DSP_DRAM_MASK)); SHL(16, R(EAX), Imm8(1)); // * sizeof(u16) #ifdef _M_X64 MOV(64, R(R11), Imm64((u64)g_dsp.dram)); ADD(64, R(EAX), R(R11)); #else ADD(32, R(EAX), Imm32((u32)g_dsp.dram)); #endif MOV(16, MDisp(EAX,0), R(ECX)); FixupBranch end = J(); // else if (saddr == 0xf) SetJumpTarget(ifx); // Does it mean gdsp_ifx_write needs u32 rather than u16? ABI_CallFunctionRR((void *)gdsp_ifx_write, EAX, ECX); SetJumpTarget(end); } // EAX - the result of the read (used by caller) // ECX - the address to read // ESI - the upper bits of the address (>> 12) void DSPEmitter::ext_dmem_read(u16 addr) { // u16 addr = g_dsp.r[addr]; MOVZX(32, 16, ECX, M(&g_dsp.r[addr])); // u16 saddr = addr >> 12; MOV(32, R(ESI), R(ECX)); SHR(16, R(ESI), Imm8(12)); // if (saddr == 0) TEST(16, R(ESI), R(ESI)); FixupBranch dram = J_CC(CC_NZ); // return g_dsp.dram[addr & DSP_DRAM_MASK]; AND(16, R(ECX), Imm16(DSP_DRAM_MASK)); SHL(16, R(ECX), Imm8(1)); // * sizeof(u16) #ifdef _M_X64 MOV(64, R(R11), Imm64((u64)g_dsp.dram)); ADD(64, R(ECX), R(R11)); #else ADD(32, R(ECX), Imm32((u32)g_dsp.dram)); #endif MOV(16, R(EAX), MDisp(ECX,0)); FixupBranch end = J(); SetJumpTarget(dram); // else if (saddr == 0x1) CMP(16, R(ESI), Imm16(0x1)); FixupBranch ifx = J_CC(CC_NZ); // return g_dsp.coef[addr & DSP_COEF_MASK]; AND(16, R(ECX), Imm16(DSP_COEF_MASK)); SHL(16, R(ECX), Imm8(1)); // * sizeof(u16) #ifdef _M_X64 MOV(64, R(R11), Imm64((u64)g_dsp.dram)); ADD(64, R(ECX), R(R11)); #else ADD(32, R(ECX), Imm32((u32)g_dsp.dram)); #endif MOV(16, R(EAX), MDisp(ECX,0)); FixupBranch end2 = J(); SetJumpTarget(ifx); // else if (saddr == 0xf) // return gdsp_ifx_read(addr); ABI_CallFunctionR((void *)gdsp_ifx_read, ECX); SetJumpTarget(end); SetJumpTarget(end2); } #endif