/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus - r4300.c * * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * * Copyright (C) 2002 Hacktarux * * * * 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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include "r4300.h" #include "ops.h" #include "exception.h" #include "interupt.h" #include "macros.h" #include "recomp.h" #include "recomph.h" #include "../memory/memory.h" #ifdef DBG #include "../debugger/debugger.h" #endif unsigned int dynacore = 0, interpcore = 0; int no_compiled_jump = 0; int stop, llbit, rompause; long long int reg[32], hi, lo; long long int local_rs, local_rt; unsigned int reg_cop0[32]; int local_rs32, local_rt32; unsigned int jump_target; float *reg_cop1_simple[32]; double *reg_cop1_double[32]; long long int reg_cop1_fgr_64[32]; int FCR0, FCR31; tlb tlb_e[32]; unsigned int delay_slot, skip_jump = 0, dyna_interp = 0, last_addr; unsigned long long int debug_count = 0; unsigned int next_interupt, CIC_Chip; precomp_instr *PC; char invalid_code[0x100000]; precomp_block *blocks[0x100000], *actual; int rounding_mode = 0x33F, trunc_mode = 0xF3F, round_mode = 0x33F, ceil_mode = 0xB3F, floor_mode = 0x73F; void (*code)(); /*#define check_memory() \ if (!invalid_code[address>>12]) \ invalid_code[address>>12] = 1;*/ #define check_memory() \ if (!invalid_code[address>>12]) \ if (blocks[address>>12]->block[(address&0xFFF)/4].ops != NOTCOMPILED) \ invalid_code[address>>12] = 1; void NI() { printf("NI() @ %x\n", (int)PC->addr); printf("opcode not implemented : "); if (PC->addr >= 0xa4000000 && PC->addr < 0xa4001000) printf("%x:%x\n", (int)PC->addr, (int)SP_DMEM[(PC->addr-0xa4000000)/4]); else printf("%x:%x\n", (int)PC->addr, (int)rdram[(PC->addr-0x80000000)/4]); stop=1; } void RESERVED() { printf("reserved opcode : "); if (PC->addr >= 0xa4000000 && PC->addr < 0xa4001000) printf("%x:%x\n", (int)PC->addr, (int)SP_DMEM[(PC->addr-0xa4000000)/4]); else printf("%x:%x\n", (int)PC->addr, (int)rdram[(PC->addr-0x80000000)/4]); stop=1; } void FIN_BLOCK() { if (!delay_slot) { jump_to((PC-1)->addr+4); /*#ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif Used by dynarec only, check should be unnecessary */ PC->ops(); if (dynacore) dyna_jump(); } else { precomp_block *blk = actual; precomp_instr *inst = PC; jump_to((PC-1)->addr+4); /*#ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif Used by dynarec only, check should be unnecessary */ if (!skip_jump) { PC->ops(); actual = blk; PC = inst+1; } else PC->ops(); if (dynacore) dyna_jump(); } } void J() { PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (!skip_jump) PC=actual->block+ (((((PC-2)->f.j.inst_index<<2) | ((PC-1)->addr & 0xF0000000))-actual->start)>>2); last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void J_OUT() { jump_target = (PC->addr & 0xF0000000) | (PC->f.j.inst_index<<2); PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (!skip_jump) jump_to(jump_target); last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void J_IDLE() { int skip; update_count(); skip = next_interupt - Count; if (skip > 3) Count += (skip & 0xFFFFFFFC); else J(); } void JAL() { PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (!skip_jump) { reg[31]=PC->addr; sign_extended(reg[31]); PC=actual->block+ (((((PC-2)->f.j.inst_index<<2) | ((PC-1)->addr & 0xF0000000))-actual->start)>>2); } last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void JAL_OUT() { jump_target = (PC->addr & 0xF0000000) | (PC->f.j.inst_index<<2); PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (!skip_jump) { reg[31]=PC->addr; sign_extended(reg[31]); jump_to(jump_target); } last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void JAL_IDLE() { int skip; update_count(); skip = next_interupt - Count; if (skip > 3) Count += (skip & 0xFFFFFFFC); else JAL(); } void BEQ() { local_rs = irs; local_rt = irt; PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (local_rs == local_rt && !skip_jump) PC += (PC-2)->f.i.immediate-1; last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BEQ_OUT() { local_rs = irs; local_rt = irt; jump_target = (int)PC->f.i.immediate; PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (!skip_jump && local_rs == local_rt) jump_to(PC->addr + ((jump_target-1)<<2)); last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BEQ_IDLE() { int skip; if (irs == irt) { update_count(); skip = next_interupt - Count; if (skip > 3) Count += (skip & 0xFFFFFFFC); else BEQ(); } else BEQ(); } void BNE() { local_rs = irs; local_rt = irt; PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (local_rs != local_rt && !skip_jump) PC += (PC-2)->f.i.immediate-1; last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BNE_OUT() { local_rs = irs; local_rt = irt; jump_target = (int)PC->f.i.immediate; PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (!skip_jump && local_rs != local_rt) jump_to(PC->addr + ((jump_target-1)<<2)); last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BNE_IDLE() { int skip; if (irs != irt) { update_count(); skip = next_interupt - Count; if (skip > 3) Count += (skip & 0xFFFFFFFC); else BNE(); } else BNE(); } void BLEZ() { local_rs = irs; PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (local_rs <= 0 && !skip_jump) PC += (PC-2)->f.i.immediate-1; last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BLEZ_OUT() { local_rs = irs; jump_target = (int)PC->f.i.immediate; PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (!skip_jump && local_rs <= 0) jump_to(PC->addr + ((jump_target-1)<<2)); last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BLEZ_IDLE() { int skip; if (irs <= irt) { update_count(); skip = next_interupt - Count; if (skip > 3) Count += (skip & 0xFFFFFFFC); else BLEZ(); } else BLEZ(); } void BGTZ() { local_rs = irs; PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (local_rs > 0 && !skip_jump) PC += (PC-2)->f.i.immediate-1; last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BGTZ_OUT() { local_rs = irs; jump_target = (int)PC->f.i.immediate; PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (!skip_jump && local_rs > 0) jump_to(PC->addr + ((jump_target-1)<<2)); last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BGTZ_IDLE() { int skip; if (irs > irt) { update_count(); skip = next_interupt - Count; if (skip > 3) Count += (skip & 0xFFFFFFFC); else BGTZ(); } else BGTZ(); } void ADDI() { irt32 = irs32 + iimmediate; sign_extended(irt); PC++; } void ADDIU() { irt32 = irs32 + iimmediate; sign_extended(irt); PC++; } void SLTI() { if (irs < iimmediate) irt = 1; else irt = 0; PC++; } void SLTIU() { if ((unsigned long long)irs < (unsigned long long)((long long)iimmediate)) irt = 1; else irt = 0; PC++; } void ANDI() { irt = irs & (unsigned short)iimmediate; PC++; } void ORI() { irt = irs | (unsigned short)iimmediate; PC++; } void XORI() { irt = irs ^ (unsigned short)iimmediate; PC++; } void LUI() { irt32 = iimmediate << 16; sign_extended(irt); PC++; } void BEQL() { if (irs == irt) { PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if(!skip_jump) PC += (PC-2)->f.i.immediate-1; } else { PC+=2; update_count(); } last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BEQL_OUT() { if (irs == irt) { jump_target = (int)PC->f.i.immediate; PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (!skip_jump) jump_to(PC->addr + ((jump_target-1)<<2)); } else { PC+=2; update_count(); } last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BEQL_IDLE() { int skip; if (irs == irt) { update_count(); skip = next_interupt - Count; if (skip > 3) Count += (skip & 0xFFFFFFFC); else BEQL(); } else BEQL(); } void BNEL() { if (irs != irt) { PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if(!skip_jump) PC += (PC-2)->f.i.immediate-1; } else { PC+=2; update_count(); } last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BNEL_OUT() { if (irs != irt) { jump_target = (int)PC->f.i.immediate; PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (!skip_jump) jump_to(PC->addr + ((jump_target-1)<<2)); } else { PC+=2; update_count(); } last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BNEL_IDLE() { int skip; if (irs != irt) { update_count(); skip = next_interupt - Count; if (skip > 3) Count += (skip & 0xFFFFFFFC); else BNEL(); } else BNEL(); } void BLEZL() { if (irs <= 0) { PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if(!skip_jump) PC += (PC-2)->f.i.immediate-1; } else { PC+=2; update_count(); } last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BLEZL_OUT() { if (irs <= 0) { jump_target = (int)PC->f.i.immediate; PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (!skip_jump) jump_to(PC->addr + ((jump_target-1)<<2)); } else { PC+=2; update_count(); } last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BLEZL_IDLE() { int skip; if (irs <= irt) { update_count(); skip = next_interupt - Count; if (skip > 3) Count += (skip & 0xFFFFFFFC); else BLEZL(); } else BLEZL(); } void BGTZL() { if (irs > 0) { PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if(!skip_jump) PC += (PC-2)->f.i.immediate-1; } else { PC+=2; update_count(); } last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BGTZL_OUT() { if (irs > 0) { jump_target = (int)PC->f.i.immediate; PC++; delay_slot=1; #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); update_count(); delay_slot=0; if (!skip_jump) jump_to(PC->addr + ((jump_target-1)<<2)); } else { PC+=2; update_count(); } last_addr = PC->addr; if (next_interupt <= Count) gen_interupt(); } void BGTZL_IDLE() { int skip; if (irs > irt) { update_count(); skip = next_interupt - Count; if (skip > 3) Count += (skip & 0xFFFFFFFC); else BGTZL(); } else BGTZL(); } void DADDI() { irt = irs + iimmediate; PC++; } void DADDIU() { irt = irs + iimmediate; PC++; } void LDL() { unsigned long long int word = 0; PC++; switch ((lsaddr) & 7) { case 0: address = lsaddr; rdword = (unsigned long long *) &lsrt; read_dword_in_memory(); break; case 1: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFF) | (word << 8); break; case 2: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFFFF) | (word << 16); break; case 3: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFF) | (word << 24); break; case 4: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFFFF) | (word << 32); break; case 5: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFFFFFFLL) | (word << 40); break; case 6: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFFFFFFFFLL) | (word << 48); break; case 7: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFFFFFFFFFFLL) | (word << 56); break; } } void LDR() { unsigned long long int word = 0; PC++; switch ((lsaddr) & 7) { case 0: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFFFFFFFFFF00LL) | (word >> 56); break; case 1: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFFFFFFFF0000LL) | (word >> 48); break; case 2: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFFFFFF000000LL) | (word >> 40); break; case 3: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFFFF00000000LL) | (word >> 32); break; case 4: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFF0000000000LL) | (word >> 24); break; case 5: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFFFF000000000000LL) | (word >> 16); break; case 6: address = (lsaddr) & 0xFFFFFFF8; rdword = &word; read_dword_in_memory(); if(address) lsrt = (lsrt & 0xFF00000000000000LL) | (word >> 8); break; case 7: address = (lsaddr) & 0xFFFFFFF8; rdword = (unsigned long long *) &lsrt; read_dword_in_memory(); break; } } void LB() { PC++; address = lsaddr; rdword = (unsigned long long *) &lsrt; read_byte_in_memory(); if (address) sign_extendedb(lsrt); } void LH() { PC++; address = lsaddr; rdword = (unsigned long long *) &lsrt; read_hword_in_memory(); if (address) sign_extendedh(lsrt); } void LWL() { unsigned long long int word = 0; PC++; switch ((lsaddr) & 3) { case 0: address = lsaddr; rdword = (unsigned long long *) &lsrt; read_word_in_memory(); break; case 1: address = (lsaddr) & 0xFFFFFFFC; rdword = &word; read_word_in_memory(); if(address) lsrt = (lsrt & 0xFF) | (word << 8); break; case 2: address = (lsaddr) & 0xFFFFFFFC; rdword = &word; read_word_in_memory(); if(address) lsrt = (lsrt & 0xFFFF) | (word << 16); break; case 3: address = (lsaddr) & 0xFFFFFFFC; rdword = &word; read_word_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFF) | (word << 24); break; } if(address) sign_extended(lsrt); } void LW() { PC++; address = lsaddr; rdword = (unsigned long long *) &lsrt; read_word_in_memory(); if (address) sign_extended(lsrt); } void LBU() { PC++; address = lsaddr; rdword = (unsigned long long *) &lsrt; read_byte_in_memory(); } void LHU() { PC++; address = lsaddr; rdword = (unsigned long long *) &lsrt; read_hword_in_memory(); } void LWR() { unsigned long long int word = 0; PC++; switch ((lsaddr) & 3) { case 0: address = (lsaddr) & 0xFFFFFFFC; rdword = &word; read_word_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFFFFFFFFFF00LL) | ((word >> 24) & 0xFF); break; case 1: address = (lsaddr) & 0xFFFFFFFC; rdword = &word; read_word_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFFFFFFFF0000LL) | ((word >> 16) & 0xFFFF); break; case 2: address = (lsaddr) & 0xFFFFFFFC; rdword = &word; read_word_in_memory(); if(address) lsrt = (lsrt & 0xFFFFFFFFFF000000LL) | ((word >> 8) & 0XFFFFFF); break; case 3: address = (lsaddr) & 0xFFFFFFFC; rdword = (unsigned long long *) &lsrt; read_word_in_memory(); if(address) sign_extended(lsrt); } } void LWU() { PC++; address = lsaddr; rdword = (unsigned long long *) &lsrt; read_word_in_memory(); } void SB() { PC++; address = lsaddr; byte = (unsigned char)(lsrt & 0xFF); write_byte_in_memory(); check_memory(); } void SH() { PC++; address = lsaddr; hword = (unsigned short)(lsrt & 0xFFFF); write_hword_in_memory(); check_memory(); } void SWL() { unsigned long long int old_word = 0; PC++; switch ((lsaddr) & 3) { case 0: address = (lsaddr) & 0xFFFFFFFC; word = (unsigned int)lsrt; write_word_in_memory(); check_memory(); break; case 1: address = (lsaddr) & 0xFFFFFFFC; rdword = &old_word; read_word_in_memory(); if(address) { word = ((unsigned int)lsrt >> 8) | (old_word & 0xFF000000); write_word_in_memory(); check_memory(); } break; case 2: address = (lsaddr) & 0xFFFFFFFC; rdword = &old_word; read_word_in_memory(); if(address) { word = ((unsigned int)lsrt >> 16) | (old_word & 0xFFFF0000); write_word_in_memory(); check_memory(); } break; case 3: address = lsaddr; byte = (unsigned char)(lsrt >> 24); write_byte_in_memory(); check_memory(); break; } } void SW() { PC++; address = lsaddr; word = (unsigned int)(lsrt & 0xFFFFFFFF); write_word_in_memory(); check_memory(); } void SDL() { unsigned long long int old_word = 0; PC++; switch ((lsaddr) & 7) { case 0: address = (lsaddr) & 0xFFFFFFF8; dword = lsrt; write_dword_in_memory(); check_memory(); break; case 1: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = ((unsigned long long)lsrt >> 8)|(old_word & 0xFF00000000000000LL); write_dword_in_memory(); check_memory(); } break; case 2: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = ((unsigned long long)lsrt >> 16)|(old_word & 0xFFFF000000000000LL); write_dword_in_memory(); check_memory(); } break; case 3: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = ((unsigned long long)lsrt >> 24)|(old_word & 0xFFFFFF0000000000LL); write_dword_in_memory(); check_memory(); } break; case 4: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = ((unsigned long long)lsrt >> 32)|(old_word & 0xFFFFFFFF00000000LL); write_dword_in_memory(); check_memory(); } break; case 5: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = ((unsigned long long)lsrt >> 40)|(old_word & 0xFFFFFFFFFF000000LL); write_dword_in_memory(); check_memory(); } break; case 6: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = ((unsigned long long)lsrt >> 48)|(old_word & 0xFFFFFFFFFFFF0000LL); write_dword_in_memory(); check_memory(); } break; case 7: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = ((unsigned long long)lsrt >> 56)|(old_word & 0xFFFFFFFFFFFFFF00LL); write_dword_in_memory(); check_memory(); } break; } } void SDR() { unsigned long long int old_word = 0; PC++; switch ((lsaddr) & 7) { case 0: address = lsaddr; rdword = &old_word; read_dword_in_memory(); if(address) { dword = (lsrt << 56) | (old_word & 0x00FFFFFFFFFFFFFFLL); write_dword_in_memory(); check_memory(); } break; case 1: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = (lsrt << 48) | (old_word & 0x0000FFFFFFFFFFFFLL); write_dword_in_memory(); check_memory(); } break; case 2: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = (lsrt << 40) | (old_word & 0x000000FFFFFFFFFFLL); write_dword_in_memory(); check_memory(); } break; case 3: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = (lsrt << 32) | (old_word & 0x00000000FFFFFFFFLL); write_dword_in_memory(); check_memory(); } break; case 4: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = (lsrt << 24) | (old_word & 0x0000000000FFFFFFLL); write_dword_in_memory(); check_memory(); } break; case 5: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = (lsrt << 16) | (old_word & 0x000000000000FFFFLL); write_dword_in_memory(); check_memory(); } break; case 6: address = (lsaddr) & 0xFFFFFFF8; rdword = &old_word; read_dword_in_memory(); if(address) { dword = (lsrt << 8) | (old_word & 0x00000000000000FFLL); write_dword_in_memory(); check_memory(); } break; case 7: address = (lsaddr) & 0xFFFFFFF8; dword = lsrt; write_dword_in_memory(); check_memory(); break; } } void SWR() { unsigned long long int old_word = 0; PC++; switch ((lsaddr) & 3) { case 0: address = lsaddr; rdword = &old_word; read_word_in_memory(); if(address) { word = ((unsigned int)lsrt << 24) | (old_word & 0x00FFFFFF); write_word_in_memory(); check_memory(); } break; case 1: address = (lsaddr) & 0xFFFFFFFC; rdword = &old_word; read_word_in_memory(); if(address) { word = ((unsigned int)lsrt << 16) | (old_word & 0x0000FFFF); write_word_in_memory(); check_memory(); } break; case 2: address = (lsaddr) & 0xFFFFFFFC; rdword = &old_word; read_word_in_memory(); if(address) { word = ((unsigned int)lsrt << 8) | (old_word & 0x000000FF); write_word_in_memory(); check_memory(); } break; case 3: address = (lsaddr) & 0xFFFFFFFC; word = (unsigned int)lsrt; write_word_in_memory(); check_memory(); break; } } void CACHE() { PC++; } void LL() { PC++; address = lsaddr; rdword = (unsigned long long *) &lsrt; read_word_in_memory(); if (address) { sign_extended(lsrt); llbit = 1; } } void LWC1() { unsigned long long int temp; if (check_cop1_unusable()) return; PC++; address = lslfaddr; rdword = &temp; read_word_in_memory(); if (address) *((int*)reg_cop1_simple[lslfft]) = *rdword; } void LDC1() { if (check_cop1_unusable()) return; PC++; address = lslfaddr; rdword = (unsigned long long *)reg_cop1_double[lslfft]; read_dword_in_memory(); } void LD() { PC++; address = lsaddr; rdword = (unsigned long long *) &lsrt; read_dword_in_memory(); } void SC() { /*PC++; printf("SC\n"); if (llbit) { address = lsaddr; word = (unsigned int)(lsrt & 0xFFFFFFFF); write_word_in_memory(); } lsrt = llbit;*/ PC++; if(llbit) { address = lsaddr; word = (unsigned int)(lsrt & 0xFFFFFFFF); write_word_in_memory(); check_memory(); llbit = 0; lsrt = 1; } else { lsrt = 0; } } void SWC1() { if (check_cop1_unusable()) return; PC++; address = lslfaddr; word = *((int*)reg_cop1_simple[lslfft]); write_word_in_memory(); check_memory(); } void SDC1() { if (check_cop1_unusable()) return; PC++; address = lslfaddr; dword = *((unsigned long long*)reg_cop1_double[lslfft]); write_dword_in_memory(); check_memory(); } void SD() { PC++; address = lsaddr; dword = lsrt; write_dword_in_memory(); check_memory(); } void NOTCOMPILED() { #ifdef CORE_DBG printf("NOTCOMPILED: addr = %x ops = %lx\n", PC->addr, (long) PC->ops); #endif if ((PC->addr>>16) == 0xa400) recompile_block((int *) SP_DMEM, blocks[0xa4000000>>12], PC->addr); else { unsigned int paddr = 0; if (PC->addr >= 0x80000000 && PC->addr < 0xc0000000) paddr = PC->addr; else paddr = virtual_to_physical_address(PC->addr, 2); if (paddr) { if ((paddr & 0x1FFFFFFF) >= 0x10000000) { recompile_block((int *) rom+((((paddr-(PC->addr-blocks[PC->addr>>12]->start)) & 0x1FFFFFFF) - 0x10000000)>>2), blocks[PC->addr>>12], PC->addr); } else recompile_block((int *) rdram+(((paddr-(PC->addr-blocks[PC->addr>>12]->start)) & 0x1FFFFFFF)>>2), blocks[PC->addr>>12], PC->addr); } else printf("not compiled exception\n"); } /*#ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif The preceeding update_debugger SHOULD be unnecessary since it should have been called before NOTCOMPILED would have been executed */ PC->ops(); if (dynacore) dyna_jump(); } void NOTCOMPILED2() { NOTCOMPILED(); } static inline unsigned int update_invalid_addr(unsigned int addr) { if (addr >= 0x80000000 && addr < 0xa0000000) { if (invalid_code[addr>>12]) invalid_code[(addr+0x20000000)>>12] = 1; if (invalid_code[(addr+0x20000000)>>12]) invalid_code[addr>>12] = 1; return addr; } else if (addr >= 0xa0000000 && addr < 0xc0000000) { if (invalid_code[addr>>12]) invalid_code[(addr-0x20000000)>>12] = 1; if (invalid_code[(addr-0x20000000)>>12]) invalid_code[addr>>12] = 1; return addr; } else { unsigned int paddr = virtual_to_physical_address(addr, 2); if (paddr) { unsigned int beg_paddr = paddr - (addr - (addr&~0xFFF)); update_invalid_addr(paddr); if (invalid_code[(beg_paddr+0x000)>>12]) invalid_code[addr>>12] = 1; if (invalid_code[(beg_paddr+0xFFC)>>12]) invalid_code[addr>>12] = 1; if (invalid_code[addr>>12]) invalid_code[(beg_paddr+0x000)>>12] = 1; if (invalid_code[addr>>12]) invalid_code[(beg_paddr+0xFFC)>>12] = 1; } return paddr; } } #define addr jump_to_address unsigned int jump_to_address; void jump_to_func() { unsigned int paddr; if (skip_jump) return; paddr = update_invalid_addr(addr); if (!paddr) return; actual = blocks[addr>>12]; if (invalid_code[addr>>12]) { if (!blocks[addr>>12]) { blocks[addr>>12] = malloc(sizeof(precomp_block)); actual = blocks[addr>>12]; blocks[addr>>12]->code = NULL; blocks[addr>>12]->block = NULL; blocks[addr>>12]->jumps_table = NULL; blocks[addr>>12]->riprel_table = NULL; } blocks[addr>>12]->start = addr & ~0xFFF; blocks[addr>>12]->end = (addr & ~0xFFF) + 0x1000; init_block((int *) rdram+(((paddr-(addr-blocks[addr>>12]->start)) & 0x1FFFFFFF)>>2), blocks[addr>>12]); } PC=actual->block+((addr-actual->start)>>2); if (dynacore) dyna_jump(); } #undef addr /* Refer to Figure 6-2 on page 155 and explanation on page B-11 of MIPS R4000 Microprocessor User's Manual (Second Edition) by Joe Heinrich. */ void shuffle_fpr_data(int oldStatus, int newStatus) { #if defined(_BIG_ENDIAN) const int isBigEndian = 1; #else const int isBigEndian = 0; #endif if ((newStatus & 0x04000000) != (oldStatus & 0x04000000)) { int i; int temp_fgr_32[32]; // pack or unpack the FGR register data if (newStatus & 0x04000000) { // switching into 64-bit mode // retrieve 32 FPR values from packed 32-bit FGR registers for (i = 0; i < 32; i++) { temp_fgr_32[i] = *((int *) ®_cop1_fgr_64[i>>1] + ((i & 1) ^ isBigEndian)); } // unpack them into 32 64-bit registers, taking the high 32-bits from their temporary place in the upper 16 FGRs for (i = 0; i < 32; i++) { int high32 = *((int *) ®_cop1_fgr_64[(i>>1)+16] + (i & 1)); *((int *) ®_cop1_fgr_64[i] + isBigEndian) = temp_fgr_32[i]; *((int *) ®_cop1_fgr_64[i] + (isBigEndian^1)) = high32; } } else { // switching into 32-bit mode // retrieve the high 32 bits from each 64-bit FGR register and store in temp array for (i = 0; i < 32; i++) { temp_fgr_32[i] = *((int *) ®_cop1_fgr_64[i] + (isBigEndian^1)); } // take the low 32 bits from each register and pack them together into 64-bit pairs for (i = 0; i < 16; i++) { int least32 = *((int *) ®_cop1_fgr_64[i*2] + isBigEndian); int most32 = *((int *) ®_cop1_fgr_64[i*2+1] + isBigEndian); reg_cop1_fgr_64[i] = ((unsigned long long) most32 << 32) | (unsigned long long) least32; } // store the high bits in the upper 16 FGRs, which wont be accessible in 32-bit mode for (i = 0; i < 32; i++) { *((int *) ®_cop1_fgr_64[(i>>1)+16] + (i & 1)) = temp_fgr_32[i]; } } } } void set_fpr_pointers(int newStatus) { int i; #if defined(_BIG_ENDIAN) const int isBigEndian = 1; #else const int isBigEndian = 0; #endif // update the FPR register pointers if (newStatus & 0x04000000) { for (i = 0; i < 32; i++) { reg_cop1_double[i] = (double*) ®_cop1_fgr_64[i]; reg_cop1_simple[i] = ((float*) ®_cop1_fgr_64[i]) + isBigEndian; } } else { for (i = 0; i < 32; i++) { reg_cop1_double[i] = (double*) ®_cop1_fgr_64[i>>1]; reg_cop1_simple[i] = ((float*) ®_cop1_fgr_64[i>>1]) + ((i & 1) ^ isBigEndian); } } } int check_cop1_unusable() { if (!(Status & 0x20000000)) { Cause = (11 << 2) | 0x10000000; exception_general(); return 1; } return 0; } void update_count() { if (interpcore) { Count = Count + (interp_addr - last_addr)/2; last_addr = interp_addr; } else { if (PC->addr < last_addr) { printf("PC->addr < last_addr\n"); } Count = Count + (PC->addr - last_addr)/2; last_addr = PC->addr; } #ifdef COMPARE_CORE if (delay_slot) compare_core(); #endif /*#ifdef DBG if (debugger_mode && !delay_slot) update_debugger(PC->addr); #endif */ } void init_blocks() { int i; for (i=0; i<0x100000; i++) { invalid_code[i] = 1; blocks[i] = NULL; } blocks[0xa4000000>>12] = malloc(sizeof(precomp_block)); invalid_code[0xa4000000>>12] = 1; blocks[0xa4000000>>12]->code = NULL; blocks[0xa4000000>>12]->block = NULL; blocks[0xa4000000>>12]->jumps_table = NULL; blocks[0xa4000000>>12]->riprel_table = NULL; blocks[0xa4000000>>12]->start = 0xa4000000; blocks[0xa4000000>>12]->end = 0xa4001000; actual=blocks[0xa4000000>>12]; init_block((int *) SP_DMEM, blocks[0xa4000000>>12]); PC=actual->block+(0x40/4); /*#ifdef DBG //should only be needed by dynamic recompiler if (debugger_mode) // debugger shows initial state (before 1st instruction). update_debugger(PC->addr); #endif*/ } /* this hard reset function simulates the boot-up state of the R4300 CPU */ void r4300_reset_hard() { unsigned int i; // clear r4300 registers and TLB entries for (i = 0; i < 32; i++) { reg[i]=0; reg_cop0[i]=0; reg_cop1_fgr_64[i]=0; // --------------tlb------------------------ tlb_e[i].mask=0; tlb_e[i].vpn2=0; tlb_e[i].g=0; tlb_e[i].asid=0; tlb_e[i].pfn_even=0; tlb_e[i].c_even=0; tlb_e[i].d_even=0; tlb_e[i].v_even=0; tlb_e[i].pfn_odd=0; tlb_e[i].c_odd=0; tlb_e[i].d_odd=0; tlb_e[i].v_odd=0; tlb_e[i].r=0; //tlb_e[i].check_parity_mask=0x1000; tlb_e[i].start_even=0; tlb_e[i].end_even=0; tlb_e[i].phys_even=0; tlb_e[i].start_odd=0; tlb_e[i].end_odd=0; tlb_e[i].phys_odd=0; } for (i=0; i<0x100000; i++) { tlb_LUT_r[i] = 0; tlb_LUT_w[i] = 0; } llbit=0; hi=0; lo=0; FCR0=0x511; FCR31=0; // set COP0 registers Random = 31; Status= 0x34000000; set_fpr_pointers(Status); Config= 0x6e463; PRevID = 0xb00; Count = 0x5000; Cause = 0x5C; Context = 0x7FFFF0; EPC = 0xFFFFFFFF; BadVAddr = 0xFFFFFFFF; ErrorEPC = 0xFFFFFFFF; rounding_mode = 0x33F; } /* this soft reset function simulates the actions of the PIF ROM, which may vary by region */ void r4300_reset_soft() { long long CRC = 0; unsigned int i; // copy boot code from ROM to SP_DMEM memcpy((char *)SP_DMEM+0x40, rom+0x40, 0xFC0); // the following values are extracted from the pj64 source code // thanks to Zilmar and Jabo reg[6] = 0xFFFFFFFFA4001F0CLL; reg[7] = 0xFFFFFFFFA4001F08LL; reg[8] = 0x00000000000000C0LL; reg[10]= 0x0000000000000040LL; reg[11]= 0xFFFFFFFFA4000040LL; reg[29]= 0xFFFFFFFFA4001FF0LL; // figure out which ROM type is loaded for (i = 0x40/4; i < (0x1000/4); i++) CRC += SP_DMEM[i]; switch(CRC) { case 0x000000D0027FDF31LL: case 0x000000CFFB631223LL: CIC_Chip = 1; break; case 0x000000D057C85244LL: CIC_Chip = 2; break; case 0x000000D6497E414BLL: CIC_Chip = 3; break; case 0x0000011A49F60E96LL: CIC_Chip = 5; break; case 0x000000D6D5BE5580LL: CIC_Chip = 6; break; default: CIC_Chip = 2; } switch(ROM_HEADER->Country_code&0xFF) { case 0x44: case 0x46: case 0x49: case 0x50: case 0x53: case 0x55: case 0x58: case 0x59: switch (CIC_Chip) { case 2: reg[5] = 0xFFFFFFFFC0F1D859LL; reg[14]= 0x000000002DE108EALL; break; case 3: reg[5] = 0xFFFFFFFFD4646273LL; reg[14]= 0x000000001AF99984LL; break; case 5: SP_IMEM[1] = 0xBDA807FC; reg[5] = 0xFFFFFFFFDECAAAD1LL; reg[14]= 0x000000000CF85C13LL; reg[24]= 0x0000000000000002LL; break; case 6: reg[5] = 0xFFFFFFFFB04DC903LL; reg[14]= 0x000000001AF99984LL; reg[24]= 0x0000000000000002LL; break; } reg[23]= 0x0000000000000006LL; reg[31]= 0xFFFFFFFFA4001554LL; break; case 0x37: case 0x41: case 0x45: case 0x4A: default: switch (CIC_Chip) { case 2: reg[5] = 0xFFFFFFFFC95973D5LL; reg[14]= 0x000000002449A366LL; break; case 3: reg[5] = 0xFFFFFFFF95315A28LL; reg[14]= 0x000000005BACA1DFLL; break; case 5: SP_IMEM[1] = 0x8DA807FC; reg[5] = 0x000000005493FB9ALL; reg[14]= 0xFFFFFFFFC2C20384LL; break; case 6: reg[5] = 0xFFFFFFFFE067221FLL; reg[14]= 0x000000005CD2B70FLL; break; } reg[20]= 0x0000000000000001LL; reg[24]= 0x0000000000000003LL; reg[31]= 0xFFFFFFFFA4001550LL; } switch (CIC_Chip) { case 1: reg[22]= 0x000000000000003FLL; break; case 2: reg[1] = 0x0000000000000001LL; reg[2] = 0x000000000EBDA536LL; reg[3] = 0x000000000EBDA536LL; reg[4] = 0x000000000000A536LL; reg[12]= 0xFFFFFFFFED10D0B3LL; reg[13]= 0x000000001402A4CCLL; reg[15]= 0x000000003103E121LL; reg[22]= 0x000000000000003FLL; reg[25]= 0xFFFFFFFF9DEBB54FLL; break; case 3: reg[1] = 0x0000000000000001LL; reg[2] = 0x0000000049A5EE96LL; reg[3] = 0x0000000049A5EE96LL; reg[4] = 0x000000000000EE96LL; reg[12]= 0xFFFFFFFFCE9DFBF7LL; reg[13]= 0xFFFFFFFFCE9DFBF7LL; reg[15]= 0x0000000018B63D28LL; reg[22]= 0x0000000000000078LL; reg[25]= 0xFFFFFFFF825B21C9LL; break; case 5: SP_IMEM[0] = 0x3C0DBFC0; SP_IMEM[2] = 0x25AD07C0; SP_IMEM[3] = 0x31080080; SP_IMEM[4] = 0x5500FFFC; SP_IMEM[5] = 0x3C0DBFC0; SP_IMEM[6] = 0x8DA80024; SP_IMEM[7] = 0x3C0BB000; reg[2] = 0xFFFFFFFFF58B0FBFLL; reg[3] = 0xFFFFFFFFF58B0FBFLL; reg[4] = 0x0000000000000FBFLL; reg[12]= 0xFFFFFFFF9651F81ELL; reg[13]= 0x000000002D42AAC5LL; reg[15]= 0x0000000056584D60LL; reg[22]= 0x0000000000000091LL; reg[25]= 0xFFFFFFFFCDCE565FLL; break; case 6: reg[2] = 0xFFFFFFFFA95930A4LL; reg[3] = 0xFFFFFFFFA95930A4LL; reg[4] = 0x00000000000030A4LL; reg[12]= 0xFFFFFFFFBCB59510LL; reg[13]= 0xFFFFFFFFBCB59510LL; reg[15]= 0x000000007A3C07F4LL; reg[22]= 0x0000000000000085LL; reg[25]= 0x00000000465E3F72LL; break; } } void r4300_execute() { unsigned int i; debug_count = 0; printf("Starting r4300 emulator\n"); delay_slot=0; stop = 0; rompause = 0; /* clear instruction counters */ #if defined(COUNT_INSTR) for (i = 0; i < 131; i++) instr_count[i] = 0; #endif last_addr = 0xa4000040; next_interupt = 624999; init_interupt(); interpcore = 0; if (dynacore == 0) { printf ("R4300 Core mode: Interpreter\n"); init_blocks(); last_addr = PC->addr; while (!stop) { #ifdef COMPARE_CORE if (PC->ops == FIN_BLOCK && (PC->addr < 0x80000000 || PC->addr >= 0xc0000000)) virtual_to_physical_address(PC->addr, 2); compare_core(); #endif #ifdef DBG if (debugger_mode) update_debugger(PC->addr); #endif PC->ops(); } } #if !defined(NO_ASM) && (defined(__i386__) || defined(__x86_64__)) else if (dynacore == 1) { dynacore = 1; printf ("R4300 Core mode: Dynamic Recompiler\n"); init_blocks(); code = (void *)(actual->code+(actual->block[0x40/4].local_addr)); dyna_start(code); PC++; #if defined(PROFILE_R4300) pfProfile = fopen("instructionaddrs.dat", "ab"); for (i=0; i<0x100000; i++) if (invalid_code[i] == 0 && blocks[i] != NULL && blocks[i]->code != NULL && blocks[i]->block != NULL) { unsigned char *x86addr; int mipsop; // store final code length for this block mipsop = -1; /* -1 == end of x86 code block */ x86addr = blocks[i]->code + blocks[i]->code_length; fwrite(&mipsop, 1, 4, pfProfile); fwrite(&x86addr, 1, sizeof(char *), pfProfile); } fclose(pfProfile); pfProfile = NULL; #endif } #endif else { printf ("R4300 Core mode: Pure Interpreter\n"); dynacore = 0; interpcore = 1; pure_interpreter(); } debug_count+= Count; printf("R4300 core finished.\n"); for (i=0; i<0x100000; i++) { if (blocks[i]) { if (blocks[i]->block) { free(blocks[i]->block); blocks[i]->block = NULL; } if (blocks[i]->code) { free(blocks[i]->code); blocks[i]->code = NULL; } if (blocks[i]->jumps_table) { free(blocks[i]->jumps_table); blocks[i]->jumps_table = NULL; } if (blocks[i]->riprel_table) { free(blocks[i]->riprel_table); blocks[i]->riprel_table = NULL; } free(blocks[i]); blocks[i] = NULL; } } if (!dynacore && interpcore) free(PC); /* print instruction counts */ #if defined(COUNT_INSTR) if (dynacore) { unsigned int iTypeCount[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned int iTotal = 0; printf("Instruction counters:\n"); for (i = 0; i < 131; i++) { printf("%8s: %08i ", instr_name[i], instr_count[i]); if (i % 5 == 4) printf("\n"); iTypeCount[instr_type[i]] += instr_count[i]; iTotal += instr_count[i]; } printf("\nInstruction type summary (total instructions = %i)\n", iTotal); for (i = 0; i < 11; i++) { printf("%20s: %04.1f%% (%i)\n", instr_typename[i], (float) iTypeCount[i] * 100.0 / iTotal, iTypeCount[i]); } } #endif }