#include "psx.h"
#include "dis.h"
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <intrin.h>
#include <Windows.h>
bool dotrace = false;
void PSX::reset()
{
cpu.p_fetch.in_fetch_addr = 0xbfc00000;
}
void PSX::poweron(eConsoleType type)
{
memset(this,0,sizeof(this));
memset(&cpu.cp0,0,sizeof(cpu.cp0));
cpu.cp0.SR.BEV = 1;
cpu.cp0.SR.TS = 1;
cpu.cp0.PRid = 0x300;
cpu.unit_muldiv.timer = 0;
cpu.p_alu.out_pc.enabled = false;
cpu.p_alu.in_pc = 0;
cpu.p_alu.decode.instr.value = 0;
cpu.p_alu.decode.op = eOP_NULL;
cpu.p_mem.in_from_alu.op = CPU::eMemOp_None;
sio[0].Reset();
sio[1].Reset();
sched.null.time = 0xFFFFFFFF;
sched.head = eScheduleItemType_null;
sched.gpu.time = PSX_CLOCK / 60;
sched.insert(eScheduleItemType_gpu);
if(type == eConsoleType_DTL) config.ram_size = 8*1024*1024;
else config.ram_size = 2*1024*1024;
config.ram_mask = config.ram_size - 1;
}
PSX::eScheduleItemType PSX::SCHED::dequeue()
{
eScheduleItemType ret = head;
head = items[head].next;
items[ret].next = eScheduleItemType_NIL;
items[ret].prev = eScheduleItemType_NIL;
return ret;
}
void PSX::SCHED::remove(eScheduleItemType todoType)
{
IScheduleItem &todo = items[todoType];
eScheduleItemType prevType = todo.prev;
eScheduleItemType nextType = todo.next;
if(prevType != eScheduleItemType_NIL) items[prevType].next = nextType;
if(nextType != eScheduleItemType_NIL) items[nextType].prev = prevType;
if(head == todoType)
head = nextType;
}
void PSX::SCHED::insert(eScheduleItemType todoType)
{
IScheduleItem &todo = items[todoType];
assert(todo.prev == eScheduleItemType_NIL && todo.next == eScheduleItemType_NIL);
u32 todo_time = todo.time;
eScheduleItemType prevType = eScheduleItemType_NIL;
eScheduleItemType currType = head;
while(items[currType].time < todo_time)
{
prevType = currType;
currType = items[currType].next;
}
if(prevType == eScheduleItemType_NIL)
{
items[todoType].next = head;
items[head].prev = todoType;
head = todoType;
}
else
{
items[todoType].next = items[prevType].next;
items[prevType].next = todoType;
items[todoType].prev = prevType;
items[currType].prev = todoType;
}
}
void PSX::exec_shed(eScheduleItemType type)
{
switch(type)
{
case eScheduleItemType_null:
break;
case eScheduleItemType_test:
printf("TEST!!!\n");
sched.test.time += 100000;
sched.insert(eScheduleItemType_test);
break;
case eScheduleItemType_gpu:
sched.gpu.time += PSX_CLOCK / 60;
sched.insert(eScheduleItemType_gpu);
vblank_trigger();
break;
}
}
void PSX::exec_cycle()
{
while(counter >= sched.items[sched.head].time)
{
eScheduleItemType todo = sched.dequeue();
exec_shed(todo);
}
sched.nextTime = sched.items[sched.head].time;
if(irq.flags.value & irq.mask.value)
{
dotrace = true;
cpu_exception(CPU::eException_INT, cpu.p_alu.in_pc);
cpu.p_alu.decode.op = eOP_NULLIFIED;
}
while(counter < sched.nextTime)
cpu_exec_cycle();
}
void PSX::vblank_trigger()
{
irq.flags.vsync = 1;
irq_update();
}
void PSX::cpu_wr_quick(u8* const buf, const int size, const u32 addr, const u32 val)
{
if(size==1) buf[addr] = val;
else if(size==2) {
*(u16*)&buf[addr] = val;
}
else {
*(u32*)&buf[addr] = val;
}
}
u32 PSX::cpu_rd_quick(const u8* const buf, const int size, const u32 addr)
{
if(size==1) return buf[addr];
else if(size==2) {
return *(u16*)&buf[addr];
}
else {
return *(u32*)&buf[addr];
}
}
u32 PSX::cpu_rd_ram(const int size, const u32 addr)
{
if(size==1)
return ram[addr];
else if(size==2) {
return *(u16*)&ram[addr];
}
else {
return *(u32*)&ram[addr];
}
}
void PSX::cpu_wr_ram(const int size, const u32 addr, const u32 val)
{
if(size==1)
ram[addr] = val;
else if(size==2) {
*(u16*)&ram[addr] = val;
}
else {
*(u32*)&ram[addr] = val;
}
}
void PSX::cpu_wr_scratch(const int size, const u32 addr, const u32 val) { cpu_wr_quick(scratch,size,addr,val); }
u32 PSX::cpu_rd_scratch(const int size, const u32 addr) { return cpu_rd_quick(scratch,size,addr); }
void PSX::cpu_wr_bios(const int size, const u32 addr, const u32 val) { cpu_wr_quick(bios,size,addr,val); }
u32 PSX::cpu_rd_bios(const int size, const u32 addr) { return cpu_rd_quick(bios,size,addr); }
void PSX::cpu_wr_pio(const int size, const u32 addr, const u32 val) { cpu_wr_quick(pio,size,addr,val); }
u32 PSX::cpu_rd_pio(const int size, const u32 addr) { return cpu_rd_quick(pio,size,addr); }
template<int size> void PSX::cpu_wrmem(u32 addr, const u32 val) { cpu_wrmem<size,false>(addr,val); }
template<int size, bool POKE> void PSX::cpu_wrmem(u32 addr, const u32 val)
{
if(!POKE && cpu.cp0.SR.IsC)
return;
if(addr<0x00800000) { cpu_wr_ram(size,addr&config.ram_mask,val); return; }
if(addr<0x1F000000) goto BUS_ERROR;
if(addr<0x1F010000) { cpu_wr_pio(size,addr&PIO_MASK,val); return; }
if(addr<0x1F800000) goto BUS_ERROR;
if(addr<0x1F800400) { cpu_wr_scratch(size,addr&SCRATCH_MASK,val); return; }
if(addr<0x1F801000) goto BUS_ERROR;
if(addr<0x1FC00000) { cpu_wr_hwreg(size,addr,val); return; }
if(addr<0x1FC80000) goto BUS_ERROR;
if(addr<0x80000000) goto BUS_ERROR;
if(addr<0x80800000) { cpu_wr_ram(size,addr&config.ram_mask,val); return; }
if(addr<0x9F000000) goto BUS_ERROR;
if(addr<0x9F010000) { cpu_wr_pio(size,addr&PIO_MASK,val); return; }
if(addr<0x9FC00000) goto BUS_ERROR;
if(addr<0x9FC80000) goto BUS_ERROR;
if(addr<0xA0000000) goto BUS_ERROR;
if(addr<0xA0800000) { cpu_wr_ram(size,addr&config.ram_mask,val); return; }
if(addr<0xBF000000) goto BUS_ERROR;
if(addr<0xBF010000) { cpu_wr_pio(size,addr&PIO_MASK,val); return; }
if(addr<0xBFC00000) goto BUS_ERROR;
if(addr<0xBFC80000) { if(POKE) { cpu_wr_bios(size,addr&BIOS_MASK,val); return; } else goto BUS_ERROR; }
if(addr == 0xFFFE0130)
{
return;
}
goto BUS_ERROR;
BUS_ERROR:
DEBUG("bus error exception (write) at 0x%08X\n",addr);
}
void PSX::patch(const u32 addr, const u32 val)
{
cpu_wrmem<4,true>(addr,val);
}
void PSX::spu_wr(const int size, const u32 addr, const u32 val)
{
DEBUG_HWREG("spu write size %d addr %08X = %08X\n",size,addr,val);
}
u32 PSX::spu_rd(const int size, const u32 addr)
{
u32 ret = 0;
DEBUG_HWREG("spu read size %d addr %08X = %08X\n",size,addr,ret);
return 0;
}
void PSX::irq_wr(const int size, const u32 addr, const u32 val)
{
if(addr == 0)
{
irq.flags.value &= ~(val&IRQ::WIRE_MASK);
}
else if(addr == 4)
{
irq.mask.value = val & IRQ::WIRE_MASK;
}
else assert(false);
irq_update();
}
u32 PSX::irq_rd(const int size, const u32 addr)
{
if(addr == 0)
return irq.flags.value;
else if(addr == 4)
return irq.mask.value;
else assert(false);
}
void PSX::irq_update()
{
sched.escape();
}
void PSX::cpu_wr_hwreg(const int size, const u32 addr, const u32 val)
{
if(addr>=0x1F801C00 && addr <= 0x1F801DFF)
{
spu_wr(size,addr,val);
return;
}
else if(addr>=0x1F801040 && addr < 0x1F80105F)
{
sio_wr(size,addr,val);
return;
}
else switch(addr)
{
case 0x1F801000: sysregs.biosInit[0] = val; break;
case 0x1F801004: sysregs.biosInit[1] = val; break;
case 0x1F801008: sysregs.biosInit[2] = val; break;
case 0x1F80100C: sysregs.biosInit[3] = val; break;
case 0x1F801010: sysregs.biosInit[4] = val; break;
case 0x1F801014: sysregs.biosInit[5] = val; break;
case 0x1F801018: sysregs.biosInit[6] = val; break;
case 0x1F80101C: sysregs.biosInit[7] = val; break;
case 0x1F801020: sysregs.biosInit[8] = val; break;
case 0x1F801070: irq_wr(size, 0, val); break;
case 0x1F801071: assert(false);
case 0x1F801072: assert(false);
case 0x1F801073: assert(false);
case 0x1F801074: irq_wr(size, 4, val); break;
case 0x1F801075: assert(false);
case 0x1F801076: assert(false);
case 0x1F801077: assert(false);
default:
DEBUG_HWREG("UNKNOWN ");
}
DEBUG_HWREG("hwreg write size %d addr %08X = %08X\n",size,addr,val);
}
u32 PSX::cpu_rd_hwreg(const int size, const u32 addr)
{
u32 ret = 0;
if(addr>=0x1F801C00 && addr <= 0x1F801DFF)
{
return spu_rd(size,addr);
}
else if(addr>=0x1F801040 && addr < 0x1F80105F)
{
return sio_rd(size,addr);
}
else switch(addr)
{
case 0x1F801000: ret = sysregs.biosInit[0]; break;
case 0x1F801004: ret = sysregs.biosInit[1]; break;
case 0x1F801008: ret = sysregs.biosInit[2]; break;
case 0x1F80100C: ret = sysregs.biosInit[3]; break;
case 0x1F801010: ret = sysregs.biosInit[4]; break;
case 0x1F801014: ret = sysregs.biosInit[5]; break;
case 0x1F801018: ret = sysregs.biosInit[6]; break;
case 0x1F80101C: ret = sysregs.biosInit[7]; break;
case 0x1F801020: ret = sysregs.biosInit[8]; break;
case 0x1F801070: ret = irq_rd(size, 0); break;
case 0x1F801071: assert(false);
case 0x1F801072: assert(false);
case 0x1F801073: assert(false);
case 0x1F801074: ret = irq_rd(size, 4); break;
case 0x1F801075: assert(false);
case 0x1F801076: assert(false);
case 0x1F801077: assert(false);
default:
DEBUG_HWREG("UNKNOWN ");
break;
}
DEBUG_HWREG("hwreg read size %d addr %08X = %08X\n",size,addr,ret);
return ret;
}
template<int size> u32 PSX::cpu_rdmem(const u32 addr)
{
if(addr<0x00800000) return cpu_rd_ram(size,addr&config.ram_mask);
if(addr<0x1F000000) goto BUS_ERROR;
if(addr<0x1F010000) return cpu_rd_pio(size,addr&PIO_MASK);
if(addr<0x1F800000) goto BUS_ERROR;
if(addr<0x1F800400) return cpu_rd_scratch(size,addr&SCRATCH_MASK);
if(addr<0x1F801000) goto BUS_ERROR;
if(addr<0x1FC00000) return cpu_rd_hwreg(size,addr);
if(addr<0x1FC80000) return cpu_rd_bios(size,addr&BIOS_MASK);
if(addr<0x80000000) goto BUS_ERROR;
if(addr<0x80800000) return cpu_rd_ram(size,addr&config.ram_mask);
if(addr<0x9F000000) goto BUS_ERROR;
if(addr<0x9F010000) return cpu_rd_pio(size,addr&PIO_MASK);
if(addr<0x9FC00000) goto BUS_ERROR;
if(addr<0x9FC80000) return cpu_rd_bios(size,addr&BIOS_MASK);
if(addr<0xA0000000) goto BUS_ERROR;
if(addr<0xA0800000) return cpu_rd_ram(size,addr&config.ram_mask);
if(addr<0xBF000000) goto BUS_ERROR;
if(addr<0xBF010000) return cpu_rd_pio(size,addr&PIO_MASK);
if(addr<0xBFC00000) goto BUS_ERROR;
if(addr<0xBFC80000) return cpu_rd_bios(size,addr&BIOS_MASK);
goto BUS_ERROR;
BUS_ERROR:
DEBUG("bus error exception (read) at 0x%08X\n",addr);
return 0;
}
void PSX::cpu_copz_mtc(const u32 z, const u32 rd, const u32 value)
{
if(z==0) cpu_cop0_mtc(rd,value);
}
void PSX::cpu_cop0_mtc(const u32 rd, const u32 value)
{
cpu.cp0.r[rd] = value;
cpu.cp0.SR.value &= ~CPU::SR_REG::ZERO_MASK;
}
u32 PSX::cpu_copz_mfc(const u32 z, const u32 rd)
{
if(z==0) return cpu_cop0_mfc(rd);
return 0;
}
u32 PSX::cpu_cop0_mfc(const u32 rd)
{
return cpu.cp0.r[rd];
}
u32 PSX::cpu_fetch(const u32 addr)
{
return cpu_rdmem<4>(addr);
}
void PSX::cpu_run_alu_bioshack()
{
const u32 ram_pc = cpu.p_alu.in_pc & 0x0FFFFFFF;
const u32 function = cpu.regs.t1;
switch(function)
{
case 0x00: break;
case 0x16: break;
case 0x18: break;
case 0x35:
if(ENABLE_CONOUT)
{
u8* const raw_addr = ram + (cpu.regs.a1 & config.ram_mask);
const u32 len = cpu.regs.a2;
for(u32 i=0;i<len;i++)
fputc(raw_addr[i],stdout);
}
break;
case 0x3C:
if(ENABLE_CONOUT) cpu_run_alu_bioshack_putchar(cpu.regs.a0);
break;
case 0x3D:
if(ENABLE_CONOUT) cpu_run_alu_bioshack_putchar(cpu.regs.a0);
case 0x3F:
break;
default:
printf("unknown bios call: 0x%02X\n", function);
break;
}
return;
}
void PSX::cpu_run_alu_bioshack_putchar(const u32 regval)
{
char c = regval;
fputc(c,stdout);
return;
}
void PSX::cpu_break(const u32 code)
{
switch(code)
{
case eFakeBreakOp_BootEXE:
{
cpu.p_fetch.in_fetch_addr = exeBootHeader.init_pc;
cpu.regs.gp = exeBootHeader.init_gp;
cpu.regs.sp = exeBootHeader.stack_load_addr;
break;
}
case eFakeBreakOp_BiosHack:
cpu.regs.t0 = 0;
cpu_run_alu_bioshack();
break;
default:
break;
}
}
#define SIGNBIT(x) ((x) & 0x80000000)
void PSX::cpu_exception(CPU::eException ex, u32 pc_victim)
{
cpu.cp0.EPC = pc_victim;
cpu.cp0.Cause.ExcCode = ex;
cpu.cp0.Cause.Sw = 0;
cpu.cp0.Cause.IP = 0;
cpu.cp0.Cause.CE = 0;
cpu.cp0.Cause.BD = 0;
u32 handler = 0x80000080;
if(cpu.cp0.SR.BEV)
handler = 0xBFC00180;
cpu.cp0.SR.KUo = cpu.cp0.SR.KUp;
cpu.cp0.SR.IEo = cpu.cp0.SR.IEp;
cpu.cp0.SR.KUp = cpu.cp0.SR.KUc;
cpu.cp0.SR.IEp = cpu.cp0.SR.IEc;
cpu.cp0.SR.KUc = 0;
cpu.cp0.SR.IEc = 0;
cpu.p_fetch.in_fetch_addr = handler;
}
void PSX::cpu_run_muldiv()
{
if(cpu.unit_muldiv.timer > 0)
{
cpu.unit_muldiv.timer--;
if(cpu.unit_muldiv.timer == 0)
{
cpu.regs.lo = cpu.unit_muldiv.lo;
cpu.regs.hi = cpu.unit_muldiv.hi;
cpu.stall_depends &= ~CPU::eStall_MulDiv;
}
}
}
void PSX::cpu_run_mem()
{
CPU::ALU_OUTPUT &input = cpu.p_mem.in_from_alu;
switch(input.op)
{
case CPU::eMemOp_StoreWord:
DEBUG_STORE("{%12lld} MEM: StoreWord [%08X] = %08X\n",abscounter,input.addr,input.value);
cpu_wrmem<4>(input.addr,input.value);
break;
case CPU::eMemOp_StoreHalfword:
DEBUG_STORE("{%12lld} MEM: StoreWord [%08X] = %04X\n",abscounter,input.addr,input.value);
cpu_wrmem<2>(input.addr,input.value);
break;
case CPU::eMemOp_StoreByte:
DEBUG_STORE("{%12lld} MEM: StoreByte [%08X] = %02X\n",abscounter,input.addr,input.value);
cpu_wrmem<1>(input.addr,input.value);
break;
case CPU::eMemOp_LoadWord:
{
const u32 temp = cpu_rdmem<4>(input.addr);
DEBUG_LOAD("{%12lld} MEM: LoadWord [%08X] == %08X\n",abscounter,input.addr,temp);
cpu.regs.r[input.rt] = temp;
break;
}
case CPU::eMemOp_LoadHalfwordSigned:
{
const u32 temp = (s16)cpu_rdmem<2>(input.addr);
DEBUG_LOAD("{%12lld} MEM: LoadHalfwordSigned [%08X] == %04X\n",abscounter,input.addr,temp);
cpu.regs.r[input.rt] = temp;
break;
}
case CPU::eMemOp_LoadHalfwordUnsigned:
{
const u32 temp = cpu_rdmem<2>(input.addr);
DEBUG_LOAD("{%12lld} MEM: LoadHalfwordUnsigned [%08X] == %04X\n",abscounter,input.addr,temp);
cpu.regs.r[input.rt] = temp;
break;
}
case CPU::eMemOp_LoadByteSigned:
{
const u32 temp = (s8)cpu_rdmem<1>(input.addr);
DEBUG_LOAD("{%12lld} MEM: LoadByte [%08X] == %02X\n",abscounter,input.addr,temp);
cpu.regs.r[input.rt] = temp;
break;
}
case CPU::eMemOp_LoadByteUnsigned:
{
const u32 temp = cpu_rdmem<1>(input.addr);
DEBUG_LOAD("{%12lld} MEM: LoadByteUnsigned [%08X] == %02X\n",abscounter,input.addr,temp);
cpu.regs.r[input.rt] = temp;
break;
}
case CPU::eMemOp_MTC:
{
const CPU::Instruction_CTYPE instr = cpu.p_mem.decode.instr.CTYPE;
cpu_copz_mtc(instr.cpnum,instr.rd,input.value);
break;
}
case CPU::eMemOp_MFC:
{
const CPU::Instruction_CTYPE instr = cpu.p_mem.decode.instr.CTYPE;
cpu.regs.r[instr.rt] = cpu_copz_mfc(instr.cpnum,instr.rd);
break;
}
case CPU::eMemOp_None:
break;
case CPU::eMemOp_Unset:
printf("*** UNHANDLED MEM OP ***\n");
break;
default:
NOCASE();
}
cpu.regs.r0 = 0;
}
void PSX::TraceALU()
{
DEBUG_TRACE("{%12lld} %08X: [%08X] (%d,%d) %s\n",abscounter,cpu.p_alu.in_pc,cpu.p_alu.decode.instr.value,
cpu.p_alu.decode.instr.value>>26,
cpu.p_alu.decode.instr.value&0x3F,
MDFN_IEN_PSX::DisassembleMIPS(cpu.p_alu.in_pc,cpu.p_alu.decode.instr.value).c_str()
);
}
void PSX::cpu_run_wb()
{
}
static const eOp DecodeTable[] =
{
eOP_SLL, eOP_ILL, eOP_SRL, eOP_SRA, eOP_SLLV, eOP_ILL, eOP_SRLV, eOP_SRAV,
eOP_JR, eOP_JALR, eOP_ILL, eOP_ILL, eOP_SYSCALL, eOP_BREAK, eOP_ILL, eOP_ILL,
eOP_MFHI, eOP_MTHI, eOP_MFLO, eOP_MTLO, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL,
eOP_MULT, eOP_MULTU, eOP_DIV, eOP_DIVU, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL,
eOP_ADD, eOP_ADDU, eOP_SUB, eOP_SUBU, eOP_AND, eOP_OR, eOP_XOR, eOP_NOR,
eOP_ILL, eOP_ILL, eOP_SLT, eOP_SLTU, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL,
eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL,
eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL,
eOP_NULL, eOP_BCOND, eOP_J, eOP_JAL, eOP_BEQ, eOP_BNE, eOP_BLEZ, eOP_BGTZ,
eOP_ADDI, eOP_ADDIU, eOP_SLTI, eOP_SLTIU, eOP_ANDI, eOP_ORI, eOP_XORI, eOP_LUI,
eOP_COPROC, eOP_COPROC, eOP_COPROC, eOP_COPROC, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL,
eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL,
eOP_LB, eOP_LH, eOP_LWL, eOP_LW, eOP_LBU, eOP_LHU, eOP_LWR, eOP_ILL,
eOP_SB, eOP_SH, eOP_SWL, eOP_SW, eOP_ILL, eOP_ILL, eOP_SWR, eOP_ILL,
eOP_LWC0, eOP_LWC1, eOP_LWC2, eOP_LWC3, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL,
eOP_SWC0, eOP_SWC1, eOP_SWC2, eOP_SWC3, eOP_ILL, eOP_ILL, eOP_ILL, eOP_ILL,
};
void PSX::cpu_run_fetch()
{
const u32 _pc = cpu.p_fetch.in_fetch_addr;
const u32 instr = cpu_fetch(cpu.p_fetch.in_fetch_addr);
const u32 opc = instr>>26;
cpu.p_fetch.decode.instr.value = instr;
const u32 _opc = instr>>26;
const u32 _func = instr&0x3F;
if(dotrace) DEBUG_TRACE("{%12lld} %08X: [%08X] (%d,%d) %s\n",abscounter, _pc, instr,_opc,_func,MDFN_IEN_PSX::DisassembleMIPS(_pc,instr).c_str());
u32 opf = instr & 0x3F;
if(instr & (0x3F << 26))
opf = 0x40 | (instr >> 26);
cpu.p_fetch.decode.op = DecodeTable[opf];
switch(cpu.p_fetch.decode.op)
{
case eOP_MFLO:
case eOP_MFHI:
cpu.stall_user |= CPU::eStall_MulDiv;
break;
}
}
#define BRANCH_SET_PC() { cpu.p_alu.out_pc.enabled = true, cpu.p_alu.out_pc.pc = cpu.p_alu.in_pc + instr.signed_target(); }
void PSX::cpu_run_alu()
{
cpu.p_alu.out_pc.enabled = false;
#ifndef NDEBUG
cpu.p_alu.out_mem.op = CPU::eMemOp_Unset;
#endif
cpu.p_alu.exception = CPU::eException_None;
switch(cpu.p_alu.decode.op)
{
case eOP_ILL:
default:
printf("*** UNHANDLED ALU OP***\n");
TraceALU();
break;
case eOP_NULLIFIED:
case eOP_NULL:
{
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_J:
{
const CPU::Instruction_JTYPE instr = cpu.p_alu.decode.instr.JTYPE;
cpu.p_alu.out_pc.enabled = true;
cpu.p_alu.out_pc.pc = (cpu.p_alu.in_pc&0xF0000000) | (instr.target << 2);
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_JAL:
{
const CPU::Instruction_JTYPE instr = cpu.p_alu.decode.instr.JTYPE;
cpu.p_alu.out_pc.enabled = true;
cpu.p_alu.out_pc.pc = (cpu.p_alu.in_pc&0xF0000000) | (instr.target << 2);
cpu.regs.ra = cpu.p_alu.in_pc + 8;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_COPROC:
{
const CPU::Instruction_CTYPE instr = cpu.p_alu.decode.instr.CTYPE;
switch(instr.format)
{
case 0:
cpu.p_alu.out_mem.op = CPU::eMemOp_MFC;
break;
case 4:
cpu.p_alu.out_mem.value = cpu.regs.r[instr.rt];
cpu.p_alu.out_mem.op = CPU::eMemOp_MTC;
break;
case 16:
switch(instr.function)
{
case 16:
cpu.cp0.SR.IEc = cpu.cp0.SR.IEp;
cpu.cp0.SR.KUc = cpu.cp0.SR.KUp;
cpu.cp0.SR.IEp = cpu.cp0.SR.IEo;
cpu.cp0.SR.KUp = cpu.cp0.SR.KUo;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
default:
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
break;
default:
NOCASE();
printf("*** UNHANDLED ALU CTYPE ***\n");
TraceALU();
}
break;
}
case eOP_SLL:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = cpu.regs.r[instr.rt] << instr.sa;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_SRL:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = cpu.regs.r[instr.rt] >> instr.sa;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_SRA:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = (s32)cpu.regs.r[instr.rt] >> instr.sa;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_SLLV:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = cpu.regs.r[instr.rt] << (cpu.regs.r[instr.rs] & 0x1F);
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_SRLV:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = cpu.regs.r[instr.rt] >> (cpu.regs.r[instr.rs] & 0x1F);
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_SRAV:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = (s32)cpu.regs.r[instr.rt] >> (cpu.regs.r[instr.rs] & 0x1F);
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_JR:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.p_alu.out_pc.enabled = true;
cpu.p_alu.out_pc.pc = cpu.regs.r[instr.rs];
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_JALR:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.p_alu.out_pc.enabled = true;
cpu.p_alu.out_pc.pc = cpu.regs.r[instr.rs];
cpu.regs.ra = cpu.p_alu.in_pc + 8;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_SYSCALL:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.p_alu.exception = CPU::eException_SYSCALL;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_BREAK:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu_break(instr.break_code());
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_MFHI:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = cpu.regs.hi;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_MTHI:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.hi = cpu.regs.r[instr.rs];
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_MFLO:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = cpu.regs.lo;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_MTLO:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.lo = cpu.regs.r[instr.rs];
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_MULT:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
const u32 a = cpu.regs.r[instr.rs];
const u32 b = cpu.regs.r[instr.rt];
s64 product = (s64)a * (s32)b;
cpu.unit_muldiv.lo = ((u32*)&product)[0];
cpu.unit_muldiv.hi = ((u32*)&product)[1];
cpu.unit_muldiv.timer = 12;
cpu.stall_depends |= CPU::eStall_MulDiv;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_MULTU:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
const u32 a = cpu.regs.r[instr.rs];
const u32 b = cpu.regs.r[instr.rt];
u64 product = (u64)a * b;
cpu.unit_muldiv.lo = ((u32*)&product)[0];
cpu.unit_muldiv.hi = ((u32*)&product)[1];
cpu.unit_muldiv.timer = 12;
cpu.stall_depends |= CPU::eStall_MulDiv;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_DIV:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
const u32 dividend = cpu.regs.r[instr.rs];
const u32 divisor = cpu.regs.r[instr.rt];
cpu.unit_muldiv.lo = (s32)dividend / (s32)divisor;
cpu.unit_muldiv.hi = (s32)dividend % (s32)divisor;
cpu.unit_muldiv.timer = 35;
cpu.stall_depends |= CPU::eStall_MulDiv;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_DIVU:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
const u32 dividend = cpu.regs.r[instr.rs];
const u32 divisor = cpu.regs.r[instr.rt];
cpu.unit_muldiv.lo = dividend / divisor;
cpu.unit_muldiv.hi = dividend % divisor;
cpu.unit_muldiv.timer = 35;
cpu.stall_depends |= CPU::eStall_MulDiv;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_ADD:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = cpu.regs.r[instr.rs] + cpu.regs.r[instr.rt];
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_ADDU:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = cpu.regs.r[instr.rs] + cpu.regs.r[instr.rt];
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_SUBU:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = cpu.regs.r[instr.rs] - cpu.regs.r[instr.rt];
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_AND:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = cpu.regs.r[instr.rs] & cpu.regs.r[instr.rt];
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_OR:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = cpu.regs.r[instr.rs] | cpu.regs.r[instr.rt];
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_NOR:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = ~(cpu.regs.r[instr.rs] | cpu.regs.r[instr.rt]);
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_SLT:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = ((s32)cpu.regs.r[instr.rs] < (s32)cpu.regs.r[instr.rt]) ? 1 : 0;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_SLTU:
{
const CPU::Instruction_RTYPE instr = cpu.p_alu.decode.instr.RTYPE;
cpu.regs.r[instr.rd] = (cpu.regs.r[instr.rs] < cpu.regs.r[instr.rt]) ? 1 : 0;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_BCOND:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
switch(instr.rt)
{
case 0:
if(SIGNBIT(cpu.regs.r[instr.rs])!=0)
BRANCH_SET_PC();
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
case 1:
if(SIGNBIT(cpu.regs.r[instr.rs])==0)
BRANCH_SET_PC();
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
break;
}
case eOP_BEQ:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
if(cpu.regs.r[instr.rs] == cpu.regs.r[instr.rt])
BRANCH_SET_PC();
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_BNE:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
if(cpu.regs.r[instr.rs] != cpu.regs.r[instr.rt])
BRANCH_SET_PC();
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_BLEZ:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
if(SIGNBIT(cpu.regs.r[instr.rs])!=0 || cpu.regs.r[instr.rs] == 0)
BRANCH_SET_PC();
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_BGTZ:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
if(SIGNBIT(cpu.regs.r[instr.rs])==0 && cpu.regs.r[instr.rs] != 0)
BRANCH_SET_PC();
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_ADDI:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.regs.r[instr.rt] = cpu.regs.r[instr.rs] + (s16)instr.immediate;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_ADDIU:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.regs.r[instr.rt] = cpu.regs.r[instr.rs] + (s16)instr.immediate;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_SLTI:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
if((s32)cpu.regs.r[instr.rs] < (s16)instr.immediate) cpu.regs.r[instr.rt] = 1;
else cpu.regs.r[instr.rt] = 0;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_SLTIU:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
if(cpu.regs.r[instr.rs] < (u32)(s16)instr.immediate) cpu.regs.r[instr.rt] = 1;
else cpu.regs.r[instr.rt] = 0;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_ANDI:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.regs.r[instr.rt] = cpu.regs.r[instr.rs] & instr.immediate;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_ORI:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.regs.r[instr.rt] = cpu.regs.r[instr.rs] | instr.immediate;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_LUI:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.regs.r[instr.rt] = instr.immediate<<16;
cpu.p_alu.out_mem.op = CPU::eMemOp_None;
break;
}
case eOP_LB:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.p_alu.out_mem.addr = cpu.regs.r[instr.base()] + (s16)instr.immediate;
cpu.p_alu.out_mem.rt = instr.rt;
cpu.p_alu.out_mem.op = CPU::eMemOp_LoadByteSigned;
break;
}
case eOP_LH:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.p_alu.out_mem.addr = cpu.regs.r[instr.base()] + (s16)instr.immediate;
cpu.p_alu.out_mem.rt = instr.rt;
cpu.p_alu.out_mem.op = CPU::eMemOp_LoadHalfwordSigned;
break;
}
case eOP_LW:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.p_alu.out_mem.addr = cpu.regs.r[instr.base()] + (s16)instr.immediate;
cpu.p_alu.out_mem.rt = instr.rt;
cpu.p_alu.out_mem.op = CPU::eMemOp_LoadWord;
break;
}
case eOP_LBU:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.p_alu.out_mem.addr = cpu.regs.r[instr.base()] + (s16)instr.immediate;
cpu.p_alu.out_mem.rt = instr.rt;
cpu.p_alu.out_mem.op = CPU::eMemOp_LoadByteUnsigned;
break;
}
case eOP_LHU:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.p_alu.out_mem.addr = cpu.regs.r[instr.base()] + (s16)instr.immediate;
cpu.p_alu.out_mem.rt = instr.rt;
cpu.p_alu.out_mem.op = CPU::eMemOp_LoadHalfwordUnsigned;
break;
}
case eOP_SB:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.p_alu.out_mem.addr = cpu.regs.r[instr.base()] + (s16)instr.immediate;
cpu.p_alu.out_mem.value = cpu.regs.r[instr.rt] & 0xFF;
cpu.p_alu.out_mem.op = CPU::eMemOp_StoreByte;
break;
}
case eOP_SH:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.p_alu.out_mem.addr = cpu.regs.r[instr.base()] + (s16)instr.immediate;
cpu.p_alu.out_mem.value = cpu.regs.r[instr.rt] & 0xFFFF;
cpu.p_alu.out_mem.op = CPU::eMemOp_StoreHalfword;
break;
}
case eOP_SW:
{
const CPU::Instruction_ITYPE instr = cpu.p_alu.decode.instr.ITYPE;
cpu.p_alu.out_mem.addr = cpu.regs.r[instr.base()] + (s16)instr.immediate;
cpu.p_alu.out_mem.value = cpu.regs.r[instr.rt];
cpu.p_alu.out_mem.op = CPU::eMemOp_StoreWord;
break;
}
}
cpu.regs.r0 = 0;
}
void PSX::cpu_exec_cycle()
{
counter++;
abscounter++;
cpu_run_muldiv();
if(cpu.stall_depends & cpu.stall_user)
{
}
else
{
cpu_run_fetch();
cpu_run_alu();
cpu_run_mem();
cpu_run_wb();
cpu.p_mem.decode = cpu.p_alu.decode;
cpu.p_mem.in_from_alu = cpu.p_alu.out_mem;
cpu.p_alu.decode = cpu.p_fetch.decode;
cpu.p_alu.in_pc = cpu.p_fetch.in_fetch_addr;
cpu.p_fetch.in_fetch_addr = cpu.p_alu.out_pc.enabled ? cpu.p_alu.out_pc.pc : (cpu.p_fetch.in_fetch_addr + 4);
if(cpu.p_alu.exception != CPU::eException_None)
{
cpu_exception(cpu.p_alu.exception,cpu.p_alu.in_pc);
}
}
}
void PSX::RunForever()
{
static const int work = 33*1024*1024*20;
DWORD a = timeGetTime();
for(;;)
{
exec_cycle();
if(counter == work) break;
}
DWORD b = timeGetTime();
printf("%d ms\n",b-a);
}