2025-01-19 11:54:23 -06:00
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
2024-07-30 13:42:36 +02:00
// SPDX-License-Identifier: GPL-3.0+
2009-02-09 21:15:56 +00:00
#include "Common.h"
2009-11-14 12:02:56 +00:00
#include <float.h>
2009-02-09 21:15:56 +00:00
#include "R5900.h"
#include "R5900OpcodeTables.h"
2016-07-01 17:07:55 +05:30
#include "GS.h"
2020-12-06 19:29:35 +00:00
#include "ps2/BiosTools.h"
2023-01-14 22:29:42 -05:00
#include "DebugTools/DebugInterface.h"
#include "DebugTools/Breakpoints.h"
2022-12-28 17:44:34 +10:00
#include "Host.h"
#include "VMManager.h"
#include "fmt/format.h"
2009-02-09 21:15:56 +00:00
2016-07-01 17:07:55 +05:30
GS_VideoMode gsVideoMode = GS_VideoMode :: Uninitialized ;
2016-08-18 19:37:35 +05:30
bool gsIsInterlaced = false ;
2009-02-09 21:15:56 +00:00
2011-10-11 13:03:24 +00:00
static __fi bool _add64_Overflow ( s64 x , s64 y , s64 & ret )
2009-03-01 21:49:17 +00:00
{
const s64 result = x + y ;
// Let's all give gigaherz a big round of applause for finding this gem,
// which apparently works, and generates compact/fast x86 code too (the
// other method below is like 5-10 times slower).
2011-10-11 13:03:24 +00:00
if ( (( ~ ( x ^ y )) & ( x ^ result )) < 0 ) {
2009-08-31 03:47:05 +00:00
cpuException ( 0x30 , cpuRegs . branch ); // fixme: is 0x30 right for overflow??
2011-10-11 13:03:24 +00:00
return true ;
}
2009-03-01 21:49:17 +00:00
// the not-as-fast style!
//if( ((x >= 0) && (y >= 0) && (result < 0)) ||
// ((x < 0) && (y < 0) && (result >= 0)) )
2009-08-31 03:47:05 +00:00
// cpuException(0x30, cpuRegs.branch);
2009-03-01 21:49:17 +00:00
2011-10-11 13:03:24 +00:00
ret = result ;
return false ;
2009-03-01 21:49:17 +00:00
}
2011-10-11 13:03:24 +00:00
static __fi bool _add32_Overflow ( s32 x , s32 y , s64 & ret )
2009-03-01 21:49:17 +00:00
{
GPR_reg64 result ; result . SD [ 0 ] = ( s64 ) x + y ;
// This 32bit method can rely on the MIPS documented method of checking for
// overflow, whichs imply compares bit 32 (rightmost bit of the upper word),
// against bit 31 (leftmost of the lower word).
// If bit32 != bit31 then we have an overflow.
2011-10-11 13:03:24 +00:00
if ( ( result . UL [ 0 ] >> 31 ) != ( result . UL [ 1 ] & 1 ) ) {
2009-08-31 03:47:05 +00:00
cpuException ( 0x30 , cpuRegs . branch );
2011-10-11 13:03:24 +00:00
return true ;
}
2022-09-13 21:20:25 -05:00
2011-10-11 13:03:24 +00:00
ret = result . SD [ 0 ];
2009-03-01 21:49:17 +00:00
2011-10-11 13:03:24 +00:00
return false ;
2009-03-01 21:49:17 +00:00
}
2010-04-27 16:23:08 +00:00
const R5900 :: OPCODE & R5900 :: GetCurrentInstruction ()
2009-02-09 21:15:56 +00:00
{
2010-04-27 16:23:08 +00:00
const OPCODE * opcode = & R5900 :: OpcodeTables :: tbl_Standard [ _Opcode_ ];
while ( opcode -> getsubclass != NULL )
2014-02-21 14:29:13 +00:00
opcode = & opcode -> getsubclass ( cpuRegs . code );
return * opcode ;
}
const R5900 :: OPCODE & R5900 :: GetInstruction ( u32 op )
{
const OPCODE * opcode = & R5900 :: OpcodeTables :: tbl_Standard [ op >> 26 ];
while ( opcode -> getsubclass != NULL )
opcode = & opcode -> getsubclass ( op );
2010-04-27 16:23:08 +00:00
return * opcode ;
}
const char * const R5900 :: bios [ 256 ] =
{
//0x00
"RFU000_FullReset" , "ResetEE" , "SetGsCrt" , "RFU003" ,
"Exit" , "RFU005" , "LoadExecPS2" , "ExecPS2" ,
"RFU008" , "RFU009" , "AddSbusIntcHandler" , "RemoveSbusIntcHandler" ,
"Interrupt2Iop" , "SetVTLBRefillHandler" , "SetVCommonHandler" , "SetVInterruptHandler" ,
//0x10
"AddIntcHandler" , "RemoveIntcHandler" , "AddDmacHandler" , "RemoveDmacHandler" ,
"_EnableIntc" , "_DisableIntc" , "_EnableDmac" , "_DisableDmac" ,
"_SetAlarm" , "_ReleaseAlarm" , "_iEnableIntc" , "_iDisableIntc" ,
"_iEnableDmac" , "_iDisableDmac" , "_iSetAlarm" , "_iReleaseAlarm" ,
//0x20
"CreateThread" , "DeleteThread" , "StartThread" , "ExitThread" ,
"ExitDeleteThread" , "TerminateThread" , "iTerminateThread" , "DisableDispatchThread" ,
"EnableDispatchThread" , "ChangeThreadPriority" , "iChangeThreadPriority" , "RotateThreadReadyQueue" ,
"iRotateThreadReadyQueue" , "ReleaseWaitThread" , "iReleaseWaitThread" , "GetThreadId" ,
//0x30
"ReferThreadStatus" , "iReferThreadStatus" , "SleepThread" , "WakeupThread" ,
"_iWakeupThread" , "CancelWakeupThread" , "iCancelWakeupThread" , "SuspendThread" ,
"iSuspendThread" , "ResumeThread" , "iResumeThread" , "JoinThread" ,
"RFU060" , "RFU061" , "EndOfHeap" , "RFU063" ,
//0x40
"CreateSema" , "DeleteSema" , "SignalSema" , "iSignalSema" ,
"WaitSema" , "PollSema" , "iPollSema" , "ReferSemaStatus" ,
"iReferSemaStatus" , "RFU073" , "SetOsdConfigParam" , "GetOsdConfigParam" ,
"GetGsHParam" , "GetGsVParam" , "SetGsHParam" , "SetGsVParam" ,
//0x50
"RFU080_CreateEventFlag" , "RFU081_DeleteEventFlag" ,
"RFU082_SetEventFlag" , "RFU083_iSetEventFlag" ,
"RFU084_ClearEventFlag" , "RFU085_iClearEventFlag" ,
"RFU086_WaitEventFlag" , "RFU087_PollEventFlag" ,
"RFU088_iPollEventFlag" , "RFU089_ReferEventFlagStatus" ,
"RFU090_iReferEventFlagStatus" , "RFU091_GetEntryAddress" ,
"EnableIntcHandler_iEnableIntcHandler" ,
"DisableIntcHandler_iDisableIntcHandler" ,
"EnableDmacHandler_iEnableDmacHandler" ,
"DisableDmacHandler_iDisableDmacHandler" ,
//0x60
"KSeg0" , "EnableCache" , "DisableCache" , "GetCop0" ,
"FlushCache" , "RFU101" , "CpuConfig" , "iGetCop0" ,
"iFlushCache" , "RFU105" , "iCpuConfig" , "sceSifStopDma" ,
2020-11-18 16:01:39 +00:00
"SetCPUTimerHandler" , "SetCPUTimer" , "SetOsdConfigParam2" , "GetOsdConfigParam2" ,
2010-04-27 16:23:08 +00:00
//0x70
"GsGetIMR_iGsGetIMR" , "GsGetIMR_iGsPutIMR" , "SetPgifHandler" , "SetVSyncFlag" ,
"RFU116" , "print" , "sceSifDmaStat_isceSifDmaStat" , "sceSifSetDma_isceSifSetDma" ,
"sceSifSetDChain_isceSifSetDChain" , "sceSifSetReg" , "sceSifGetReg" , "ExecOSD" ,
"Deci2Call" , "PSMode" , "MachineType" , "GetMemorySize" ,
};
2010-04-27 16:42:17 +00:00
static u32 deci2addr = 0 ;
static u32 deci2handler = 0 ;
2010-04-27 16:23:08 +00:00
static char deci2buffer [ 256 ];
void Deci2Reset ()
{
2010-04-27 17:04:12 +00:00
deci2handler = 0 ;
deci2addr = 0 ;
2023-06-16 21:25:52 +10:00
std :: memset ( deci2buffer , 0 , sizeof ( deci2buffer ));
2010-04-27 16:23:08 +00:00
}
2022-12-28 21:13:38 +10:00
bool SaveStateBase :: deci2Freeze ()
2010-04-27 16:23:08 +00:00
{
2022-12-28 21:13:38 +10:00
if ( ! FreezeTag ( "deci2" ))
return false ;
2010-04-27 16:23:08 +00:00
Freeze ( deci2addr );
Freeze ( deci2handler );
Freeze ( deci2buffer );
2022-12-28 21:13:38 +10:00
return IsOkay ();
2010-04-27 16:23:08 +00:00
}
/*
* int Deci2Call(int, u_int *);
*
* HLE implementation of the Deci2 interface.
*/
static int __Deci2Call ( int call , u32 * addr )
{
if ( call > 0x10 )
return - 1 ;
switch ( call )
2009-02-09 21:15:56 +00:00
{
2010-04-27 16:23:08 +00:00
case 1 : // open
if ( addr != NULL )
{
2010-04-27 16:42:17 +00:00
deci2addr = addr [ 1 ];
2010-04-27 16:23:08 +00:00
BIOS_LOG ( "deci2open: %x,%x,%x,%x" ,
addr [ 3 ], addr [ 2 ], addr [ 1 ], addr [ 0 ]);
deci2handler = addr [ 2 ];
}
else
{
2010-04-27 16:42:17 +00:00
deci2handler = 0 ;
2010-04-27 16:23:08 +00:00
DevCon . Warning ( "Deci2Call.Open > NULL address ignored." );
}
return 1 ;
2009-02-09 21:15:56 +00:00
2010-04-27 16:23:08 +00:00
case 2 : // close
2010-04-27 16:42:17 +00:00
deci2addr = 0 ;
deci2handler = 0 ;
2010-04-27 16:23:08 +00:00
return 1 ;
2009-02-09 21:15:56 +00:00
2010-04-27 16:23:08 +00:00
case 3 : // reqsend
{
char reqaddr [ 128 ];
if ( addr != NULL )
2023-10-02 02:06:07 +10:00
std :: snprintf ( reqaddr , std :: size ( reqaddr ), "%x %x %x %x" , addr [ 3 ], addr [ 2 ], addr [ 1 ], addr [ 0 ]);
2010-04-27 16:23:08 +00:00
2010-04-27 16:42:17 +00:00
if ( ! deci2addr ) return 1 ;
2022-09-13 21:20:25 -05:00
2010-04-27 16:42:17 +00:00
const u32 * d2ptr = ( u32 * ) PSM ( deci2addr );
2010-04-27 16:23:08 +00:00
BIOS_LOG ( "deci2reqsend: %s: deci2addr: %x,%x,%x,buf=%x %x,%x,len=%x,%x" ,
(( addr == NULL ) ? "NULL" : reqaddr ),
2010-04-27 16:42:17 +00:00
d2ptr [ 7 ], d2ptr [ 6 ], d2ptr [ 5 ], d2ptr [ 4 ],
d2ptr [ 3 ], d2ptr [ 2 ], d2ptr [ 1 ], d2ptr [ 0 ]);
2010-04-27 16:23:08 +00:00
// cpuRegs.pc = deci2handler;
2010-04-27 16:42:17 +00:00
// Console.WriteLn("deci2msg: %s", (char*)PSM(d2ptr[4]+0xc));
2010-04-27 16:23:08 +00:00
2010-04-27 16:42:17 +00:00
if ( d2ptr [ 1 ] > 0xc ){
2010-04-27 16:23:08 +00:00
// this looks horribly wrong, justification please?
2010-04-27 16:42:17 +00:00
u8 * pdeciaddr = ( u8 * ) dmaGetAddr ( d2ptr [ 4 ] + 0xc , false );
2010-04-27 16:23:08 +00:00
if ( pdeciaddr == NULL )
2010-04-27 16:42:17 +00:00
pdeciaddr = ( u8 * ) PSM ( d2ptr [ 4 ] + 0xc );
2010-04-27 16:23:08 +00:00
else
2010-04-27 16:42:17 +00:00
pdeciaddr += ( d2ptr [ 4 ] + 0xc ) % 16 ;
2010-04-27 16:23:08 +00:00
2010-04-27 16:42:17 +00:00
const int copylen = std :: min < uint > ( 255 , d2ptr [ 1 ] - 0xc );
2014-08-27 13:45:23 +10:00
memcpy ( deci2buffer , pdeciaddr , copylen );
2010-05-25 22:12:14 +00:00
deci2buffer [ copylen ] = '\0' ;
2010-04-27 16:23:08 +00:00
2010-08-06 05:46:09 +00:00
eeConLog ( ShiftJIS_ConvertString ( deci2buffer ) );
2010-04-27 16:23:08 +00:00
}
2010-04-27 16:42:17 +00:00
(( u32 * ) PSM ( deci2addr ))[ 3 ] = 0 ;
2010-04-27 16:23:08 +00:00
return 1 ;
}
case 4 : // poll
if ( addr != NULL )
BIOS_LOG ( "deci2poll: %x,%x,%x,%x \n " , addr [ 3 ], addr [ 2 ], addr [ 1 ], addr [ 0 ]);
return 1 ;
case 5 : // exrecv
return 1 ;
case 6 : // exsend
return 1 ;
case 0x10 : //kputs
2010-08-06 05:46:09 +00:00
if ( addr != NULL )
{
eeDeci2Log ( ShiftJIS_ConvertString (( char * ) PSM ( * addr )) );
}
2010-04-27 16:23:08 +00:00
return 1 ;
2009-02-09 21:15:56 +00:00
}
2010-04-27 16:23:08 +00:00
return 0 ;
}
2009-02-09 21:15:56 +00:00
2010-04-27 16:23:08 +00:00
namespace R5900 {
2009-02-09 21:15:56 +00:00
namespace Interpreter {
namespace OpcodeImpl {
void COP2 ()
{
//std::string disOut;
//disR5900Fasm(disOut, cpuRegs.code, cpuRegs.pc);
2009-03-27 06:34:51 +00:00
//VU0_LOG("%s", disOut.c_str());
2009-02-09 21:15:56 +00:00
Int_COP2PrintTable [ _Rs_ ]();
}
void Unknown () {
2009-03-27 06:34:51 +00:00
CPU_LOG ( "%8.8lx: Unknown opcode called" , cpuRegs . pc );
2009-02-09 21:15:56 +00:00
}
2009-10-29 13:32:40 +00:00
void MMI_Unknown () { Console . Warning ( "Unknown MMI opcode called" ); }
void COP0_Unknown () { Console . Warning ( "Unknown COP0 opcode called" ); }
void COP1_Unknown () { Console . Warning ( "Unknown FPU/COP1 opcode called" ); }
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
2009-02-09 21:15:56 +00:00
/*********************************************************
* Arithmetic with immediate operand *
* Format: OP rt, rs, immediate *
*********************************************************/
2009-03-01 21:49:17 +00:00
// Implementation Notes:
// * It is important that instructions perform overflow checks prior to shortcutting on
// the zero register (when it is used as a destination). Overflow exceptions are still
// handled even though the result is discarded.
// Rt = Rs + Im signed [exception on overflow]
void ADDI ()
{
2011-10-11 13:03:24 +00:00
s64 result ;
bool overflow = _add32_Overflow ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ], _Imm_ , result );
if ( overflow || ! _Rt_ ) return ;
2009-03-01 21:49:17 +00:00
cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ] = result ;
}
// Rt = Rs + Im signed !!! [overflow ignored]
// This instruction is effectively identical to ADDI. It is not a true unsigned operation,
// but rather it is a signed operation that ignores overflows.
void ADDIU ()
{
if ( ! _Rt_ ) return ;
2022-01-03 00:00:46 +10:00
cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = u64 ( s64 ( s32 ( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + u32 ( s32 ( _Imm_ )))));
2009-03-01 21:49:17 +00:00
}
// Rt = Rs + Im [exception on overflow]
// This is the full 64 bit version of ADDI. Overflow occurs at 64 bits instead
// of at 32 bits.
void DADDI ()
{
2011-10-11 13:03:24 +00:00
s64 result ;
bool overflow = _add64_Overflow ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ], _Imm_ , result );
if ( overflow || ! _Rt_ ) return ;
2009-03-01 21:49:17 +00:00
cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ] = result ;
}
// Rt = Rs + Im [overflow ignored]
// This instruction is effectively identical to DADDI. It is not a true unsigned operation,
// but rather it is a signed operation that ignores overflows.
void DADDIU ()
{
if ( ! _Rt_ ) return ;
2022-01-03 00:00:46 +10:00
cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] + u64 ( s64 ( _Imm_ ));
2009-03-01 21:49:17 +00:00
}
2009-02-09 21:15:56 +00:00
void ANDI () { if ( ! _Rt_ ) return ; cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] & ( u64 ) _ImmU_ ; } // Rt = Rs And Im (zero-extended)
void ORI () { if ( ! _Rt_ ) return ; cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] | ( u64 ) _ImmU_ ; } // Rt = Rs Or Im (zero-extended)
void XORI () { if ( ! _Rt_ ) return ; cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] ^ ( u64 ) _ImmU_ ; } // Rt = Rs Xor Im (zero-extended)
2009-03-01 21:49:17 +00:00
void SLTI () { if ( ! _Rt_ ) return ; cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] < ( s64 )( _Imm_ )) ? 1 : 0 ; } // Rt = Rs < Im (signed)
void SLTIU () { if ( ! _Rt_ ) return ; cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = ( cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] < ( u64 )( _Imm_ )) ? 1 : 0 ; } // Rt = Rs < Im (unsigned)
2009-02-09 21:15:56 +00:00
/*********************************************************
* Register arithmetic *
* Format: OP rd, rs, rt *
*********************************************************/
2009-03-01 21:49:17 +00:00
// Rd = Rs + Rt (Exception on Integer Overflow)
void ADD ()
{
2011-10-11 13:03:24 +00:00
s64 result ;
bool overflow = _add32_Overflow ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ], cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ], result );
if ( overflow || ! _Rd_ ) return ;
2009-03-01 21:49:17 +00:00
cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = result ;
}
void DADD ()
{
2011-10-11 13:03:24 +00:00
s64 result ;
bool overflow = _add64_Overflow ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ], cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ], result );
if ( overflow || ! _Rd_ ) return ;
2009-03-01 21:49:17 +00:00
cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = result ;
}
// Rd = Rs - Rt (Exception on Integer Overflow)
void SUB ()
{
2011-10-11 13:03:24 +00:00
s64 result ;
bool overflow = _add32_Overflow ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ], - cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ], result );
if ( overflow || ! _Rd_ ) return ;
2009-03-01 21:49:17 +00:00
cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = result ;
}
// Rd = Rs - Rt (Exception on Integer Overflow)
void DSUB ()
{
2011-10-11 13:03:24 +00:00
s64 result ;
bool overflow = _add64_Overflow ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ], - cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ], result );
if ( overflow || ! _Rd_ ) return ;
2009-03-01 21:49:17 +00:00
cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = result ;
}
2022-01-03 00:00:46 +10:00
void ADDU () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = u64 ( s64 ( s32 ( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ]))); } // Rd = Rs + Rt
void DADDU () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] + cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ]; }
void SUBU () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = u64 ( s64 ( s32 ( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] - cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ]))); } // Rd = Rs - Rt
void DSUBU () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] - cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ]; }
2009-02-09 21:15:56 +00:00
void AND () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] & cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ]; } // Rd = Rs And Rt
void OR () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] | cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ]; } // Rd = Rs Or Rt
void XOR () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] ^ cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ]; } // Rd = Rs Xor Rt
void NOR () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] =~ ( cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] | cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ]); } // Rd = Rs Nor Rt
2009-03-01 21:49:17 +00:00
void SLT () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] < cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ]) ? 1 : 0 ; } // Rd = Rs < Rt (signed)
void SLTU () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = ( cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] < cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ]) ? 1 : 0 ; } // Rd = Rs < Rt (unsigned)
2009-02-09 21:15:56 +00:00
/*********************************************************
* Register mult/div & Register trap logic *
* Format: OP rs, rt *
*********************************************************/
2009-03-01 21:49:17 +00:00
2009-03-22 19:40:43 +00:00
// Signed division "overflows" on (0x80000000 / -1), here (LO = 0x80000000, HI = 0) is returned by MIPS
2010-04-25 00:31:27 +00:00
// in division by zero on MIPS, it appears that:
2009-03-22 19:40:43 +00:00
// LO gets 1 if rs is negative (and the division is signed) and -1 otherwise.
// HI gets the value of rs.
2009-03-01 21:49:17 +00:00
// Result is stored in HI/LO [no arithmetic exceptions]
void DIV ()
{
2009-03-22 19:40:43 +00:00
if ( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] == 0x80000000 && cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ] == 0xffffffff )
{
cpuRegs . LO . SD [ 0 ] = ( s32 ) 0x80000000 ;
cpuRegs . HI . SD [ 0 ] = ( s32 ) 0x0 ;
}
else if ( cpuRegs . GPR . r [ _Rt_ ]. SL [ 0 ] != 0 )
2009-03-01 21:49:17 +00:00
{
2009-02-09 21:15:56 +00:00
cpuRegs . LO . SD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. SL [ 0 ] / cpuRegs . GPR . r [ _Rt_ ]. SL [ 0 ];
cpuRegs . HI . SD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. SL [ 0 ] % cpuRegs . GPR . r [ _Rt_ ]. SL [ 0 ];
}
2009-03-22 19:40:43 +00:00
else
{
cpuRegs . LO . SD [ 0 ] = ( cpuRegs . GPR . r [ _Rs_ ]. SL [ 0 ] < 0 ) ? 1 : - 1 ;
cpuRegs . HI . SD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. SL [ 0 ];
}
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
// Result is stored in HI/LO [no arithmetic exceptions]
void DIVU ()
{
2010-04-25 00:31:27 +00:00
if ( cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ] != 0 )
2009-03-01 21:49:17 +00:00
{
2009-02-19 09:59:02 +00:00
// note: DIVU has no sign extension when assigning back to 64 bits
2009-03-01 21:49:17 +00:00
// note 2: reference material strongly disagrees. (air)
cpuRegs . LO . SD [ 0 ] = ( s32 )( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] / cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ]);
cpuRegs . HI . SD [ 0 ] = ( s32 )( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] % cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ]);
2009-02-09 21:15:56 +00:00
}
2009-03-22 19:40:43 +00:00
else
{
cpuRegs . LO . SD [ 0 ] = - 1 ;
cpuRegs . HI . SD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. SL [ 0 ];
}
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
// Result is written to both HI/LO and to the _Rd_ (Lo only)
void MULT ()
{
s64 res = ( s64 ) cpuRegs . GPR . r [ _Rs_ ]. SL [ 0 ] * cpuRegs . GPR . r [ _Rt_ ]. SL [ 0 ];
2009-02-09 21:15:56 +00:00
2009-02-19 09:59:02 +00:00
// Sign-extend into 64 bits:
2009-03-01 21:49:17 +00:00
cpuRegs . LO . SD [ 0 ] = ( s32 )( res & 0xffffffff );
cpuRegs . HI . SD [ 0 ] = ( s32 )( res >> 32 );
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
if ( _Rd_ ) cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . LO . UD [ 0 ];
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
// Result is written to both HI/LO and to the _Rd_ (Lo only)
void MULTU ()
{
u64 res = ( u64 ) cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] * cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ];
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
// Note: sign-extend into 64 bits even though it's an unsigned mult.
cpuRegs . LO . SD [ 0 ] = ( s32 )( res & 0xffffffff );
cpuRegs . HI . SD [ 0 ] = ( s32 )( res >> 32 );
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
if ( _Rd_ ) cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . LO . UD [ 0 ];
2009-02-09 21:15:56 +00:00
}
/*********************************************************
* Load higher 16 bits of the first word in GPR with imm *
* Format: OP rt, immediate *
*********************************************************/
2010-04-25 00:31:27 +00:00
void LUI () {
if ( ! _Rt_ ) return ;
2009-02-09 21:15:56 +00:00
cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = ( s32 )( cpuRegs . code << 16 );
}
/*********************************************************
* Move from HI/LO to GPR *
* Format: OP rd *
*********************************************************/
void MFHI () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . HI . UD [ 0 ]; } // Rd = Hi
void MFLO () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . LO . UD [ 0 ]; } // Rd = Lo
/*********************************************************
* Move to GPR to HI/LO & Register jump *
* Format: OP rs *
*********************************************************/
void MTHI () { cpuRegs . HI . UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ]; } // Hi = Rs
void MTLO () { cpuRegs . LO . UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ]; } // Lo = Rs
/*********************************************************
* Shift arithmetic with constant shift *
* Format: OP rd, rt, sa *
*********************************************************/
2009-03-01 21:49:17 +00:00
void SRA () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = ( s32 )( cpuRegs . GPR . r [ _Rt_ ]. SL [ 0 ] >> _Sa_ ); } // Rd = Rt >> sa (arithmetic)
void SRL () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = ( s32 )( cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ] >> _Sa_ ); } // Rd = Rt >> sa (logical) [sign extend!!]
2009-02-09 21:15:56 +00:00
void SLL () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = ( s32 )( cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ] << _Sa_ ); } // Rd = Rt << sa
void DSLL () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = ( u64 )( cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] << _Sa_ ); }
void DSLL32 (){ if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = ( u64 )( cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] << ( _Sa_ + 32 ));}
2009-03-01 21:49:17 +00:00
void DSRA () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ] >> _Sa_ ; }
void DSRA32 (){ if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ] >> ( _Sa_ + 32 );}
void DSRL () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] >> _Sa_ ; }
void DSRL32 (){ if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] >> ( _Sa_ + 32 );}
2009-02-09 21:15:56 +00:00
/*********************************************************
* Shift arithmetic with variant register shift *
* Format: OP rd, rt, rs *
*********************************************************/
void SLLV () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = ( s32 )( cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ] << ( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] & 0x1f ));} // Rd = Rt << rs
void SRAV () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = ( s32 )( cpuRegs . GPR . r [ _Rt_ ]. SL [ 0 ] >> ( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] & 0x1f ));} // Rd = Rt >> rs (arithmetic)
void SRLV () { if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = ( s32 )( cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ] >> ( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] & 0x1f ));} // Rd = Rt >> rs (logical)
2010-04-25 00:31:27 +00:00
void DSLLV (){ if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = ( u64 )( cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] << ( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] & 0x3f ));}
2009-02-09 21:15:56 +00:00
void DSRAV (){ if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. SD [ 0 ] = ( s64 )( cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ] >> ( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] & 0x3f ));}
void DSRLV (){ if ( ! _Rd_ ) return ; cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = ( u64 )( cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] >> ( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] & 0x3f ));}
/*********************************************************
* Load and store for GPR *
* Format: OP rt, offset(base) *
*********************************************************/
2009-03-01 21:49:17 +00:00
// Implementation Notes Regarding Memory Operations:
// * It it 'correct' to do all loads into temp variables, even if the destination GPR
// is the zero reg (which nullifies the result). The memory needs to be accessed
// regardless so that hardware registers behave as expected (some clear on read) and
// so that TLB Misses are handled as expected as well.
//
// * Low/High varieties of instructions, such as LWL/LWH, do *not* raise Address Error
// exceptions, since the lower bits of the address are used to determine the portions
// of the address/register operations.
2009-02-09 21:15:56 +00:00
2023-02-10 00:33:57 +10:00
__noinline static void RaiseAddressError ( u32 addr , bool store )
2022-12-28 17:44:34 +10:00
{
const std :: string message ( fmt :: format ( "Address Error, addr=0x{:x} [{}]" , addr , store ? "store" : "load" ));
// TODO: This doesn't actually get raised in the CPU yet.
Console . Error ( message );
Cpu -> CancelInstruction ();
}
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
void LB ()
{
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
s8 temp = memRead8 ( addr );
if ( ! _Rt_ ) return ;
cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ] = temp ;
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
void LBU ()
{
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
u8 temp = memRead8 ( addr );
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
if ( ! _Rt_ ) return ;
cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = temp ;
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
void LH ()
{
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
2009-02-09 21:15:56 +00:00
2023-12-22 19:27:44 +10:00
if ( addr & 1 ) [[unlikely]]
2022-12-28 17:44:34 +10:00
RaiseAddressError ( addr , false );
2009-03-01 21:49:17 +00:00
s16 temp = memRead16 ( addr );
if ( ! _Rt_ ) return ;
cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ] = temp ;
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
void LHU ()
2010-04-25 00:31:27 +00:00
{
2009-03-01 21:49:17 +00:00
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
2009-02-09 21:15:56 +00:00
2023-12-22 19:27:44 +10:00
if ( addr & 1 ) [[unlikely]]
2022-12-28 17:44:34 +10:00
RaiseAddressError ( addr , false );
2009-03-01 21:49:17 +00:00
u16 temp = memRead16 ( addr );
if ( ! _Rt_ ) return ;
cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = temp ;
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
void LW ()
{
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
2009-02-09 21:15:56 +00:00
2023-12-22 19:27:44 +10:00
if ( addr & 3 ) [[unlikely]]
2022-12-28 17:44:34 +10:00
RaiseAddressError ( addr , false );
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
u32 temp = memRead32 ( addr );
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
if ( ! _Rt_ ) return ;
cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ] = ( s32 ) temp ;
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
void LWU ()
2010-04-25 00:31:27 +00:00
{
2009-03-01 21:49:17 +00:00
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
2023-12-22 19:27:44 +10:00
if ( addr & 3 ) [[unlikely]]
2022-12-28 17:44:34 +10:00
RaiseAddressError ( addr , false );
2009-03-01 21:49:17 +00:00
u32 temp = memRead32 ( addr );
2010-04-25 00:31:27 +00:00
2009-03-01 21:49:17 +00:00
if ( ! _Rt_ ) return ;
cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = temp ;
2009-02-09 21:15:56 +00:00
}
2013-06-30 11:43:12 +00:00
static const u32 LWL_MASK [ 4 ] = { 0xffffff , 0x0000ffff , 0x000000ff , 0x00000000 };
static const u32 LWR_MASK [ 4 ] = { 0x000000 , 0xff000000 , 0xffff0000 , 0xffffff00 };
2009-02-25 16:51:05 +00:00
static const u8 LWL_SHIFT [ 4 ] = { 24 , 16 , 8 , 0 };
static const u8 LWR_SHIFT [ 4 ] = { 0 , 8 , 16 , 24 };
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
void LWL ()
{
2009-02-09 21:15:56 +00:00
s32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
u32 shift = addr & 3 ;
2009-03-01 21:49:17 +00:00
2021-09-09 17:36:36 +10:00
u32 mem = memRead32 ( addr & ~ 3 );
2009-03-01 21:49:17 +00:00
if ( ! _Rt_ ) return ;
2009-02-09 21:15:56 +00:00
2021-09-09 17:36:36 +10:00
// ensure the compiler does correct sign extension into 64 bits by using s32
cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ] = ( s32 )(( cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ] & LWL_MASK [ shift ]) |
( mem << LWL_SHIFT [ shift ]));
2009-02-09 21:15:56 +00:00
/*
Mem = 1234. Reg = abcd
2009-02-25 16:51:05 +00:00
(result is always sign extended into the upper 32 bits of the Rt)
2009-02-09 21:15:56 +00:00
0 4bcd (mem << 24) | (reg & 0x00ffffff)
1 34cd (mem << 16) | (reg & 0x0000ffff)
2 234d (mem << 8) | (reg & 0x000000ff)
3 1234 (mem ) | (reg & 0x00000000)
*/
}
2009-03-01 21:49:17 +00:00
void LWR ()
{
2009-02-25 18:34:25 +00:00
s32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
u32 shift = addr & 3 ;
2009-02-25 16:51:05 +00:00
2009-03-01 21:49:17 +00:00
u32 mem = memRead32 ( addr & ~ 3 );
if ( ! _Rt_ ) return ;
2009-02-25 19:29:32 +00:00
// Use unsigned math here, and conditionally sign extend below, when needed.
mem = ( cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ] & LWR_MASK [ shift ]) | ( mem >> LWR_SHIFT [ shift ]);
2009-02-25 16:51:05 +00:00
if ( shift == 0 )
{
// This special case requires sign extension into the full 64 bit dest.
2009-02-25 19:29:32 +00:00
cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ] = ( s32 ) mem ;
2009-02-25 16:51:05 +00:00
}
else
2009-02-25 19:29:32 +00:00
{
// This case sets the lower 32 bits of the target register. Upper
2009-02-25 16:51:05 +00:00
// 32 bits are always preserved.
cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ] = mem ;
}
2009-02-25 19:29:32 +00:00
2009-02-09 21:15:56 +00:00
/*
Mem = 1234. Reg = abcd
2009-02-25 16:51:05 +00:00
0 1234 (mem ) | (reg & 0x00000000) [sign extend into upper 32 bits!]
2009-02-09 21:15:56 +00:00
1 a123 (mem >> 8) | (reg & 0xff000000)
2 ab12 (mem >> 16) | (reg & 0xffff0000)
3 abc1 (mem >> 24) | (reg & 0xffffff00)
*/
}
2009-03-01 21:49:17 +00:00
// dummy variable used as a destination address for writes to the zero register, so
// that the zero register always stays zero.
2021-09-03 01:23:59 -05:00
alignas ( 16 ) static GPR_reg m_dummy_gpr_zero ;
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
// Returns the x86 address of the requested GPR, which is safe for writing. (includes
// special handling for returning a dummy var for GPR0(zero), so that it's value is
// always preserved)
2010-08-09 15:42:13 +00:00
static GPR_reg * gpr_GetWritePtr ( uint gpr )
2009-03-01 21:49:17 +00:00
{
2010-08-09 15:42:13 +00:00
return (( gpr == 0 ) ? & m_dummy_gpr_zero : & cpuRegs . GPR . r [ gpr ]);
2009-03-01 21:49:17 +00:00
}
void LD ()
{
s32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
2010-04-25 00:31:27 +00:00
2023-12-22 19:27:44 +10:00
if ( addr & 7 ) [[unlikely]]
2022-12-28 17:44:34 +10:00
RaiseAddressError ( addr , false );
2009-03-01 21:49:17 +00:00
2022-10-12 23:57:53 +10:00
cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = memRead64 ( addr );
2009-02-09 21:15:56 +00:00
}
2009-02-25 16:51:05 +00:00
static const u64 LDL_MASK [ 8 ] =
2016-06-29 23:00:15 +05:30
{ 0x00ffffffffffffffULL , 0x0000ffffffffffffULL , 0x000000ffffffffffULL , 0x00000000ffffffffULL ,
0x0000000000ffffffULL , 0x000000000000ffffULL , 0x00000000000000ffULL , 0x0000000000000000ULL
2009-02-25 16:51:05 +00:00
};
static const u64 LDR_MASK [ 8 ] =
2016-06-29 23:00:15 +05:30
{ 0x0000000000000000ULL , 0xff00000000000000ULL , 0xffff000000000000ULL , 0xffffff0000000000ULL ,
0xffffffff00000000ULL , 0xffffffffff000000ULL , 0xffffffffffff0000ULL , 0xffffffffffffff00ULL
2009-02-25 16:51:05 +00:00
};
static const u8 LDR_SHIFT [ 8 ] = { 0 , 8 , 16 , 24 , 32 , 40 , 48 , 56 };
static const u8 LDL_SHIFT [ 8 ] = { 56 , 48 , 40 , 32 , 24 , 16 , 8 , 0 };
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
void LDL ()
{
2009-02-09 21:15:56 +00:00
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
u32 shift = addr & 7 ;
2022-10-12 23:57:53 +10:00
u64 mem = memRead64 ( addr & ~ 7 );
2009-03-01 21:49:17 +00:00
if ( ! _Rt_ ) return ;
2010-04-25 00:31:27 +00:00
cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = ( cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] & LDL_MASK [ shift ]) |
2009-02-09 21:15:56 +00:00
( mem << LDL_SHIFT [ shift ]);
}
2009-03-01 21:49:17 +00:00
void LDR ()
{
2009-02-09 21:15:56 +00:00
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
u32 shift = addr & 7 ;
2009-03-01 21:49:17 +00:00
2022-10-12 23:57:53 +10:00
u64 mem = memRead64 ( addr & ~ 7 );
2009-02-09 21:15:56 +00:00
if ( ! _Rt_ ) return ;
2010-04-25 00:31:27 +00:00
cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] = ( cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] & LDR_MASK [ shift ]) |
2009-02-09 21:15:56 +00:00
( mem >> LDR_SHIFT [ shift ]);
}
2009-03-01 21:49:17 +00:00
void LQ ()
{
2009-04-02 13:42:30 +00:00
// MIPS Note: LQ and SQ are special and "silently" align memory addresses, thus
// an address error due to unaligned access isn't possible like it is on other loads/stores.
2010-04-25 00:31:27 +00:00
2009-03-01 21:49:17 +00:00
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
2010-08-09 15:42:13 +00:00
memRead128 ( addr & ~ 0xf , ( u128 * ) gpr_GetWritePtr ( _Rt_ ));
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
void SB ()
2010-04-25 00:31:27 +00:00
{
2009-03-01 21:49:17 +00:00
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
2010-04-25 00:31:27 +00:00
memWrite8 ( addr , cpuRegs . GPR . r [ _Rt_ ]. UC [ 0 ]);
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
void SH ()
2010-04-25 00:31:27 +00:00
{
2009-03-01 21:49:17 +00:00
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
2023-12-22 19:27:44 +10:00
if ( addr & 1 ) [[unlikely]]
2022-12-28 17:44:34 +10:00
RaiseAddressError ( addr , true );
2009-02-09 21:15:56 +00:00
2010-04-25 00:31:27 +00:00
memWrite16 ( addr , cpuRegs . GPR . r [ _Rt_ ]. US [ 0 ]);
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
void SW ()
{
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
2023-12-22 19:27:44 +10:00
if ( addr & 3 ) [[unlikely]]
2022-12-28 17:44:34 +10:00
RaiseAddressError ( addr , true );
2009-02-09 21:15:56 +00:00
2022-12-28 17:44:34 +10:00
memWrite32 ( addr , cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ]);
2009-02-09 21:15:56 +00:00
}
2009-02-25 16:51:05 +00:00
static const u32 SWL_MASK [ 4 ] = { 0xffffff00 , 0xffff0000 , 0xff000000 , 0x00000000 };
static const u32 SWR_MASK [ 4 ] = { 0x00000000 , 0x000000ff , 0x0000ffff , 0x00ffffff };
static const u8 SWR_SHIFT [ 4 ] = { 0 , 8 , 16 , 24 };
static const u8 SWL_SHIFT [ 4 ] = { 24 , 16 , 8 , 0 };
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
void SWL ()
{
2009-02-09 21:15:56 +00:00
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
u32 shift = addr & 3 ;
2009-03-01 21:49:17 +00:00
u32 mem = memRead32 ( addr & ~ 3 );
2009-02-09 21:15:56 +00:00
2009-02-25 16:51:05 +00:00
memWrite32 ( addr & ~ 3 ,
( cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ] >> SWL_SHIFT [ shift ]) |
( mem & SWL_MASK [ shift ])
);
2009-03-01 21:49:17 +00:00
2009-02-09 21:15:56 +00:00
/*
Mem = 1234. Reg = abcd
0 123a (reg >> 24) | (mem & 0xffffff00)
1 12ab (reg >> 16) | (mem & 0xffff0000)
2 1abc (reg >> 8) | (mem & 0xff000000)
3 abcd (reg ) | (mem & 0x00000000)
*/
}
void SWR () {
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
u32 shift = addr & 3 ;
2009-03-01 21:49:17 +00:00
u32 mem = memRead32 ( addr & ~ 3 );
2009-02-09 21:15:56 +00:00
2009-02-25 16:51:05 +00:00
memWrite32 ( addr & ~ 3 ,
( cpuRegs . GPR . r [ _Rt_ ]. UL [ 0 ] << SWR_SHIFT [ shift ]) |
( mem & SWR_MASK [ shift ])
);
2009-02-09 21:15:56 +00:00
/*
Mem = 1234. Reg = abcd
0 abcd (reg ) | (mem & 0x00000000)
1 bcd4 (reg << 8) | (mem & 0x000000ff)
2 cd34 (reg << 16) | (mem & 0x0000ffff)
3 d234 (reg << 24) | (mem & 0x00ffffff)
*/
}
2009-03-01 21:49:17 +00:00
void SD ()
{
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
2023-12-22 19:27:44 +10:00
if ( addr & 7 ) [[unlikely]]
2022-12-28 17:44:34 +10:00
RaiseAddressError ( addr , true );
2009-02-09 21:15:56 +00:00
2022-10-12 23:57:53 +10:00
memWrite64 ( addr , cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ]);
2009-02-09 21:15:56 +00:00
}
2009-02-25 16:51:05 +00:00
static const u64 SDL_MASK [ 8 ] =
2016-06-29 23:00:15 +05:30
{ 0xffffffffffffff00ULL , 0xffffffffffff0000ULL , 0xffffffffff000000ULL , 0xffffffff00000000ULL ,
0xffffff0000000000ULL , 0xffff000000000000ULL , 0xff00000000000000ULL , 0x0000000000000000ULL
2009-02-25 16:51:05 +00:00
};
static const u64 SDR_MASK [ 8 ] =
2016-06-29 23:00:15 +05:30
{ 0x0000000000000000ULL , 0x00000000000000ffULL , 0x000000000000ffffULL , 0x0000000000ffffffULL ,
0x00000000ffffffffULL , 0x000000ffffffffffULL , 0x0000ffffffffffffULL , 0x00ffffffffffffffULL
2009-02-25 16:51:05 +00:00
};
static const u8 SDL_SHIFT [ 8 ] = { 56 , 48 , 40 , 32 , 24 , 16 , 8 , 0 };
static const u8 SDR_SHIFT [ 8 ] = { 0 , 8 , 16 , 24 , 32 , 40 , 48 , 56 };
2009-02-09 21:15:56 +00:00
2009-03-01 21:49:17 +00:00
void SDL ()
{
2009-02-09 21:15:56 +00:00
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
u32 shift = addr & 7 ;
2022-10-12 23:57:53 +10:00
u64 mem = memRead64 ( addr & ~ 7 );
2009-02-25 16:51:05 +00:00
mem = ( cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] >> SDL_SHIFT [ shift ]) |
( mem & SDL_MASK [ shift ]);
2022-10-12 23:57:53 +10:00
memWrite64 ( addr & ~ 7 , mem );
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
void SDR ()
{
2009-02-09 21:15:56 +00:00
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
u32 shift = addr & 7 ;
2022-10-12 23:57:53 +10:00
u64 mem = memRead64 ( addr & ~ 7 );
2009-02-25 16:51:05 +00:00
mem = ( cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] << SDR_SHIFT [ shift ]) |
( mem & SDR_MASK [ shift ]);
2022-10-12 23:57:53 +10:00
memWrite64 ( addr & ~ 7 , mem );
2009-02-09 21:15:56 +00:00
}
2009-03-01 21:49:17 +00:00
void SQ ()
{
2009-04-02 13:42:30 +00:00
// MIPS Note: LQ and SQ are special and "silently" align memory addresses, thus
// an address error due to unaligned access isn't possible like it is on other loads/stores.
2009-02-09 21:15:56 +00:00
2009-04-02 13:42:30 +00:00
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
2010-08-25 15:32:17 +00:00
memWrite128 ( addr & ~ 0xf , cpuRegs . GPR . r [ _Rt_ ]. UQ );
2009-02-09 21:15:56 +00:00
}
/*********************************************************
* Conditional Move *
* Format: OP rd, rs, rt *
*********************************************************/
void MOVZ () {
if ( ! _Rd_ ) return ;
if ( cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] == 0 ) {
cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ];
}
}
void MOVN () {
if ( ! _Rd_ ) return ;
if ( cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ] != 0 ) {
cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ];
}
}
/*********************************************************
* Special purpose instructions *
* Format: OP *
*********************************************************/
2010-01-22 08:49:54 +00:00
// This function is the only one that uses Sifcmd.h in Pcsx2.
#include "Sifcmd.h"
2009-02-09 21:15:56 +00:00
void SYSCALL ()
{
u8 call ;
if ( cpuRegs . GPR . n . v1 . SL [ 0 ] < 0 )
2009-11-23 06:54:24 +00:00
call = ( u8 )( - cpuRegs . GPR . n . v1 . SL [ 0 ]);
2009-02-09 21:15:56 +00:00
else
call = cpuRegs . GPR . n . v1 . UC [ 0 ];
2010-04-27 16:23:08 +00:00
BIOS_LOG ( "Bios call: %s (%x)" , R5900 :: bios [ call ], call );
2009-02-09 21:15:56 +00:00
2016-08-18 20:10:08 +05:30
switch ( static_cast < Syscall > ( call ))
{
case Syscall :: SetGsCrt :
{
2023-01-14 22:29:42 -05:00
//Function "SetGsCrt(Interlace, Mode, Field)"
//Useful for fetching information of interlace/video/field display parameters of the Graphics Synthesizer
2016-08-18 19:37:35 +05:30
2023-01-14 22:29:42 -05:00
gsIsInterlaced = cpuRegs . GPR . n . a0 . UL [ 0 ] & 1 ;
bool gsIsFrameMode = cpuRegs . GPR . n . a2 . UL [ 0 ] & 1 ;
const char * inter = ( gsIsInterlaced ) ? "Interlaced" : "Progressive" ;
const char * field = ( gsIsFrameMode ) ? "FRAME" : "FIELD" ;
std :: string mode ;
// Warning info might be incorrect!
switch ( cpuRegs . GPR . n . a1 . UC [ 0 ])
{
case 0x0 :
case 0x2 :
mode = "NTSC 640x448 @ 59.940 (59.82)" ; gsSetVideoMode ( GS_VideoMode :: NTSC ); break ;
2009-02-09 21:15:56 +00:00
2023-01-14 22:29:42 -05:00
case 0x1 :
case 0x3 :
mode = "PAL 640x512 @ 50.000 (49.76)" ; gsSetVideoMode ( GS_VideoMode :: PAL ); break ;
2009-02-09 21:15:56 +00:00
2023-01-14 22:29:42 -05:00
case 0x1A : mode = "VESA 640x480 @ 59.940" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
case 0x1B : mode = "VESA 640x480 @ 72.809" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
case 0x1C : mode = "VESA 640x480 @ 75.000" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
case 0x1D : mode = "VESA 640x480 @ 85.008" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
2009-02-09 21:15:56 +00:00
2023-01-14 22:29:42 -05:00
case 0x2A : mode = "VESA 800x600 @ 56.250" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
case 0x2B : mode = "VESA 800x600 @ 60.317" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
case 0x2C : mode = "VESA 800x600 @ 72.188" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
case 0x2D : mode = "VESA 800x600 @ 75.000" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
case 0x2E : mode = "VESA 800x600 @ 85.061" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
2015-10-26 17:53:25 +01:00
2023-01-14 22:29:42 -05:00
case 0x3B : mode = "VESA 1024x768 @ 60.004" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
case 0x3C : mode = "VESA 1024x768 @ 70.069" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
case 0x3D : mode = "VESA 1024x768 @ 75.029" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
case 0x3E : mode = "VESA 1024x768 @ 84.997" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
2015-10-26 17:53:25 +01:00
2023-01-14 22:29:42 -05:00
case 0x4A : mode = "VESA 1280x1024 @ 63.981" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
case 0x4B : mode = "VESA 1280x1024 @ 79.976" ; gsSetVideoMode ( GS_VideoMode :: VESA ); break ;
2015-10-26 17:53:25 +01:00
2023-01-14 22:29:42 -05:00
case 0x50 : mode = "SDTV 720x480 @ 59.94" ; gsSetVideoMode ( GS_VideoMode :: SDTV_480P ); break ;
case 0x51 : mode = "HDTV 1920x1080 @ 60.00" ; gsSetVideoMode ( GS_VideoMode :: HDTV_1080I ); break ;
case 0x52 : mode = "HDTV 1280x720 @ ??.???" ; gsSetVideoMode ( GS_VideoMode :: HDTV_720P ); break ;
case 0x53 : mode = "SDTV 768x576 @ ??.???" ; gsSetVideoMode ( GS_VideoMode :: SDTV_576P ); break ;
case 0x54 : mode = "HDTV 1920x1080 @ ??.???" ; gsSetVideoMode ( GS_VideoMode :: HDTV_1080P ); break ;
2015-10-26 17:53:25 +01:00
2023-01-14 22:29:42 -05:00
case 0x72 :
case 0x82 :
mode = "DVD NTSC 640x448 @ ??.???" ; gsSetVideoMode ( GS_VideoMode :: DVD_NTSC ); break ;
case 0x73 :
case 0x83 :
mode = "DVD PAL 720x480 @ ??.???" ; gsSetVideoMode ( GS_VideoMode :: DVD_PAL ); break ;
2015-10-26 17:53:25 +01:00
2023-01-14 22:29:42 -05:00
default :
DevCon . Error ( "Mode %x is not supported. Report me upstream" , cpuRegs . GPR . n . a1 . UC [ 0 ]);
gsSetVideoMode ( GS_VideoMode :: Unknown );
}
DevCon . Warning ( "Set GS CRTC configuration. %s %s (%s)" , mode . c_str (), inter , field );
}
break ;
case Syscall :: ExecPS2 :
{
if ( DebugInterface :: getPauseOnEntry ())
{
CBreakPoints :: AddBreakPoint ( BREAKPOINT_EE , cpuRegs . GPR . n . a0 . UL [ 0 ], true );
DebugInterface :: setPauseOnEntry ( false );
}
}
break ;
2024-04-29 15:33:58 +10:00
case Syscall :: RFU060 :
if ( CHECK_EXTRAMEM && cpuRegs . GPR . n . a1 . UL [ 0 ] == 0xFFFFFFFF )
{
cpuRegs . GPR . n . a1 . UL [ 0 ] = Ps2MemSize :: ExposedRam - cpuRegs . GPR . n . a2 . SL [ 0 ];
}
break ;
2021-10-14 22:27:02 +01:00
case Syscall :: SetOsdConfigParam :
2024-04-21 03:28:58 +01:00
// The whole thing gets written back to BIOS memory, so it'll be in the right place, no need to continue HLEing
2021-10-14 22:27:02 +01:00
AllowParams1 = true ;
break ;
2020-11-21 16:31:36 +00:00
case Syscall :: GetOsdConfigParam :
2024-04-21 03:28:58 +01:00
if ( ! NoOSD && ! AllowParams1 )
2020-11-18 01:44:00 +00:00
{
2024-04-21 03:28:58 +01:00
ReadOSDConfigParames ();
2020-11-22 21:25:04 +00:00
u32 memaddr = cpuRegs . GPR . n . a0 . UL [ 0 ];
2022-09-13 21:20:25 -05:00
2024-04-21 03:28:58 +01:00
memWrite32 ( memaddr , configParams1 . UL [ 0 ]);
// Call the set function, as we need to set this back to the BIOS storage position.
if ( cpuRegs . GPR . n . v1 . SL [ 0 ] < 0 )
cpuRegs . GPR . n . v1 . SL [ 0 ] = - Syscall :: SetOsdConfigParam ;
else
cpuRegs . GPR . n . v1 . UC [ 0 ] = Syscall :: SetOsdConfigParam ;
2020-11-18 16:01:39 +00:00
2024-04-21 03:28:58 +01:00
AllowParams1 = true ;
2020-11-18 01:44:00 +00:00
}
2020-11-18 16:01:39 +00:00
break ;
2021-10-14 22:27:02 +01:00
case Syscall :: SetOsdConfigParam2 :
2024-04-21 03:28:58 +01:00
if ( ! AllowParams2 )
{
ReadOSDConfigParames ();
u32 memaddr = cpuRegs . GPR . n . a0 . UL [ 0 ];
u32 size = cpuRegs . GPR . n . a1 . UL [ 0 ];
u32 offset = cpuRegs . GPR . n . a2 . UL [ 0 ];
if ( offset == 0 && size >= 4 )
AllowParams2 = true ;
for ( u32 i = 0 ; i < size ; i ++ )
{
if ( offset >= 4 )
break ;
configParams2 . UC [ offset ++ ] = memRead8 ( memaddr ++ );
}
}
2021-10-14 22:27:02 +01:00
break ;
2020-11-21 16:31:36 +00:00
case Syscall :: GetOsdConfigParam2 :
2023-06-13 22:43:11 +10:00
if ( ! NoOSD && ! AllowParams2 )
2020-11-18 16:01:39 +00:00
{
2024-04-21 03:28:58 +01:00
ReadOSDConfigParames ();
2020-11-22 21:25:04 +00:00
u32 memaddr = cpuRegs . GPR . n . a0 . UL [ 0 ];
2024-04-21 03:28:58 +01:00
u32 size = cpuRegs . GPR . n . a1 . UL [ 0 ];
u32 offset = cpuRegs . GPR . n . a2 . UL [ 0 ];
2015-10-26 17:53:25 +01:00
2024-04-21 03:28:58 +01:00
if ( offset + size > 2 )
Console . Warning ( "Warning: GetOsdConfigParam2 Reading extended language/version configs, may be incorrect!" );
2020-11-18 16:01:39 +00:00
2024-04-21 03:28:58 +01:00
for ( u32 i = 0 ; i < size ; i ++ )
{
if ( offset >= 4 )
memWrite8 ( memaddr ++ , 0 );
else
memWrite8 ( memaddr ++ , configParams2 . UC [ offset ++ ]);
}
2020-11-18 16:01:39 +00:00
return ;
}
break ;
2016-08-18 20:10:08 +05:30
case Syscall :: SetVTLBRefillHandler :
2015-10-26 17:53:25 +01:00
DevCon . Warning ( "A tlb refill handler is set. New handler %x" , ( u32 * ) PSM ( cpuRegs . GPR . n . a1 . UL [ 0 ]));
break ;
2021-05-09 22:08:56 -04:00
case Syscall :: StartThread :
case Syscall :: ChangeThreadPriority :
{
2021-10-08 13:12:03 +02:00
if ( CurrentBiosInformation . eeThreadListAddr == 0 )
2021-05-09 22:08:56 -04:00
{
u32 offset = 0x0 ;
// Suprisingly not that slow :)
while ( offset < 0x5000 ) // I find that the instructions are in between 0x4000 -> 0x5000
{
u32 addr = 0x80000000 + offset ;
const u32 inst1 = memRead32 ( addr );
const u32 inst2 = memRead32 ( addr += 4 );
const u32 inst3 = memRead32 ( addr += 4 );
2015-10-26 17:53:25 +01:00
2021-05-09 22:08:56 -04:00
if ( ThreadListInstructions [ 0 ] == inst1 && // sw v0,0x0(v0)
ThreadListInstructions [ 1 ] == inst2 && // no-op
ThreadListInstructions [ 2 ] == inst3 ) // no-op
{
// We've found the instruction pattern!
// We (well, I) know that the thread address is always 0x8001 + the immediate of the 6th instruction from here
const u32 op = memRead32 ( 0x80000000 + offset + ( sizeof ( u32 ) * 6 ));
2021-10-08 13:12:03 +02:00
CurrentBiosInformation . eeThreadListAddr = 0x80010000 + static_cast < u16 > ( op ) - 8 ; // Subtract 8 because the address here is offset by 8.
DevCon . WriteLn ( "BIOS: Successfully found the instruction pattern. Assuming the thread list is here: %0x" , CurrentBiosInformation . eeThreadListAddr );
2021-05-09 22:08:56 -04:00
break ;
}
offset += 4 ;
}
2021-10-08 13:12:03 +02:00
if ( ! CurrentBiosInformation . eeThreadListAddr )
2021-05-09 22:08:56 -04:00
{
// We couldn't find the address
2021-10-08 13:12:03 +02:00
CurrentBiosInformation . eeThreadListAddr = - 1 ;
2021-05-09 22:08:56 -04:00
// If you're here because a user has reported this message, this means that the instruction pattern is not present on their bios, or it is aligned weirdly.
Console . Warning ( "BIOS Warning: Unable to get a thread list offset. The debugger thread and stack frame views will not be functional." );
}
}
}
break ;
2016-08-18 20:10:08 +05:30
case Syscall :: sceSifSetDma :
2015-10-26 17:53:25 +01:00
// The only thing this code is used for is the one log message, so don't execute it if we aren't logging bios messages.
2024-10-13 16:04:33 -04:00
if ( TraceActive ( EE . Bios ))
2015-10-26 17:53:25 +01:00
{
//struct t_sif_cmd_header *hdr;
//struct t_sif_rpc_bind *bind;
//struct t_rpc_server_data *server;
int n_transfer ;
u32 addr ;
//int sid;
n_transfer = cpuRegs . GPR . n . a1 . UL [ 0 ] - 1 ;
if ( n_transfer >= 0 )
{
addr = cpuRegs . GPR . n . a0 . UL [ 0 ] + n_transfer * sizeof ( t_sif_dma_transfer );
2024-05-08 13:39:40 +02:00
t_sif_dma_transfer * dmat = ( t_sif_dma_transfer * ) PSM ( addr );
2015-10-26 17:53:25 +01:00
BIOS_LOG ( "bios_%s: n_transfer=%d, size=%x, attr=%x, dest=%x, src=%x" ,
R5900 :: bios [ cpuRegs . GPR . n . v1 . UC [ 0 ]], n_transfer ,
dmat -> size , dmat -> attr ,
dmat -> dest , dmat -> src );
}
}
break ;
2016-08-18 20:10:08 +05:30
case Syscall :: Deci2Call :
{
if ( cpuRegs . GPR . n . a0 . UL [ 0 ] == 0x10 )
{
eeConLog ( ShiftJIS_ConvertString (( char * ) PSM ( memRead32 ( cpuRegs . GPR . n . a1 . UL [ 0 ]))));
}
else
__Deci2Call ( cpuRegs . GPR . n . a0 . UL [ 0 ], ( u32 * ) PSM ( cpuRegs . GPR . n . a1 . UL [ 0 ]));
2018-08-27 01:56:48 -10:00
break ;
2016-08-18 20:10:08 +05:30
}
2018-08-27 01:56:48 -10:00
case Syscall :: sysPrintOut :
{
if ( cpuRegs . GPR . n . a0 . UL [ 0 ] != 0 )
{
// TODO: Only supports 7 format arguments. Need to read from the stack for more.
// Is there a function which collects PS2 arguments?
2021-01-09 16:12:20 -05:00
char * fmt = ( char * ) PSM ( cpuRegs . GPR . n . a0 . UL [ 0 ]);
u64 regs [ 7 ] = {
2018-08-27 01:56:48 -10:00
cpuRegs . GPR . n . a1 . UL [ 0 ],
cpuRegs . GPR . n . a2 . UL [ 0 ],
cpuRegs . GPR . n . a3 . UL [ 0 ],
cpuRegs . GPR . n . t0 . UL [ 0 ],
cpuRegs . GPR . n . t1 . UL [ 0 ],
cpuRegs . GPR . n . t2 . UL [ 0 ],
2021-01-09 16:12:20 -05:00
cpuRegs . GPR . n . t3 . UL [ 0 ],
};
// Pretty much what this does is find instances of string arguments and remaps them.
// Instead of the addresse(s) being relative to the PS2 address space, make them relative to program memory.
2022-09-13 21:20:25 -05:00
// (This fixes issue #2865)
2021-01-09 16:12:20 -05:00
int curRegArg = 0 ;
for ( int i = 0 ; 1 ; i ++ )
{
if ( fmt [ i ] == '\0' )
break ;
if ( fmt [ i ] == '%' )
{
// The extra check here is to be compatible with "%%s"
if ( i == 0 || fmt [ i - 1 ] != '%' ) {
if ( fmt [ i + 1 ] == 's' ) {
regs [ curRegArg ] = ( u64 ) PSM ( regs [ curRegArg ]); // PS2 Address -> PCSX2 Address
}
curRegArg ++ ;
}
}
}
2024-01-04 15:21:47 -05:00
char buf [ 2048 ];
snprintf ( buf , sizeof ( buf ), fmt ,
2021-01-09 16:12:20 -05:00
regs [ 0 ],
regs [ 1 ],
regs [ 2 ],
regs [ 3 ],
regs [ 4 ],
regs [ 5 ],
regs [ 6 ]
2018-08-27 01:56:48 -10:00
);
2024-01-04 15:21:47 -05:00
eeConLog ( buf );
2018-08-27 01:56:48 -10:00
}
break ;
}
2024-04-29 15:33:58 +10:00
case Syscall :: GetMemorySize :
if ( CHECK_EXTRAMEM )
{
cpuRegs . GPR . n . v0 . UL [ 0 ] = Ps2MemSize :: ExposedRam ;
return ;
}
break ;
2022-09-13 21:20:25 -05:00
2016-08-18 20:10:08 +05:30
2015-10-26 17:53:25 +01:00
default :
break ;
2009-02-09 21:15:56 +00:00
}
cpuRegs . pc -= 4 ;
cpuException ( 0x20 , cpuRegs . branch );
}
2014-08-14 03:21:09 -04:00
void BREAK () {
2009-02-09 21:15:56 +00:00
cpuRegs . pc -= 4 ;
cpuException ( 0x24 , cpuRegs . branch );
}
2014-08-14 03:21:09 -04:00
void MFSA () {
2009-02-09 21:15:56 +00:00
if ( ! _Rd_ ) return ;
2018-09-04 20:45:28 +01:00
cpuRegs . GPR . r [ _Rd_ ]. UD [ 0 ] = ( u64 ) cpuRegs . sa ;
2009-02-09 21:15:56 +00:00
}
2014-08-14 03:21:09 -04:00
void MTSA () {
2018-09-04 20:45:28 +01:00
cpuRegs . sa = ( u32 ) cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ];
2009-02-09 21:15:56 +00:00
}
2010-04-25 00:31:27 +00:00
// SNY supports three basic modes, two which synchronize memory accesses (related
2009-03-01 21:49:17 +00:00
// to the cache) and one which synchronizes the instruction pipeline (effectively
// a stall in either case). Our emulation model does not track EE-side pipeline
// status or stalls, nor does it implement the CACHE. Thus SYNC need do nothing.
2014-08-14 03:21:09 -04:00
void SYNC ()
2009-02-09 21:15:56 +00:00
{
}
2009-03-01 21:49:17 +00:00
// Used to prefetch data into the EE's cache, or schedule a dirty write-back.
// CACHE is not emulated at this time (nor is there any need to emulate it), so
// this function does nothing in the context of our emulator.
2014-08-14 03:21:09 -04:00
void PREF ()
2009-02-09 21:15:56 +00:00
{
}
2010-03-15 14:15:40 +00:00
static void trap ( u16 code = 0 )
{
cpuRegs . pc -= 4 ;
Console . Warning ( "Trap exception at 0x%08x" , cpuRegs . pc );
cpuException ( 0x34 , cpuRegs . branch );
}
2009-02-09 21:15:56 +00:00
/*********************************************************
* Register trap *
* Format: OP rs, rt *
*********************************************************/
2010-03-15 14:15:40 +00:00
void TGE () { if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] >= cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ]) trap ( _TrapCode_ ); }
void TGEU () { if ( cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] >= cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ]) trap ( _TrapCode_ ); }
void TLT () { if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] < cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ]) trap ( _TrapCode_ ); }
void TLTU () { if ( cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] < cpuRegs . GPR . r [ _Rt_ ]. UD [ 0 ]) trap ( _TrapCode_ ); }
void TEQ () { if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] == cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ]) trap ( _TrapCode_ ); }
void TNE () { if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] != cpuRegs . GPR . r [ _Rt_ ]. SD [ 0 ]) trap ( _TrapCode_ ); }
2009-02-09 21:15:56 +00:00
/*********************************************************
* Trap with immediate operand *
* Format: OP rs, rt *
*********************************************************/
2010-03-15 14:15:40 +00:00
void TGEI () { if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] >= _Imm_ ) trap (); }
void TLTI () { if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] < _Imm_ ) trap (); }
void TEQI () { if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] == _Imm_ ) trap (); }
void TNEI () { if ( cpuRegs . GPR . r [ _Rs_ ]. SD [ 0 ] != _Imm_ ) trap (); }
void TGEIU () { if ( cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] >= ( u64 ) _Imm_ ) trap (); }
void TLTIU () { if ( cpuRegs . GPR . r [ _Rs_ ]. UD [ 0 ] < ( u64 ) _Imm_ ) trap (); }
2009-02-09 21:15:56 +00:00
/*********************************************************
* Sa intructions *
* Format: OP rs, rt *
*********************************************************/
void MTSAB () {
2018-09-04 20:45:28 +01:00
cpuRegs . sa = (( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] & 0xF ) ^ ( _Imm_ & 0xF ));
2009-02-09 21:15:56 +00:00
}
void MTSAH () {
2018-09-04 20:45:28 +01:00
cpuRegs . sa = (( cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] & 0x7 ) ^ ( _Imm_ & 0x7 )) << 1 ;
2009-02-09 21:15:56 +00:00
}
} } } // end namespace R5900::Interpreter::OpcodeImpl