2013-12-19 17:10:14 +00:00
|
|
|
/*
|
|
|
|
Ugly, hacky, bad code
|
|
|
|
It decodes sh4 opcodes too
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "types.h"
|
2014-12-11 12:48:37 +00:00
|
|
|
|
2015-07-25 06:39:35 +00:00
|
|
|
#if FEAT_SHREC != DYNAREC_NONE
|
2014-12-11 12:48:37 +00:00
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
#include "decoder.h"
|
|
|
|
#include "shil.h"
|
|
|
|
#include "ngen.h"
|
|
|
|
#include "hw/sh4/sh4_opcode_list.h"
|
|
|
|
#include "hw/sh4/sh4_core.h"
|
|
|
|
#include "hw/sh4/sh4_mem.h"
|
|
|
|
#include "decoder_opcodes.h"
|
|
|
|
|
2015-07-28 17:02:49 +00:00
|
|
|
#define BLOCK_MAX_SH_OPS_SOFT 500
|
|
|
|
#define BLOCK_MAX_SH_OPS_HARD 511
|
|
|
|
|
2019-09-29 21:14:38 +00:00
|
|
|
static RuntimeBlockInfo* blk;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-09-29 21:14:38 +00:00
|
|
|
static const char idle_hash[] =
|
|
|
|
//BIOS
|
|
|
|
">:1:05:13B23363"
|
|
|
|
">:1:04:2E23A33B"
|
|
|
|
">:1:04:FB498832"
|
|
|
|
">:1:0A:50A249F9"
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-09-29 21:14:38 +00:00
|
|
|
//SC
|
|
|
|
">:1:0A:B4E90338"
|
|
|
|
">:1:04:11578A16"
|
|
|
|
">:1:04:C281CC52"
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-09-29 21:14:38 +00:00
|
|
|
//HH
|
|
|
|
">:1:07:0757DC10"
|
|
|
|
">:1:04:1476CC5E"
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-09-29 21:14:38 +00:00
|
|
|
//these look very suspicious, but I'm not sure about any of them
|
|
|
|
//cross testing w/ IKA makes them more suspects than not
|
|
|
|
">:1:0D:8C2921FF"
|
|
|
|
">:1:04:B806EEE4"
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-09-29 21:14:38 +00:00
|
|
|
// Dead or Alive 2
|
|
|
|
">:1:08:0A37187A";
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-08-30 21:35:10 +00:00
|
|
|
static inline shil_param mk_imm(u32 immv)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
return shil_param(FMT_IMM,immv);
|
|
|
|
}
|
2019-08-30 21:35:10 +00:00
|
|
|
|
|
|
|
static inline shil_param mk_reg(Sh4RegType reg)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
return shil_param(reg);
|
|
|
|
}
|
2019-08-30 21:35:10 +00:00
|
|
|
|
|
|
|
static inline shil_param mk_regi(int reg)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
return mk_reg((Sh4RegType)reg);
|
|
|
|
}
|
|
|
|
|
2019-09-29 21:14:38 +00:00
|
|
|
static state_t state;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-08-30 21:35:10 +00:00
|
|
|
static void Emit(shilop op,shil_param rd=shil_param(),shil_param rs1=shil_param(),shil_param rs2=shil_param(),u32 flags=0,shil_param rs3=shil_param(),shil_param rd2=shil_param())
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
shil_opcode sp;
|
|
|
|
|
|
|
|
sp.flags=flags;
|
|
|
|
sp.op=op;
|
|
|
|
sp.rd=(rd);
|
|
|
|
sp.rd2=(rd2);
|
|
|
|
sp.rs1=(rs1);
|
|
|
|
sp.rs2=(rs2);
|
|
|
|
sp.rs3=(rs3);
|
2019-03-25 10:53:13 +00:00
|
|
|
sp.guest_offs = state.cpu.rpc - blk->vaddr;
|
|
|
|
sp.delay_slot = state.cpu.is_delayslot;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
blk->oplist.push_back(sp);
|
|
|
|
}
|
|
|
|
|
2019-08-30 21:35:10 +00:00
|
|
|
static void dec_fallback(u32 op)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
shil_opcode opcd;
|
|
|
|
opcd.op=shop_ifb;
|
|
|
|
|
|
|
|
opcd.rs1=shil_param(FMT_IMM,OpDesc[op]->NeedPC());
|
|
|
|
|
|
|
|
opcd.rs2=shil_param(FMT_IMM,state.cpu.rpc+2);
|
|
|
|
opcd.rs3=shil_param(FMT_IMM,op);
|
2019-03-25 10:53:13 +00:00
|
|
|
|
|
|
|
opcd.guest_offs = state.cpu.rpc - blk->vaddr;
|
|
|
|
opcd.delay_slot = state.cpu.is_delayslot;
|
2013-12-19 17:10:14 +00:00
|
|
|
blk->oplist.push_back(opcd);
|
|
|
|
}
|
|
|
|
|
2019-08-30 21:35:10 +00:00
|
|
|
static void dec_DynamicSet(u32 regbase,u32 offs=0)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
if (offs==0)
|
|
|
|
Emit(shop_jdyn,reg_pc_dyn,mk_reg((Sh4RegType)regbase));
|
|
|
|
else
|
|
|
|
Emit(shop_jdyn,reg_pc_dyn,mk_reg((Sh4RegType)regbase),mk_imm(offs));
|
|
|
|
}
|
|
|
|
|
2019-08-30 21:35:10 +00:00
|
|
|
static void dec_End(u32 dst,BlockEndType flags,bool delay)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
if (state.ngen.OnlyDynamicEnds && flags == BET_StaticJump)
|
|
|
|
{
|
|
|
|
Emit(shop_mov32,mk_reg(reg_nextpc),mk_imm(dst));
|
|
|
|
dec_DynamicSet(reg_nextpc);
|
|
|
|
dec_End(0xFFFFFFFF,BET_DynamicJump,delay);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state.ngen.OnlyDynamicEnds)
|
|
|
|
{
|
|
|
|
verify(flags == BET_DynamicJump);
|
|
|
|
}
|
|
|
|
|
|
|
|
state.BlockType=flags;
|
|
|
|
state.NextOp=delay?NDO_Delayslot:NDO_End;
|
|
|
|
state.DelayOp=NDO_End;
|
|
|
|
state.JumpAddr=dst;
|
|
|
|
state.NextAddr=state.cpu.rpc+2+(delay?2:0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define GetN(str) ((str>>8) & 0xf)
|
|
|
|
#define GetM(str) ((str>>4) & 0xf)
|
|
|
|
#define GetImm4(str) ((str>>0) & 0xf)
|
|
|
|
#define GetImm8(str) ((str>>0) & 0xff)
|
|
|
|
#define GetSImm8(str) ((s8)((str>>0) & 0xff))
|
|
|
|
#define GetImm12(str) ((str>>0) & 0xfff)
|
|
|
|
#define GetSImm12(str) (((s16)((GetImm12(str))<<4))>>4)
|
|
|
|
|
|
|
|
|
|
|
|
#define SR_STATUS_MASK 0x700083F2
|
|
|
|
#define SR_T_MASK 1
|
|
|
|
|
2019-09-29 21:14:38 +00:00
|
|
|
static u32 dec_jump_simm8(u32 op)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
return state.cpu.rpc + GetSImm8(op)*2 + 4;
|
|
|
|
}
|
2019-09-29 21:14:38 +00:00
|
|
|
static u32 dec_jump_simm12(u32 op)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
return state.cpu.rpc + GetSImm12(op)*2 + 4;
|
|
|
|
}
|
2019-09-29 21:14:38 +00:00
|
|
|
static u32 dec_set_pr()
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
u32 retaddr=state.cpu.rpc + 4;
|
|
|
|
Emit(shop_mov32,reg_pr,mk_imm(retaddr));
|
|
|
|
return retaddr;
|
|
|
|
}
|
2019-09-29 21:14:38 +00:00
|
|
|
static void dec_write_sr(shil_param src)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
Emit(shop_and,mk_reg(reg_sr_status),src,mk_imm(SR_STATUS_MASK));
|
|
|
|
Emit(shop_and,mk_reg(reg_sr_T),src,mk_imm(SR_T_MASK));
|
|
|
|
}
|
|
|
|
//bf <bdisp8>
|
|
|
|
sh4dec(i1000_1011_iiii_iiii)
|
|
|
|
{
|
|
|
|
dec_End(dec_jump_simm8(op),BET_Cond_0,false);
|
|
|
|
}
|
|
|
|
//bf.s <bdisp8>
|
|
|
|
sh4dec(i1000_1111_iiii_iiii)
|
|
|
|
{
|
|
|
|
blk->has_jcond=true;
|
|
|
|
Emit(shop_jcond,reg_pc_dyn,reg_sr_T);
|
|
|
|
dec_End(dec_jump_simm8(op),BET_Cond_0,true);
|
|
|
|
}
|
|
|
|
//bt <bdisp8>
|
|
|
|
sh4dec(i1000_1001_iiii_iiii)
|
|
|
|
{
|
|
|
|
dec_End(dec_jump_simm8(op),BET_Cond_1,false);
|
|
|
|
}
|
|
|
|
//bt.s <bdisp8>
|
|
|
|
sh4dec(i1000_1101_iiii_iiii)
|
|
|
|
{
|
|
|
|
blk->has_jcond=true;
|
|
|
|
Emit(shop_jcond,reg_pc_dyn,reg_sr_T);
|
|
|
|
dec_End(dec_jump_simm8(op),BET_Cond_1,true);
|
|
|
|
}
|
|
|
|
//bra <bdisp12>
|
|
|
|
sh4dec(i1010_iiii_iiii_iiii)
|
|
|
|
{
|
|
|
|
dec_End(dec_jump_simm12(op),BET_StaticJump,true);
|
|
|
|
}
|
|
|
|
//braf <REG_N>
|
|
|
|
sh4dec(i0000_nnnn_0010_0011)
|
|
|
|
{
|
|
|
|
u32 n = GetN(op);
|
|
|
|
|
|
|
|
dec_DynamicSet(reg_r0+n,state.cpu.rpc + 4);
|
|
|
|
dec_End(0xFFFFFFFF,BET_DynamicJump,true);
|
|
|
|
}
|
|
|
|
//jmp @<REG_N>
|
|
|
|
sh4dec(i0100_nnnn_0010_1011)
|
|
|
|
{
|
|
|
|
u32 n = GetN(op);
|
|
|
|
|
|
|
|
dec_DynamicSet(reg_r0+n);
|
|
|
|
dec_End(0xFFFFFFFF,BET_DynamicJump,true);
|
|
|
|
}
|
|
|
|
//bsr <bdisp12>
|
|
|
|
sh4dec(i1011_iiii_iiii_iiii)
|
|
|
|
{
|
|
|
|
//TODO: set PR
|
|
|
|
dec_set_pr();
|
|
|
|
dec_End(dec_jump_simm12(op),BET_StaticCall,true);
|
|
|
|
}
|
|
|
|
//bsrf <REG_N>
|
|
|
|
sh4dec(i0000_nnnn_0000_0011)
|
|
|
|
{
|
|
|
|
u32 n = GetN(op);
|
|
|
|
//TODO: set PR
|
|
|
|
u32 retaddr=dec_set_pr();
|
|
|
|
dec_DynamicSet(reg_r0+n,retaddr);
|
|
|
|
dec_End(0xFFFFFFFF,BET_DynamicCall,true);
|
|
|
|
}
|
|
|
|
//jsr @<REG_N>
|
|
|
|
sh4dec(i0100_nnnn_0000_1011)
|
|
|
|
{
|
|
|
|
u32 n = GetN(op);
|
|
|
|
|
|
|
|
//TODO: Set pr
|
|
|
|
dec_set_pr();
|
|
|
|
dec_DynamicSet(reg_r0+n);
|
|
|
|
dec_End(0xFFFFFFFF,BET_DynamicCall,true);
|
|
|
|
}
|
|
|
|
//rts
|
|
|
|
sh4dec(i0000_0000_0000_1011)
|
|
|
|
{
|
|
|
|
dec_DynamicSet(reg_pr);
|
|
|
|
dec_End(0xFFFFFFFF,BET_DynamicRet,true);
|
|
|
|
}
|
|
|
|
//rte
|
|
|
|
sh4dec(i0000_0000_0010_1011)
|
|
|
|
{
|
|
|
|
//TODO: Write SR, Check intr
|
|
|
|
dec_write_sr(reg_ssr);
|
|
|
|
Emit(shop_sync_sr);
|
|
|
|
dec_DynamicSet(reg_spc);
|
|
|
|
dec_End(0xFFFFFFFF,BET_DynamicIntr,true);
|
|
|
|
}
|
|
|
|
//trapa #<imm>
|
|
|
|
sh4dec(i1100_0011_iiii_iiii)
|
|
|
|
{
|
|
|
|
//TODO: ifb
|
|
|
|
dec_fallback(op);
|
|
|
|
dec_DynamicSet(reg_nextpc);
|
|
|
|
dec_End(0xFFFFFFFF,BET_DynamicJump,false);
|
|
|
|
}
|
|
|
|
//sleep
|
|
|
|
sh4dec(i0000_0000_0001_1011)
|
|
|
|
{
|
|
|
|
//TODO: ifb
|
|
|
|
dec_fallback(op);
|
|
|
|
dec_DynamicSet(reg_nextpc);
|
|
|
|
dec_End(0xFFFFFFFF,BET_DynamicJump,false);
|
|
|
|
}
|
|
|
|
|
|
|
|
//ldc.l @<REG_N>+,SR
|
2019-03-25 10:53:13 +00:00
|
|
|
/*
|
2013-12-19 17:10:14 +00:00
|
|
|
sh4dec(i0100_nnnn_0000_0111)
|
|
|
|
{
|
|
|
|
u32 sr_t;
|
|
|
|
ReadMemU32(sr_t,r[n]);
|
|
|
|
if (sh4_exept_raised)
|
|
|
|
return;
|
|
|
|
sr.SetFull(sr_t);
|
|
|
|
r[n] += 4;
|
|
|
|
if (UpdateSR())
|
|
|
|
{
|
2013-12-24 00:56:44 +00:00
|
|
|
//FIXME only if interrupts got on .. :P
|
2013-12-19 17:10:14 +00:00
|
|
|
UpdateINTC();
|
2019-07-28 16:49:59 +00:00
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
dec_End(0xFFFFFFFF,BET_StaticIntr,false);
|
|
|
|
}
|
2019-03-25 10:53:13 +00:00
|
|
|
*/
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
//ldc <REG_N>,SR
|
|
|
|
sh4dec(i0100_nnnn_0000_1110)
|
|
|
|
{
|
|
|
|
u32 n = GetN(op);
|
|
|
|
|
|
|
|
dec_write_sr((Sh4RegType)(reg_r0+n));
|
|
|
|
Emit(shop_sync_sr);
|
|
|
|
dec_End(0xFFFFFFFF,BET_StaticIntr,false);
|
|
|
|
}
|
|
|
|
|
|
|
|
//nop !
|
|
|
|
sh4dec(i0000_0000_0000_1001)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-03-25 10:53:13 +00:00
|
|
|
//fschg
|
2013-12-19 17:10:14 +00:00
|
|
|
sh4dec(i1111_0011_1111_1101)
|
|
|
|
{
|
|
|
|
//fpscr.SZ is bit 20
|
|
|
|
Emit(shop_xor,reg_fpscr,reg_fpscr,mk_imm(1<<20));
|
|
|
|
state.cpu.FSZ64=!state.cpu.FSZ64;
|
|
|
|
}
|
|
|
|
|
|
|
|
//frchg
|
|
|
|
sh4dec(i1111_1011_1111_1101)
|
|
|
|
{
|
|
|
|
Emit(shop_xor,reg_fpscr,reg_fpscr,mk_imm(1<<21));
|
|
|
|
Emit(shop_mov32,reg_old_fpscr,reg_fpscr);
|
|
|
|
shil_param rmn;//null param
|
|
|
|
Emit(shop_frswap,regv_xmtrx,regv_fmtrx,regv_xmtrx,0,rmn,regv_fmtrx);
|
|
|
|
}
|
|
|
|
|
|
|
|
//not-so-elegant, but avoids extra opcodes and temporalities ..
|
|
|
|
//rotcl
|
|
|
|
sh4dec(i0100_nnnn_0010_0100)
|
|
|
|
{
|
|
|
|
u32 n = GetN(op);
|
|
|
|
Sh4RegType rn=(Sh4RegType)(reg_r0+n);
|
|
|
|
|
|
|
|
Emit(shop_rocl,rn,rn,reg_sr_T,0,shil_param(),reg_sr_T);
|
|
|
|
/*
|
|
|
|
Emit(shop_ror,rn,rn,mk_imm(31));
|
2013-12-24 00:56:44 +00:00
|
|
|
Emit(shop_xor,rn,rn,reg_sr_T); //Only affects last bit (swap part a)
|
|
|
|
Emit(shop_xor,reg_sr_T,reg_sr_T,rn); //srT -> rn
|
|
|
|
Emit(shop_and,reg_sr_T,reg_sr_T,mk_imm(1)); //Keep only last bit
|
|
|
|
Emit(shop_xor,rn,rn,reg_sr_T); //Only affects last bit (swap part b)
|
2013-12-19 17:10:14 +00:00
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
//rotcr
|
|
|
|
sh4dec(i0100_nnnn_0010_0101)
|
|
|
|
{
|
|
|
|
u32 n = GetN(op);
|
|
|
|
Sh4RegType rn=(Sh4RegType)(reg_r0+n);
|
|
|
|
|
|
|
|
Emit(shop_rocr,rn,rn,reg_sr_T,0,shil_param(),reg_sr_T);
|
|
|
|
/*
|
2013-12-24 00:56:44 +00:00
|
|
|
Emit(shop_xor,rn,rn,reg_sr_T); //Only affects last bit (swap part a)
|
|
|
|
Emit(shop_xor,reg_sr_T,reg_sr_T,rn); //srT -> rn
|
|
|
|
Emit(shop_and,reg_sr_T,reg_sr_T,mk_imm(1)); //Keep only last bit
|
|
|
|
Emit(shop_xor,rn,rn,reg_sr_T); //Only affects last bit (swap part b)
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
Emit(shop_ror,rn,rn,mk_imm(1));
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2019-09-29 21:14:38 +00:00
|
|
|
static const Sh4RegType SREGS[] =
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
reg_mach,
|
|
|
|
reg_macl,
|
|
|
|
reg_pr,
|
|
|
|
reg_sgr,
|
|
|
|
NoReg,
|
|
|
|
reg_fpul,
|
|
|
|
reg_fpscr,
|
|
|
|
NoReg,
|
|
|
|
|
|
|
|
NoReg,
|
|
|
|
NoReg,
|
|
|
|
NoReg,
|
|
|
|
NoReg,
|
|
|
|
NoReg,
|
|
|
|
NoReg,
|
|
|
|
NoReg,
|
|
|
|
reg_dbr,
|
|
|
|
};
|
|
|
|
|
2019-09-29 21:14:38 +00:00
|
|
|
static const Sh4RegType CREGS[] =
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
reg_sr,
|
|
|
|
reg_gbr,
|
|
|
|
reg_vbr,
|
|
|
|
reg_ssr,
|
|
|
|
reg_spc,
|
|
|
|
NoReg,
|
|
|
|
NoReg,
|
|
|
|
NoReg,
|
|
|
|
|
|
|
|
reg_r0_Bank,
|
|
|
|
reg_r1_Bank,
|
|
|
|
reg_r2_Bank,
|
|
|
|
reg_r3_Bank,
|
|
|
|
reg_r4_Bank,
|
|
|
|
reg_r5_Bank,
|
|
|
|
reg_r6_Bank,
|
|
|
|
reg_r7_Bank,
|
|
|
|
};
|
|
|
|
|
2019-08-30 21:35:10 +00:00
|
|
|
static void dec_param(DecParam p,shil_param& r1,shil_param& r2, u32 op)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
switch(p)
|
|
|
|
{
|
|
|
|
//constants
|
|
|
|
case PRM_PC_D8_x2:
|
|
|
|
r1=mk_imm((state.cpu.rpc+4)+(GetImm8(op)<<1));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_PC_D8_x4:
|
|
|
|
r1=mk_imm(((state.cpu.rpc+4)&0xFFFFFFFC)+(GetImm8(op)<<2));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_ZERO:
|
|
|
|
r1= mk_imm(0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_ONE:
|
|
|
|
r1= mk_imm(1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_TWO:
|
|
|
|
r1= mk_imm(2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_TWO_INV:
|
|
|
|
r1= mk_imm(~2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_ONE_F32:
|
|
|
|
r1= mk_imm(0x3f800000);
|
|
|
|
break;
|
|
|
|
|
|
|
|
//imms
|
|
|
|
case PRM_SIMM8:
|
|
|
|
r1=mk_imm(GetSImm8(op));
|
|
|
|
break;
|
|
|
|
case PRM_UIMM8:
|
|
|
|
r1=mk_imm(GetImm8(op));
|
|
|
|
break;
|
|
|
|
|
|
|
|
//direct registers
|
|
|
|
case PRM_R0:
|
|
|
|
r1=mk_reg(reg_r0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_RN:
|
|
|
|
r1=mk_regi(reg_r0+GetN(op));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_RM:
|
|
|
|
r1=mk_regi(reg_r0+GetM(op));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_FRN_SZ:
|
|
|
|
if (state.cpu.FSZ64)
|
|
|
|
{
|
|
|
|
int rx=GetN(op)/2;
|
|
|
|
if (GetN(op)&1)
|
|
|
|
rx+=regv_xd_0;
|
|
|
|
else
|
|
|
|
rx+=regv_dr_0;
|
|
|
|
|
|
|
|
r1=mk_regi(rx);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case PRM_FRN:
|
|
|
|
r1=mk_regi(reg_fr_0+GetN(op));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_FRM_SZ:
|
|
|
|
if (state.cpu.FSZ64)
|
|
|
|
{
|
|
|
|
int rx=GetM(op)/2;
|
|
|
|
if (GetM(op)&1)
|
|
|
|
rx+=regv_xd_0;
|
|
|
|
else
|
|
|
|
rx+=regv_dr_0;
|
|
|
|
|
|
|
|
r1=mk_regi(rx);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case PRM_FRM:
|
|
|
|
r1=mk_regi(reg_fr_0+GetM(op));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_FPUL:
|
|
|
|
r1=mk_regi(reg_fpul);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_FPN: //float pair, 3 bits
|
|
|
|
r1=mk_regi(regv_dr_0+GetN(op)/2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_FVN: //float quad, 2 bits
|
|
|
|
r1=mk_regi(regv_fv_0+GetN(op)/4);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_FVM: //float quad, 2 bits
|
|
|
|
r1=mk_regi(regv_fv_0+(GetN(op)&0x3));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_XMTRX: //float matrix, 0 bits
|
|
|
|
r1=mk_regi(regv_xmtrx);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_FRM_FR0:
|
|
|
|
r1=mk_regi(reg_fr_0+GetM(op));
|
|
|
|
r2=mk_regi(reg_fr_0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_SR_T:
|
|
|
|
r1=mk_regi(reg_sr_T);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_SR_STATUS:
|
|
|
|
r1=mk_regi(reg_sr_status);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_SREG: //FPUL/FPSCR/MACH/MACL/PR/DBR/SGR
|
|
|
|
r1=mk_regi(SREGS[GetM(op)]);
|
|
|
|
break;
|
|
|
|
case PRM_CREG: //SR/GBR/VBR/SSR/SPC/<RM_BANK>
|
|
|
|
r1=mk_regi(CREGS[GetM(op)]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
//reg/imm reg/reg
|
|
|
|
case PRM_RN_D4_x1:
|
|
|
|
case PRM_RN_D4_x2:
|
|
|
|
case PRM_RN_D4_x4:
|
|
|
|
{
|
|
|
|
u32 shft=p-PRM_RN_D4_x1;
|
|
|
|
r1=mk_regi(reg_r0+GetN(op));
|
|
|
|
r2=mk_imm(GetImm4(op)<<shft);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_RN_R0:
|
|
|
|
r1=mk_regi(reg_r0+GetN(op));
|
|
|
|
r2=mk_regi(reg_r0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_RM_D4_x1:
|
|
|
|
case PRM_RM_D4_x2:
|
|
|
|
case PRM_RM_D4_x4:
|
|
|
|
{
|
|
|
|
u32 shft=p-PRM_RM_D4_x1;
|
|
|
|
r1=mk_regi(reg_r0+GetM(op));
|
|
|
|
r2=mk_imm(GetImm4(op)<<shft);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_RM_R0:
|
|
|
|
r1=mk_regi(reg_r0+GetM(op));
|
|
|
|
r2=mk_regi(reg_r0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRM_GBR_D8_x1:
|
|
|
|
case PRM_GBR_D8_x2:
|
|
|
|
case PRM_GBR_D8_x4:
|
|
|
|
{
|
|
|
|
u32 shft=p-PRM_GBR_D8_x1;
|
|
|
|
r1=mk_regi(reg_gbr);
|
|
|
|
r2=mk_imm(GetImm8(op)<<shft);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-12-24 00:56:44 +00:00
|
|
|
die("Non-supported parameter used");
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MASK_N_M 0xF00F
|
|
|
|
#define MASK_N 0xF0FF
|
|
|
|
#define MASK_NONE 0xFFFF
|
|
|
|
|
|
|
|
#define DIV0U_KEY 0x0019
|
|
|
|
#define DIV0S_KEY 0x2007
|
|
|
|
#define DIV1_KEY 0x3004
|
|
|
|
#define ROTCL_KEY 0x4024
|
|
|
|
|
2019-09-29 21:14:38 +00:00
|
|
|
static Sh4RegType div_som_reg1;
|
|
|
|
static Sh4RegType div_som_reg2;
|
|
|
|
static Sh4RegType div_som_reg3;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-08-30 21:35:10 +00:00
|
|
|
static u32 MatchDiv32(u32 pc , Sh4RegType ®1,Sh4RegType ®2 , Sh4RegType ®3)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
u32 v_pc=pc;
|
|
|
|
u32 match=1;
|
|
|
|
for (int i=0;i<32;i++)
|
|
|
|
{
|
2019-03-25 10:53:13 +00:00
|
|
|
u16 opcode=IReadMem16(v_pc);
|
2013-12-19 17:10:14 +00:00
|
|
|
v_pc+=2;
|
|
|
|
if ((opcode&MASK_N)==ROTCL_KEY)
|
|
|
|
{
|
|
|
|
if (reg1==NoReg)
|
|
|
|
reg1=(Sh4RegType)GetN(opcode);
|
|
|
|
else if (reg1!=(Sh4RegType)GetN(opcode))
|
|
|
|
break;
|
|
|
|
match++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-03-22 00:26:43 +00:00
|
|
|
//printf("DIV MATCH BROKEN BY: %s\n",OpDesc[opcode]->diss);
|
2013-12-19 17:10:14 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-03-25 10:53:13 +00:00
|
|
|
opcode=IReadMem16(v_pc);
|
2013-12-19 17:10:14 +00:00
|
|
|
v_pc+=2;
|
|
|
|
if ((opcode&MASK_N_M)==DIV1_KEY)
|
|
|
|
{
|
|
|
|
if (reg2==NoReg)
|
|
|
|
reg2=(Sh4RegType)GetM(opcode);
|
|
|
|
else if (reg2!=(Sh4RegType)GetM(opcode))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (reg2==reg1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (reg3==NoReg)
|
|
|
|
reg3=(Sh4RegType)GetN(opcode);
|
|
|
|
else if (reg3!=(Sh4RegType)GetN(opcode))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (reg3==reg1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
match++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return match;
|
|
|
|
}
|
2019-08-30 21:35:10 +00:00
|
|
|
|
|
|
|
static bool MatchDiv32u(u32 op,u32 pc)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-09-20 15:28:41 +00:00
|
|
|
if (settings.dynarec.safemode)
|
2018-07-06 07:49:39 +00:00
|
|
|
return false;
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
div_som_reg1=NoReg;
|
|
|
|
div_som_reg2=NoReg;
|
|
|
|
div_som_reg3=NoReg;
|
|
|
|
|
|
|
|
u32 match=MatchDiv32(pc+2,div_som_reg1,div_som_reg2,div_som_reg3);
|
|
|
|
|
|
|
|
|
|
|
|
//log("DIV32U matched %d%% @ 0x%X\n",match*100/65,pc);
|
|
|
|
if (match==65)
|
|
|
|
{
|
|
|
|
//DIV32U was perfectly matched :)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else //no match ...
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-30 21:35:10 +00:00
|
|
|
static bool MatchDiv32s(u32 op,u32 pc)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-09-20 15:28:41 +00:00
|
|
|
if (settings.dynarec.safemode)
|
2018-07-06 07:49:39 +00:00
|
|
|
return false;
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
u32 n = GetN(op);
|
|
|
|
u32 m = GetM(op);
|
|
|
|
|
|
|
|
div_som_reg1=NoReg;
|
|
|
|
div_som_reg2=(Sh4RegType)m;
|
|
|
|
div_som_reg3=(Sh4RegType)n;
|
|
|
|
|
|
|
|
u32 match=MatchDiv32(pc+2,div_som_reg1,div_som_reg2,div_som_reg3);
|
2018-07-10 12:36:28 +00:00
|
|
|
//printf("DIV32S matched %d%% @ 0x%X\n",match*100/65,pc);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
if (match==65)
|
|
|
|
{
|
|
|
|
//DIV32S was perfectly matched :)
|
2018-07-10 12:36:28 +00:00
|
|
|
//printf("div32s %d/%d/%d\n",div_som_reg1,div_som_reg2,div_som_reg3);
|
2013-12-19 17:10:14 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else //no match ...
|
|
|
|
{
|
|
|
|
/*
|
2019-03-25 10:53:13 +00:00
|
|
|
printf("%04X\n",IReadMem16(pc-2));
|
|
|
|
printf("%04X\n",IReadMem16(pc-0));
|
|
|
|
printf("%04X\n",IReadMem16(pc+2));
|
|
|
|
printf("%04X\n",IReadMem16(pc+4));
|
|
|
|
printf("%04X\n",IReadMem16(pc+6));*/
|
2013-12-19 17:10:14 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
//This ended up too rare (and too hard to match)
|
|
|
|
bool MatchDiv0S_0(u32 pc)
|
|
|
|
{
|
2019-03-25 10:53:13 +00:00
|
|
|
if (IReadMem16(pc+0)==0x233A && //XOR r3,r3
|
|
|
|
IReadMem16(pc+2)==0x2137 && //DIV0S r3,r1
|
|
|
|
IReadMem16(pc+4)==0x322A && //SUBC r2,r2
|
|
|
|
IReadMem16(pc+6)==0x313A && //SUBC r3,r1
|
|
|
|
(IReadMem16(pc+8)&0xF00F)==0x2007) //DIV0S x,x
|
2013-12-19 17:10:14 +00:00
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2019-08-30 21:35:10 +00:00
|
|
|
static bool dec_generic(u32 op)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
DecMode mode;DecParam d;DecParam s;shilop natop;u32 e;
|
|
|
|
if (OpDesc[op]->decode==0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
u64 inf=OpDesc[op]->decode;
|
|
|
|
|
|
|
|
e=(u32)(inf>>32);
|
|
|
|
mode=(DecMode)((inf>>24)&0xFF);
|
|
|
|
d=(DecParam)((inf>>16)&0xFF);
|
|
|
|
s=(DecParam)((inf>>8)&0xFF);
|
|
|
|
natop=(shilop)((inf>>0)&0xFF);
|
|
|
|
|
|
|
|
bool transfer_64=false;
|
|
|
|
if (op>=0xF000)
|
|
|
|
{
|
|
|
|
state.info.has_fpu=true;
|
2013-12-24 00:56:44 +00:00
|
|
|
//return false;//FPU off for now
|
2013-12-19 17:10:14 +00:00
|
|
|
if (state.cpu.FPR64 /*|| state.cpu.FSZ64*/)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (state.cpu.FSZ64 && (d==PRM_FRN_SZ || d==PRM_FRM_SZ || s==PRM_FRN_SZ || s==PRM_FRM_SZ))
|
2019-08-30 21:35:10 +00:00
|
|
|
transfer_64 = true;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
shil_param rs1,rs2,rs3,rd;
|
|
|
|
|
|
|
|
dec_param(s,rs2,rs3,op);
|
|
|
|
dec_param(d,rs1,rs3,op);
|
|
|
|
|
|
|
|
switch(mode)
|
|
|
|
{
|
|
|
|
case DM_ReadSRF:
|
|
|
|
Emit(shop_mov32,rs1,reg_sr_status);
|
|
|
|
Emit(shop_or,rs1,rs1,reg_sr_T);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DM_WriteTOp:
|
|
|
|
Emit(natop,reg_sr_T,rs1,rs2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DM_DT:
|
|
|
|
verify(natop==shop_sub);
|
|
|
|
Emit(natop,rs1,rs1,rs2);
|
|
|
|
Emit(shop_seteq,mk_reg(reg_sr_T),rs1,mk_imm(0));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DM_Shift:
|
|
|
|
if (natop==shop_shl && e==1)
|
|
|
|
Emit(shop_shr,mk_reg(reg_sr_T),rs1,mk_imm(31));
|
|
|
|
else if (e==1)
|
|
|
|
Emit(shop_and,mk_reg(reg_sr_T),rs1,mk_imm(1));
|
|
|
|
|
|
|
|
Emit(natop,rs1,rs1,mk_imm(e));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DM_Rot:
|
|
|
|
if (!(((s32)e>=0?e:-e)&0x1000))
|
|
|
|
{
|
|
|
|
if ((s32)e<0)
|
|
|
|
{
|
2013-12-24 00:56:44 +00:00
|
|
|
//left rotate
|
2013-12-19 17:10:14 +00:00
|
|
|
Emit(shop_shr,mk_reg(reg_sr_T),rs2,mk_imm(31));
|
|
|
|
e=-e;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//right rotate
|
|
|
|
Emit(shop_and,mk_reg(reg_sr_T),rs2,mk_imm(1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
e&=31;
|
|
|
|
|
|
|
|
Emit(natop,rs1,rs2,mk_imm(e));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DM_BinaryOp://d=d op s
|
|
|
|
if (e&1)
|
|
|
|
Emit(natop,rs1,rs1,rs2,0,rs3);
|
|
|
|
else
|
|
|
|
Emit(natop,shil_param(),rs1,rs2,0,rs3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DM_UnaryOp: //d= op s
|
|
|
|
if (transfer_64 && natop==shop_mov32)
|
|
|
|
natop=shop_mov64;
|
|
|
|
|
|
|
|
if (natop==shop_cvt_i2f_n && state.cpu.RoundToZero)
|
|
|
|
natop=shop_cvt_i2f_z;
|
|
|
|
|
|
|
|
if (e&1)
|
|
|
|
Emit(natop,shil_param(),rs1);
|
|
|
|
else
|
|
|
|
Emit(natop,rs1,rs2);
|
|
|
|
break;
|
|
|
|
|
2013-12-24 00:56:44 +00:00
|
|
|
case DM_WriteM: //write(d,s)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
//0 has no effect, so get rid of it
|
|
|
|
if (rs3.is_imm() && rs3._imm==0)
|
|
|
|
rs3=shil_param();
|
|
|
|
|
|
|
|
state.info.has_writem=true;
|
|
|
|
if (transfer_64) e=(s32)e*2;
|
|
|
|
bool update_after=false;
|
|
|
|
if ((s32)e<0)
|
|
|
|
{
|
2019-04-28 18:41:39 +00:00
|
|
|
if (rs1._reg!=rs2._reg && !mmu_enabled()) //reg shouldn't be updated if its written
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2019-04-28 18:41:39 +00:00
|
|
|
Emit(shop_sub,rs1,rs1,mk_imm(-e));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
verify(rs3.is_null());
|
|
|
|
rs3=mk_imm(e);
|
|
|
|
update_after=true;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Emit(shop_writem,shil_param(),rs1,rs2,(s32)e<0?-e:e,rs3);
|
|
|
|
|
|
|
|
if (update_after)
|
|
|
|
{
|
|
|
|
Emit(shop_sub,rs1,rs1,mk_imm(-e));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DM_ReadM:
|
|
|
|
//0 has no effect, so get rid of it
|
|
|
|
if (rs3.is_imm() && rs3._imm==0)
|
|
|
|
rs3=shil_param();
|
|
|
|
|
|
|
|
state.info.has_readm=true;
|
|
|
|
if (transfer_64) e=(s32)e*2;
|
|
|
|
|
|
|
|
Emit(shop_readm,rs1,rs2,shil_param(),(s32)e<0?-e:e,rs3);
|
|
|
|
if ((s32)e<0)
|
|
|
|
{
|
2013-12-24 00:56:44 +00:00
|
|
|
if (rs1._reg!=rs2._reg)//the reg shouldn't be updated if it was just read.
|
2013-12-19 17:10:14 +00:00
|
|
|
Emit(shop_add,rs2,rs2,mk_imm(-e));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DM_fiprOp:
|
|
|
|
{
|
|
|
|
shil_param rdd=mk_regi(rs1._reg+3);
|
|
|
|
Emit(natop,rdd,rs1,rs2);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DM_EXTOP:
|
|
|
|
{
|
|
|
|
Emit(natop,rs1,rs2,mk_imm(e==1?0xFF:0xFFFF));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DM_MUL:
|
|
|
|
{
|
|
|
|
shilop op;
|
|
|
|
shil_param rd=mk_reg(reg_macl);
|
|
|
|
shil_param rd2=shil_param();
|
|
|
|
|
|
|
|
switch((s32)e)
|
|
|
|
{
|
2013-12-24 00:56:44 +00:00
|
|
|
case 16: op=shop_mul_u16; break;
|
|
|
|
case -16: op=shop_mul_s16; break;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2013-12-24 00:56:44 +00:00
|
|
|
case -32: op=shop_mul_i32; break;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2013-12-24 00:56:44 +00:00
|
|
|
case 64: op=shop_mul_u64; rd2 = mk_reg(reg_mach); break;
|
|
|
|
case -64: op=shop_mul_s64; rd2 = mk_reg(reg_mach); break;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
die("DM_MUL: Failed to classify opcode");
|
|
|
|
}
|
|
|
|
|
|
|
|
Emit(op,rd,rs1,rs2,0,shil_param(),rd2);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DM_DIV0:
|
|
|
|
{
|
|
|
|
if (e==1)
|
|
|
|
{
|
|
|
|
if (MatchDiv32u(op,state.cpu.rpc))
|
|
|
|
{
|
|
|
|
verify(!state.cpu.is_delayslot);
|
|
|
|
//div32u
|
2019-06-13 16:27:21 +00:00
|
|
|
Emit(shop_div32u, mk_reg(div_som_reg1), mk_reg(div_som_reg1), mk_reg(div_som_reg2), 0, mk_reg(div_som_reg3), mk_reg(div_som_reg3));
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-06-13 16:27:21 +00:00
|
|
|
Emit(shop_and, mk_reg(reg_sr_T), mk_reg(div_som_reg1), mk_imm(1));
|
|
|
|
Emit(shop_shr, mk_reg(div_som_reg1), mk_reg(div_som_reg1), mk_imm(1));
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-06-13 16:27:21 +00:00
|
|
|
Emit(shop_div32p2, mk_reg(div_som_reg3), mk_reg(div_som_reg3), mk_reg(div_som_reg2), 0, mk_reg(reg_sr_T));
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
//skip the aggregated opcodes
|
2019-06-13 16:27:21 +00:00
|
|
|
state.cpu.rpc += 128;
|
|
|
|
blk->guest_cycles += CPU_RATIO*64;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-12-24 00:56:44 +00:00
|
|
|
//clear QM (bits 8,9)
|
2013-12-19 17:10:14 +00:00
|
|
|
u32 qm=(1<<8)|(1<<9);
|
|
|
|
Emit(shop_and,mk_reg(reg_sr_status),mk_reg(reg_sr_status),mk_imm(~qm));
|
|
|
|
//clear T !
|
|
|
|
Emit(shop_mov32,mk_reg(reg_sr_T),mk_imm(0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (MatchDiv32s(op,state.cpu.rpc))
|
|
|
|
{
|
|
|
|
verify(!state.cpu.is_delayslot);
|
|
|
|
//div32s
|
2019-06-13 16:27:21 +00:00
|
|
|
Emit(shop_div32s, mk_reg(div_som_reg1), mk_reg(div_som_reg1), mk_reg(div_som_reg2), 0, mk_reg(div_som_reg3), mk_reg(div_som_reg3));
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-06-13 16:27:21 +00:00
|
|
|
Emit(shop_and, mk_reg(reg_sr_T), mk_reg(div_som_reg1), mk_imm((1 << 31) | 1)); // set lsb and sign of quotient in T
|
|
|
|
Emit(shop_sar, mk_reg(div_som_reg1), mk_reg(div_som_reg1), mk_imm(1)); // shift quotient right
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-06-13 16:27:21 +00:00
|
|
|
Emit(shop_div32p2, mk_reg(div_som_reg3), mk_reg(div_som_reg3), mk_reg(div_som_reg2), 0, mk_reg(reg_sr_T));
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-06-13 16:27:21 +00:00
|
|
|
Emit(shop_and, mk_reg(reg_sr_T), mk_reg(reg_sr_T), mk_imm(1)); // clean up T
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
//skip the aggregated opcodes
|
|
|
|
state.cpu.rpc+=128;
|
2019-06-13 16:27:21 +00:00
|
|
|
blk->guest_cycles += CPU_RATIO * 64;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//sr.Q=r[n]>>31;
|
|
|
|
//sr.M=r[m]>>31;
|
|
|
|
//sr.T=sr.M^sr.Q;
|
|
|
|
|
|
|
|
//This is nasty because there isn't a temp reg ..
|
|
|
|
//VERY NASTY
|
|
|
|
|
|
|
|
//Clear Q & M
|
|
|
|
Emit(shop_and,mk_reg(reg_sr_status),mk_reg(reg_sr_status),mk_imm(~((1<<8)|(1<<9))));
|
|
|
|
|
|
|
|
//sr.Q=r[n]>>31;
|
|
|
|
Emit(shop_sar,mk_reg(reg_sr_T),rs1,mk_imm(31));
|
|
|
|
Emit(shop_and,mk_reg(reg_sr_T),mk_reg(reg_sr_T),mk_imm(1<<8));
|
|
|
|
Emit(shop_or,mk_reg(reg_sr_status),mk_reg(reg_sr_status),mk_reg(reg_sr_T));
|
|
|
|
|
|
|
|
//sr.M=r[m]>>31;
|
|
|
|
Emit(shop_sar,mk_reg(reg_sr_T),rs2,mk_imm(31));
|
|
|
|
Emit(shop_and,mk_reg(reg_sr_T),mk_reg(reg_sr_T),mk_imm(1<<9));
|
|
|
|
Emit(shop_or,mk_reg(reg_sr_status),mk_reg(reg_sr_status),mk_reg(reg_sr_T));
|
|
|
|
|
|
|
|
//sr.T=sr.M^sr.Q;
|
|
|
|
Emit(shop_xor,mk_reg(reg_sr_T),rs1,rs2);
|
|
|
|
Emit(shop_shr,mk_reg(reg_sr_T),mk_reg(reg_sr_T),mk_imm(31));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DM_ADC:
|
|
|
|
{
|
|
|
|
Emit(natop,rs1,rs1,rs2,0,mk_reg(reg_sr_T),mk_reg(reg_sr_T));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-05-26 11:30:05 +00:00
|
|
|
case DM_NEGC:
|
|
|
|
Emit(natop, rs1, rs2, mk_reg(reg_sr_T), 0, shil_param(), mk_reg(reg_sr_T));
|
|
|
|
break;
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
default:
|
|
|
|
verify(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-09-29 21:14:38 +00:00
|
|
|
static void state_Setup(u32 rpc,fpscr_t fpu_cfg)
|
2018-09-02 13:49:23 +00:00
|
|
|
{
|
|
|
|
state.cpu.rpc=rpc;
|
|
|
|
state.cpu.is_delayslot=false;
|
|
|
|
state.cpu.FPR64=fpu_cfg.PR;
|
|
|
|
state.cpu.FSZ64=fpu_cfg.SZ;
|
|
|
|
state.cpu.RoundToZero=fpu_cfg.RM==1;
|
2019-03-25 10:53:13 +00:00
|
|
|
//verify(fpu_cfg.RM<2); // Happens with many wince games (set to 3)
|
2018-09-02 13:49:23 +00:00
|
|
|
//what about fp/fs ?
|
|
|
|
|
|
|
|
state.NextOp=NDO_NextOp;
|
|
|
|
state.BlockType=BET_SCL_Intr;
|
|
|
|
state.JumpAddr=0xFFFFFFFF;
|
|
|
|
state.NextAddr=0xFFFFFFFF;
|
|
|
|
|
|
|
|
state.info.has_readm=false;
|
|
|
|
state.info.has_writem=false;
|
|
|
|
state.info.has_fpu=false;
|
|
|
|
}
|
|
|
|
|
2019-04-15 16:02:34 +00:00
|
|
|
bool dec_DecodeBlock(RuntimeBlockInfo* rbi,u32 max_cycles)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
blk=rbi;
|
2019-03-25 10:53:13 +00:00
|
|
|
state_Setup(blk->vaddr, blk->fpu_cfg);
|
2013-12-19 17:10:14 +00:00
|
|
|
ngen_GetFeatures(&state.ngen);
|
|
|
|
|
|
|
|
blk->guest_opcodes=0;
|
2019-04-15 16:02:34 +00:00
|
|
|
// If full MMU, don't allow the block to extend past the end of the current 4K page
|
|
|
|
u32 max_pc = mmu_enabled() ? ((state.cpu.rpc >> 12) + 1) << 12 : 0xFFFFFFFF;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
switch(state.NextOp)
|
|
|
|
{
|
|
|
|
case NDO_Delayslot:
|
|
|
|
state.NextOp=state.DelayOp;
|
|
|
|
state.cpu.is_delayslot=true;
|
|
|
|
//there is no break here by design
|
|
|
|
case NDO_NextOp:
|
|
|
|
{
|
2019-04-15 16:02:34 +00:00
|
|
|
if ((blk->oplist.size() >= BLOCK_MAX_SH_OPS_SOFT || blk->guest_cycles >= max_cycles || state.cpu.rpc >= max_pc)
|
|
|
|
&& !state.cpu.is_delayslot)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
dec_End(state.cpu.rpc,BET_StaticJump,false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-06-13 16:36:08 +00:00
|
|
|
u32 op = IReadMem16(state.cpu.rpc);
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
if (op==0 && state.cpu.is_delayslot)
|
|
|
|
{
|
2019-07-01 10:17:51 +00:00
|
|
|
INFO_LOG(DYNAREC, "Delayslot 0 hack!");
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
blk->guest_opcodes++;
|
2019-05-14 10:38:56 +00:00
|
|
|
if (!mmu_enabled())
|
|
|
|
{
|
|
|
|
if (op>=0xF000)
|
|
|
|
blk->guest_cycles+=0;
|
|
|
|
else
|
|
|
|
blk->guest_cycles+=CPU_RATIO;
|
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
else
|
2019-05-14 10:38:56 +00:00
|
|
|
{
|
|
|
|
blk->guest_cycles += max((int)OpDesc[op]->LatencyCycles, 1);
|
|
|
|
}
|
2019-03-25 10:53:13 +00:00
|
|
|
if (OpDesc[op]->IsFloatingPoint())
|
2019-04-18 11:55:10 +00:00
|
|
|
{
|
|
|
|
if (sr.FD == 1)
|
|
|
|
{
|
|
|
|
// We need to know FPSCR to compile the block, so let the exception handler run first
|
|
|
|
// as it may change the fp registers
|
|
|
|
Do_Exception(next_pc, 0x800, 0x100);
|
|
|
|
return false;
|
|
|
|
}
|
2019-03-25 10:53:13 +00:00
|
|
|
blk->has_fpu_op = true;
|
2019-04-18 11:55:10 +00:00
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
verify(!(state.cpu.is_delayslot && OpDesc[op]->SetPC()));
|
|
|
|
if (state.ngen.OnlyDynamicEnds || !OpDesc[op]->rec_oph)
|
|
|
|
{
|
|
|
|
if (state.ngen.InterpreterFallback || !dec_generic(op))
|
|
|
|
{
|
|
|
|
dec_fallback(op);
|
|
|
|
if (OpDesc[op]->SetPC())
|
|
|
|
{
|
|
|
|
dec_DynamicSet(reg_nextpc);
|
|
|
|
dec_End(0xFFFFFFFF,BET_DynamicJump,false);
|
|
|
|
}
|
|
|
|
if (OpDesc[op]->SetFPSCR() && !state.cpu.is_delayslot)
|
|
|
|
{
|
|
|
|
dec_End(state.cpu.rpc+2,BET_StaticJump,false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
OpDesc[op]->rec_oph(op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
state.cpu.rpc+=2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NDO_Jump:
|
|
|
|
die("Too old");
|
2019-04-15 16:02:34 +00:00
|
|
|
//state.NextOp=state.JumpOp;
|
|
|
|
//state.cpu.rpc=state.JumpAddr;
|
2013-12-19 17:10:14 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case NDO_End:
|
|
|
|
goto _end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_end:
|
2019-03-25 10:53:13 +00:00
|
|
|
blk->sh4_code_size=state.cpu.rpc-blk->vaddr;
|
2013-12-19 17:10:14 +00:00
|
|
|
blk->NextBlock=state.NextAddr;
|
|
|
|
blk->BranchBlock=state.JumpAddr;
|
|
|
|
blk->BlockType=state.BlockType;
|
2015-07-28 17:02:49 +00:00
|
|
|
|
|
|
|
verify(blk->oplist.size() <= BLOCK_MAX_SH_OPS_HARD);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-06-04 19:20:34 +00:00
|
|
|
#if 0
|
2013-12-19 17:10:14 +00:00
|
|
|
switch(rbi->addr)
|
|
|
|
{
|
|
|
|
case 0x8C09ED16:
|
|
|
|
case 0x8C0BA50E:
|
|
|
|
case 0x8C0BA506:
|
|
|
|
case 0x8C0BA526:
|
|
|
|
case 0x8C224800:
|
2019-09-29 21:14:38 +00:00
|
|
|
INFO_LOG(DYNAREC, "HASH: %08X reloc %s", blk->addr, blk->hash());
|
2013-12-19 17:10:14 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//cycle tricks
|
|
|
|
if (settings.dynarec.idleskip)
|
|
|
|
{
|
|
|
|
//Experimental hash-id based idle skip
|
2019-09-29 21:14:38 +00:00
|
|
|
if (!mmu_enabled() && strstr(idle_hash, blk->hash()))
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2019-09-29 21:14:38 +00:00
|
|
|
DEBUG_LOG(DYNAREC, "IDLESKIP: %08X reloc match %s", blk->addr, blk->hash());
|
|
|
|
blk->guest_cycles = max_cycles;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Small-n-simple idle loop detector :p
|
|
|
|
if (state.info.has_readm && !state.info.has_writem && !state.info.has_fpu && blk->guest_opcodes<6)
|
|
|
|
{
|
2019-03-25 10:53:13 +00:00
|
|
|
if (blk->BlockType==BET_Cond_0 || (blk->BlockType==BET_Cond_1 && blk->BranchBlock<=blk->vaddr))
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
blk->guest_cycles*=3;
|
|
|
|
}
|
|
|
|
|
2019-03-25 10:53:13 +00:00
|
|
|
if (blk->BranchBlock==blk->vaddr)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
blk->guest_cycles*=10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//if in syscalls area (ip.bin etc) skip fast :p
|
|
|
|
if ((blk->addr&0x1FFF0000)==0x0C000000)
|
|
|
|
{
|
|
|
|
if (blk->addr&0x8000)
|
|
|
|
{
|
|
|
|
//ip.bin (boot loader/img etc)
|
|
|
|
blk->guest_cycles*=15;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//syscalls
|
|
|
|
blk->guest_cycles*=5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//blk->guest_cycles=5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
blk->guest_cycles*=1.5;
|
|
|
|
}
|
2019-04-19 07:58:25 +00:00
|
|
|
// Win CE boost
|
|
|
|
if (mmu_enabled())
|
2019-05-15 13:26:08 +00:00
|
|
|
blk->guest_cycles *= 1.5f;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
//make sure we don't use wayy-too-many cycles
|
|
|
|
blk->guest_cycles=min(blk->guest_cycles,max_cycles);
|
|
|
|
//make sure we don't use wayy-too-few cycles
|
|
|
|
blk->guest_cycles=max(1U,blk->guest_cycles);
|
|
|
|
blk=0;
|
2019-04-15 16:02:34 +00:00
|
|
|
|
|
|
|
return true;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
2018-07-06 07:49:39 +00:00
|
|
|
#endif
|