/*==================================================================== filename: gdsp_opcodes_helper.h project: GameCube DSP Tool (gcdsp) created: 2005.03.04 mail: duddie@walla.com Copyright (c) 2005 Duddie 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 _DSP_INT_UTIL_H #define _DSP_INT_UTIL_H #include "Common.h" #include "DSPInterpreter.h" #include "DSPCore.h" #include "DSPMemoryMap.h" #include "DSPStacks.h" // --------------------------------------------------------------------------------------- // --- SR // --------------------------------------------------------------------------------------- inline void dsp_SR_set_flag(int flag) { g_dsp.r.sr |= flag; } inline bool dsp_SR_is_flag_set(int flag) { return (g_dsp.r.sr & flag) != 0; } // --------------------------------------------------------------------------------------- // --- AR increments, decrements // --------------------------------------------------------------------------------------- inline u16 dsp_increase_addr_reg(u16 reg, s16 _ix) { u32 ar = g_dsp.r.ar[reg]; u32 wr = g_dsp.r.wr[reg]; s32 ix = _ix; u32 mx = (wr | 1) << 1; u32 nar = ar + ix; u32 dar = (nar ^ ar ^ ix) & mx; if (ix >= 0) { if (dar > wr) //overflow nar -= wr + 1; } else { if ((((nar + wr + 1) ^ nar) & dar) <= wr) //underflow or below min for mask nar += wr + 1; } return nar; } inline u16 dsp_decrease_addr_reg(u16 reg, s16 _ix) { u32 ar = g_dsp.r.ar[reg]; u32 wr = g_dsp.r.wr[reg]; s32 ix = _ix; u32 mx = (wr | 1) << 1; u32 nar = ar - ix; u32 dar = (nar ^ ar ^ ~ix) & mx; if ((u32)ix > 0xFFFF8000) //(ix < 0 && ix != -0x8000) { if (dar > wr) //overflow nar -= wr + 1; } else { if ((((nar + wr + 1) ^ nar) & dar) <= wr) //underflow or below min for mask nar += wr + 1; } return nar; } inline u16 dsp_increment_addr_reg(u16 reg) { u32 ar = g_dsp.r.ar[reg]; u32 wr = g_dsp.r.wr[reg]; u32 nar = ar + 1; if ((nar ^ ar) > ((wr | 1) << 1)) nar -= wr + 1; return nar; } inline u16 dsp_decrement_addr_reg(u16 reg) { u32 ar = g_dsp.r.ar[reg]; u32 wr = g_dsp.r.wr[reg]; u32 nar = ar + wr; if (((nar ^ ar) & ((wr | 1) << 1)) > wr) nar -= wr + 1; return nar; } // --------------------------------------------------------------------------------------- // --- reg // --------------------------------------------------------------------------------------- inline u16 dsp_op_read_reg(int reg) { switch (reg & 0x1f) { case DSP_REG_ST0: case DSP_REG_ST1: case DSP_REG_ST2: case DSP_REG_ST3: return dsp_reg_load_stack(reg - DSP_REG_ST0); case DSP_REG_AR0: case DSP_REG_AR1: case DSP_REG_AR2: case DSP_REG_AR3: return g_dsp.r.ar[reg - DSP_REG_AR0]; case DSP_REG_IX0: case DSP_REG_IX1: case DSP_REG_IX2: case DSP_REG_IX3: return g_dsp.r.ix[reg - DSP_REG_IX0]; case DSP_REG_WR0: case DSP_REG_WR1: case DSP_REG_WR2: case DSP_REG_WR3: return g_dsp.r.wr[reg - DSP_REG_WR0]; case DSP_REG_ACH0: case DSP_REG_ACH1: return g_dsp.r.ac[reg - DSP_REG_ACH0].h; case DSP_REG_CR: return g_dsp.r.cr; case DSP_REG_SR: return g_dsp.r.sr; case DSP_REG_PRODL: return g_dsp.r.prod.l; case DSP_REG_PRODM: return g_dsp.r.prod.m; case DSP_REG_PRODH: return g_dsp.r.prod.h; case DSP_REG_PRODM2: return g_dsp.r.prod.m2; case DSP_REG_AXL0: case DSP_REG_AXL1: return g_dsp.r.ax[reg - DSP_REG_AXL0].l; case DSP_REG_AXH0: case DSP_REG_AXH1: return g_dsp.r.ax[reg - DSP_REG_AXH0].h; case DSP_REG_ACL0: case DSP_REG_ACL1: return g_dsp.r.ac[reg - DSP_REG_ACL0].l; case DSP_REG_ACM0: case DSP_REG_ACM1: return g_dsp.r.ac[reg - DSP_REG_ACM0].m; default: _assert_msg_(DSP_INT, 0, "cannot happen"); return 0; } } inline void dsp_op_write_reg(int reg, u16 val) { switch (reg & 0x1f) { // 8-bit sign extended registers. Should look at prod.h too... case DSP_REG_ACH0: case DSP_REG_ACH1: // sign extend from the bottom 8 bits. g_dsp.r.ac[reg-DSP_REG_ACH0].h = (u16)(s16)(s8)(u8)val; break; // Stack registers. case DSP_REG_ST0: case DSP_REG_ST1: case DSP_REG_ST2: case DSP_REG_ST3: dsp_reg_store_stack(reg - DSP_REG_ST0, val); break; case DSP_REG_AR0: case DSP_REG_AR1: case DSP_REG_AR2: case DSP_REG_AR3: g_dsp.r.ar[reg - DSP_REG_AR0] = val; break; case DSP_REG_IX0: case DSP_REG_IX1: case DSP_REG_IX2: case DSP_REG_IX3: g_dsp.r.ix[reg - DSP_REG_IX0] = val; break; case DSP_REG_WR0: case DSP_REG_WR1: case DSP_REG_WR2: case DSP_REG_WR3: g_dsp.r.wr[reg - DSP_REG_WR0] = val; break; case DSP_REG_CR: g_dsp.r.cr = val; break; case DSP_REG_SR: g_dsp.r.sr = val; break; case DSP_REG_PRODL: g_dsp.r.prod.l = val; break; case DSP_REG_PRODM: g_dsp.r.prod.m = val; break; case DSP_REG_PRODH: g_dsp.r.prod.h = val; break; case DSP_REG_PRODM2: g_dsp.r.prod.m2 = val; break; case DSP_REG_AXL0: case DSP_REG_AXL1: g_dsp.r.ax[reg - DSP_REG_AXL0].l = val; break; case DSP_REG_AXH0: case DSP_REG_AXH1: g_dsp.r.ax[reg - DSP_REG_AXH0].h = val; break; case DSP_REG_ACL0: case DSP_REG_ACL1: g_dsp.r.ac[reg - DSP_REG_ACL0].l = val; break; case DSP_REG_ACM0: case DSP_REG_ACM1: g_dsp.r.ac[reg - DSP_REG_ACM0].m = val; break; } } inline void dsp_conditional_extend_accum(int reg) { switch (reg) { case DSP_REG_ACM0: case DSP_REG_ACM1: if (g_dsp.r.sr & SR_40_MODE_BIT) { // Sign extend into whole accum. u16 val = g_dsp.r.ac[reg-DSP_REG_ACM0].m; g_dsp.r.ac[reg - DSP_REG_ACM0].h = (val & 0x8000) ? 0xFFFF : 0x0000; g_dsp.r.ac[reg - DSP_REG_ACM0].l = 0; } } } // --------------------------------------------------------------------------------------- // --- prod // --------------------------------------------------------------------------------------- inline s64 dsp_get_long_prod() { #if PROFILE ProfilerAddDelta(g_dsp.err_pc, 1); #endif s64 val = (s8)(u8)g_dsp.r.prod.h; val <<= 32; s64 low_prod = g_dsp.r.prod.m; low_prod += g_dsp.r.prod.m2; low_prod <<= 16; low_prod |= g_dsp.r.prod.l; val += low_prod; return val; } inline s64 dsp_get_long_prod_round_prodl() { s64 prod = dsp_get_long_prod(); if (prod & 0x10000) prod = (prod + 0x8000) & ~0xffff; else prod = (prod + 0x7fff) & ~0xffff; return prod; } // For accurate emulation, this is wrong - but the real prod registers behave // in completely bizarre ways. Not needed to emulate them correctly for game ucodes. inline void dsp_set_long_prod(s64 val) { #if PROFILE ProfilerAddDelta(g_dsp.err_pc, 1); #endif g_dsp.r.prod.l = (u16)val; val >>= 16; g_dsp.r.prod.m = (u16)val; val >>= 16; g_dsp.r.prod.h = /*(s16)(s8)*/(u8)val;//todo: check expansion g_dsp.r.prod.m2 = 0; } // --------------------------------------------------------------------------------------- // --- ACC - main accumulators (40-bit) // --------------------------------------------------------------------------------------- inline s64 dsp_get_long_acc(int reg) { #if PROFILE ProfilerAddDelta(g_dsp.err_pc, 1); #endif s64 high = (s64)(s8)g_dsp.r.ac[reg].h << 32; u32 mid_low = ((u32)g_dsp.r.ac[reg].m << 16) | g_dsp.r.ac[reg].l; return high | mid_low; } inline void dsp_set_long_acc(int _reg, s64 val) { #if PROFILE ProfilerAddDelta(g_dsp.err_pc, 1); #endif g_dsp.r.ac[_reg].l = (u16)val; val >>= 16; g_dsp.r.ac[_reg].m = (u16)val; val >>= 16; g_dsp.r.ac[_reg].h = (u16)(s16)(s8)(u8)val; } inline s64 dsp_convert_long_acc(s64 val) // s64 -> s40 { return ((s64)(s8)(val >> 32))<<32 | (u32)val; } inline s64 dsp_round_long_acc(s64 val) { if (val & 0x10000) val = (val + 0x8000) & ~0xffff; else val = (val + 0x7fff) & ~0xffff; return val; } inline s16 dsp_get_acc_l(int _reg) { return g_dsp.r.ac[_reg].l; } inline s16 dsp_get_acc_m(int _reg) { return g_dsp.r.ac[_reg].m; } inline s16 dsp_get_acc_h(int _reg) { return g_dsp.r.ac[_reg].h; } // --------------------------------------------------------------------------------------- // --- AX - extra accumulators (32-bit) // --------------------------------------------------------------------------------------- inline s32 dsp_get_long_acx(int _reg) { #if PROFILE ProfilerAddDelta(g_dsp.err_pc, 1); #endif return ((u32)g_dsp.r.ax[_reg].h << 16) | g_dsp.r.ax[_reg].l; } inline s16 dsp_get_ax_l(int _reg) { return (s16)g_dsp.r.ax[_reg].l; } inline s16 dsp_get_ax_h(int _reg) { return (s16)g_dsp.r.ax[_reg].h; } #endif