/* Audio Overload SDK - PSX and IOP hardware emulation Copyright (c) 2007 R. Belmont and Richard Bannister. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the names of R. Belmont and Richard Bannister nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* psx_hw.c - Minimal PSX/IOP hardware glue/emulation/whatever supported: main RAM (2 MB, mirrored to fill an 8 MB space like on real HW) DMA channel 4 (SPURAM) in both directions (including completion IRQ) VBL IRQ Root counters 2 and 3 including completion events and IRQs Some BIOS services including exception handling (via HLE) HLE emulation of IOP operating system, including multithreading SPU(2), SPU(2)RAM (via PEOpS) Special notes: PSF1 - Chocobo's Dungeon 2 contains an illegal code sequence (patched) PSF2 - Shadow Hearts assumes that the wave buffer alloc will go to 0x80060000 and the sequence buffer to 0x80170000. Our memory management doesn't work out that way, so we have to (wait for it) cheese it. */ #include #include "ao.h" #include "cpuintrf.h" #include "psx.h" #include "peops/spu.h" #include "peops/regs.h" #define DEBUG_HLE_BIOS (0) // debug PS1 HLE BIOS #define DEBUG_HLE_IOP (0) // debug PS2 IOP OS calls #define DEBUG_UNK_RW (0) // debug unknown reads/writes #define DEBUG_THREADING (0) // debug PS2 IOP threading extern int psf_refresh; extern int psxcpu_verbose; #define MAX_FILE_SLOTS (32) static volatile int softcall_target = 0; static int filestat[MAX_FILE_SLOTS]; static uint8 *filedata[MAX_FILE_SLOTS]; static uint32 filesize[MAX_FILE_SLOTS], filepos[MAX_FILE_SLOTS]; uint32 psf2_get_loadaddr(void); void psf2_set_loadaddr(uint32 new); static void call_irq_routine(mips_cpu_context *cpu, uint32 routine, uint32 parameter); static int intr_susp = 0; static uint64 sys_time; static int timerexp = 0; typedef struct { char name[10]; uint32 dispatch; } ExternLibEntries; static int32 iNumLibs; static ExternLibEntries reglibs[32]; typedef struct { uint32 type; uint32 value; uint32 param; int inUse; } EventFlag; static int32 iNumFlags; static EventFlag evflags[32]; typedef struct { uint32 attr; uint32 option; int32 init; int32 current; int32 max; int32 threadsWaiting; int32 inuse; } Semaphore; #define SEMA_MAX (64) static int32 iNumSema; static Semaphore semaphores[SEMA_MAX]; // thread states enum { TS_RUNNING = 0, // now running TS_READY, // ready to run TS_WAITEVFLAG, // waiting on an event flag TS_WAITSEMA, // waiting on a semaphore TS_WAITDELAY, // waiting on a time delay TS_SLEEPING, // sleeping TS_CREATED, // newly created, hasn't run yet TS_MAXSTATE }; typedef struct { int32 iState; // state of thread uint32 flags; // flags uint32 routine; // start of code for the thread uint32 stackloc; // stack location in IOP RAM uint32 stacksize; // stack size uint32 refCon; // user value passed in at CreateThread time uint32 waitparm; // what we're waiting on if in one the TS_WAIT* states uint32 save_regs[37]; // CPU registers belonging to this thread } Thread; static int32 iNumThreads, iCurThread; static Thread threads[32]; #if DEBUG_THREADING static char *_ThreadStateNames[TS_MAXSTATE] = { "RUNNING", "READY", "WAITEVFLAG", "WAITSEMA", "WAITDELAY", "SLEEPING", "CREATED" }; #endif #if DEBUG_HLE_IOP static char *seek_types[3] = { "SEEK_SET", "SEEK_CUR", "SEEK_END" }; #endif typedef struct { int32 iActive; uint32 count; uint32 target; uint32 source; uint32 prescale; uint32 handler; uint32 hparam; uint32 mode; } IOPTimer; static IOPTimer iop_timers[8]; static int32 iNumTimers; typedef struct { uint32 count; uint32 mode; uint32 target; uint32 sysclock; } Counter; static Counter root_cnts[3]; // 3 of the bastards #define CLOCK_DIV (8) // 33 MHz / this = what we run the R3000 at to keep the CPU usage not insane // counter modes #define RC_EN (0x0001) // halt #define RC_RESET (0x0008) // automatically wrap #define RC_IQ1 (0x0010) // IRQ when target reached #define RC_IQ2 (0x0040) // IRQ when target reached (pSX treats same as IQ1?) #define RC_CLC (0x0100) // counter uses direct system clock #define RC_DIV8 (0x0200) // (counter 2 only) system clock/8 typedef struct { uint32 desc; int32 status; int32 mode; uint32 fhandler; } EvtCtrlBlk[32]; static EvtCtrlBlk *Event; static EvtCtrlBlk *CounterEvent; // Sony event states #define EvStUNUSED 0x0000 #define EvStWAIT 0x1000 #define EvStACTIVE 0x2000 #define EvStALREADY 0x4000 // Sony event modes #define EvMdINTR 0x1000 #define EvMdNOINTR 0x2000 static uint32 spu_delay, dma_icr, irq_data, irq_mask, dma_timer, WAI; static uint32 dma4_madr, dma4_bcr, dma4_chcr, dma4_delay; static uint32 dma7_madr, dma7_bcr, dma7_chcr, dma7_delay; static uint32 dma4_cb, dma7_cb, dma4_fval, dma4_flag, dma7_fval, dma7_flag; static uint32 irq9_cb, irq9_fval, irq9_flag; // take a snapshot of the CPU state for a thread static void FreezeThread(mips_cpu_context *cpu, int32 iThread, int flag) { int i; union cpuinfo mipsinfo; #if DEBUG_THREADING // printf("IOP: FreezeThread(%d)\n", iThread); #endif for (i = 0; i < 32; i++) { mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo); threads[iThread].save_regs[i] = mipsinfo.i; } mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo); threads[iThread].save_regs[32] = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo); threads[iThread].save_regs[33] = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo); threads[iThread].save_regs[35] = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo); threads[iThread].save_regs[36] = mipsinfo.i; // if a thread is freezing itself due to a IOP syscall, we must save the RA as the PC // to come back to or else the syscall will recurse if (flag) { mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); } else { mips_get_info(cpu, CPUINFO_INT_PC, &mipsinfo); } threads[iThread].save_regs[34] = mipsinfo.i; #if DEBUG_THREADING { char buffer[256]; DasmMIPS(buffer, mipsinfo.i, &psx_ram[(mipsinfo.i & 0x7fffffff)/4]); printf("IOP: FreezeThread(%d) => %08x [%s]\n", iThread, threads[iThread].save_regs[34], buffer); } #endif // if thread was running, now it's ready if (threads[iThread].iState == TS_RUNNING) { threads[iThread].iState = TS_READY; } } // restore the CPU state from a thread's snapshot static void ThawThread(mips_cpu_context *cpu, int32 iThread) { int i; union cpuinfo mipsinfo; // the first time a thread is put on the CPU, // some special setup is required if (threads[iThread].iState == TS_CREATED) { // PC = starting routine threads[iThread].save_regs[34] = threads[iThread].routine-4; // compensate for weird delay slot effects // SP = thread's stack area threads[iThread].save_regs[29] = (threads[iThread].stackloc + threads[iThread].stacksize) - 16; threads[iThread].save_regs[29] |= 0x80000000; threads[iThread].save_regs[35] = threads[iThread].save_regs[36] = 0; #if DEBUG_THREADING // printf("IOP: Initial setup for thread %d => PC %x SP %x\n", iThread, threads[iThread].save_regs[34]+4, threads[iThread].save_regs[29]); #endif } #if DEBUG_THREADING { char buffer[256]; mips_get_info(CPUINFO_INT_PC, &mipsinfo); DasmMIPS(buffer, mipsinfo.i, &psx_ram[(mipsinfo.i & 0x7fffffff)/4]); printf("IOP: ThawThread(%d) => %08x [%s] (wake %d)\n", iThread, threads[iThread].save_regs[34], buffer, wakecount); } #endif for (i = 0; i < 32; i++) { mipsinfo.i = threads[iThread].save_regs[i]; mips_set_info(cpu, CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo); } mipsinfo.i = threads[iThread].save_regs[32]; mips_set_info(cpu, CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo); mipsinfo.i = threads[iThread].save_regs[33]; mips_set_info(cpu, CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo); mipsinfo.i = threads[iThread].save_regs[34]; mips_set_info(cpu, CPUINFO_INT_PC, &mipsinfo); mipsinfo.i = threads[iThread].save_regs[35]; mips_set_info(cpu, CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo); mipsinfo.i = threads[iThread].save_regs[36]; mips_set_info(cpu, CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo); threads[iThread].iState = TS_RUNNING; } // find a new thread to run static void ps2_reschedule(mips_cpu_context *cpu) { int i, starti, iNextThread; iNextThread = -1; // see if any thread other than the current one is ready to run i = iCurThread+1; if (i >= iNumThreads) { i = 0; } starti = i; // starting with the next thread after this one, // see who wants to run while (i < iNumThreads) { if (i != iCurThread) { if (threads[i].iState == TS_READY) { iNextThread = i; break; } } i++; } // if we started above thread 0 and didn't pick one, // go around and try from zero if ((starti > 0) && (iNextThread == -1)) { for (i = 0; i < iNumThreads; i++) { if (i != iCurThread) { if (threads[i].iState == TS_READY) { iNextThread = i; break; } } } } if (iNextThread != -1) { #if DEBUG_THREADING for (i = 0; i < iNumThreads; i++) { printf("Thread %02d: %s\n", i, _ThreadStateNames[threads[i].iState]); } #endif if (iCurThread != -1) { FreezeThread(cpu, iCurThread, 0); } ThawThread(cpu, iNextThread); iCurThread = iNextThread; threads[iCurThread].iState = TS_RUNNING; } else { // no thread to switch to, is the current one still running? if (iCurThread != -1) { if (threads[iCurThread].iState != TS_RUNNING) { #if DEBUG_THREADING printf("IOP: no threads to run\n"); for (i = 0; i < iNumThreads; i++) { printf("Thread %02d: %s\n", i, _ThreadStateNames[threads[i].iState]); } #endif mips_shorten_frame(cpu); // kill the CPU iCurThread = -1; // no threads are active } } else { mips_shorten_frame(cpu); // kill the CPU iCurThread = -1; // no threads are active } } } static void psx_irq_update(mips_cpu_context *cpu) { union cpuinfo mipsinfo; if ((irq_data & irq_mask) != 0) { // assert the line WAI = 0; mipsinfo.i = ASSERT_LINE; mips_set_info( cpu, CPUINFO_INT_INPUT_STATE + MIPS_IRQ0, &mipsinfo ); } else { // clear the line mipsinfo.i = CLEAR_LINE; mips_set_info( cpu, CPUINFO_INT_INPUT_STATE + MIPS_IRQ0, &mipsinfo ); } } void psx_irq_set(mips_cpu_context *cpu, uint32 irq) { irq_data |= irq; psx_irq_update(cpu); } static uint32 gpu_stat = 0; uint32 psx_hw_read(mips_cpu_context *cpu, offs_t offset, uint32 mem_mask) { if (offset >= 0x00000000 && offset <= 0x007fffff) { offset &= 0x1fffff; return LE32(cpu->psx_ram[offset>>2]); } if (offset >= 0x80000000 && offset <= 0x807fffff) { offset &= 0x1fffff; return LE32(cpu->psx_ram[offset>>2]); } if (offset == 0xbfc00180 || offset == 0xbfc00184) // exception vector { return FUNCT_HLECALL; } if (offset == 0x1f801014) { return spu_delay; } if (offset == 0xbf801014) { return spu_delay; } if (offset == 0x1f801814) { gpu_stat ^= 0xffffffff; return gpu_stat; } if (offset >= 0x1f801c00 && offset <= 0x1f801dff) { if ((mem_mask == 0xffff0000) || (mem_mask == 0xffffff00)) { return SPUreadRegister(cpu, offset) & ~mem_mask; } else if (mem_mask == 0x0000ffff) { return SPUreadRegister(cpu, offset)<<16; } else printf("SPU: read unknown mask %08x\n", mem_mask); } if (offset >= 0xbf900000 && offset <= 0xbf9007ff) { if ((mem_mask == 0xffff0000) || (mem_mask == 0xffffff00)) { return SPU2read(cpu, offset) & ~mem_mask; } else if (mem_mask == 0x0000ffff) { return SPU2read(cpu, offset)<<16; } else if (mem_mask == 0) { return SPU2read(cpu, offset) | SPU2read(cpu, offset+2)<<16; } else printf("SPU2: read unknown mask %08x\n", mem_mask); } if (offset >= 0x1f801100 && offset <= 0x1f801128) { int cnt = (offset>>4) & 0xf; switch (offset & 0xf) { case 0: // printf("RC: read counter %d count = %x\n", cnt, root_cnts[cnt].count); return root_cnts[cnt].count; break; case 4: // printf("RC: read counter %d mode\n", cnt); return root_cnts[cnt].mode; break; case 8: // printf("RC: read counter %d target\n", cnt); return root_cnts[cnt].target; break; } return 0; } if (offset == 0x1f8010f4) { return dma_icr; } else if (offset == 0x1f801070) { // printf("Read IRQ_data %x (mask %08x)\n", irq_data, mem_mask); return irq_data; } else if (offset == 0x1f801074) { return irq_mask; } /* if (offset == 0xbf801508) { return dma7_bcr; }*/ if (offset == 0xbf920344) { return 0x80808080; } #if DEBUG_UNK_RW { union cpuinfo mipsinfo; mips_get_info(cpu, CPUINFO_INT_PC, &mipsinfo); printf("Unknown read: %08x, mask %08x (PC=%x)\n", offset&~3, mem_mask, mipsinfo.i); } #endif return 0; } static void psx_dma4(mips_cpu_context *cpu, uint32 madr, uint32 bcr, uint32 chcr) { if (chcr == 0x01000201) // cpu to SPU { // printf("DMA4: RAM %08x to SPU\n", madr); bcr = (bcr>>16) * (bcr & 0xffff) * 2; SPUwriteDMAMem(cpu, madr&0x1fffff, bcr); } else { // printf("DMA4: SPU to RAM %08x\n", madr); bcr = (bcr>>16) * (bcr & 0xffff) * 2; SPUreadDMAMem(cpu, madr&0x1fffff, bcr); } } static void ps2_dma4(mips_cpu_context *cpu, uint32 madr, uint32 bcr, uint32 chcr) { if (chcr == 0x01000201) // cpu to SPU2 { #if DEBUG_HLE_IOP printf("DMA4: RAM %08x to SPU2\n", madr); #endif bcr = (bcr>>16) * (bcr & 0xffff) * 4; SPU2writeDMA4Mem(cpu, madr&0x1fffff, bcr); } else { #if DEBUG_HLE_IOP printf("DMA4: SPU2 to RAM %08x\n", madr); #endif bcr = (bcr>>16) * (bcr & 0xffff) * 4; SPU2readDMA4Mem(cpu, madr&0x1fffff, bcr); } dma4_delay = 80; } static void ps2_dma7(mips_cpu_context *cpu, uint32 madr, uint32 bcr, uint32 chcr) { if ((chcr == 0x01000201) || (chcr == 0x00100010) || (chcr == 0x000f0010) || (chcr == 0x00010010)) // cpu to SPU2 { #if DEBUG_HLE_IOP printf("DMA7: RAM %08x to SPU2\n", madr); #endif bcr = (bcr>>16) * (bcr & 0xffff) * 4; SPU2writeDMA7Mem(cpu, madr&0x1fffff, bcr); } else { #if DEBUG_HLE_IOP printf("DMA7: SPU2 to RAM %08x\n", madr); #endif bcr = (bcr>>16) * (bcr & 0xffff) * 4; // SPU2readDMA7Mem(madr&0x1fffff, bcr); } dma7_delay = 80; } void psx_hw_write(mips_cpu_context *cpu, offs_t offset, uint32 data, uint32 mem_mask) { union cpuinfo mipsinfo; if (offset >= 0x00000000 && offset <= 0x007fffff) { offset &= 0x1fffff; // if (offset < 0x10000) printf("Write %x to kernel @ %x\n", data, offset); mips_get_info(cpu, CPUINFO_INT_PC, &mipsinfo); cpu->psx_ram[offset>>2] &= LE32(mem_mask); cpu->psx_ram[offset>>2] |= LE32(data); return; } if (offset >= 0x80000000 && offset <= 0x807fffff) { offset &= 0x1fffff; // if (offset < 0x10000) printf("Write %x to kernel @ %x\n", data, offset); mips_get_info(cpu, CPUINFO_INT_PC, &mipsinfo); cpu->psx_ram[offset>>2] &= LE32(mem_mask); cpu->psx_ram[offset>>2] |= LE32(data); return; } if (offset == 0x1f801014 || offset == 0xbf801014) { spu_delay &= mem_mask; spu_delay |= data; return; } if (offset >= 0x1f801c00 && offset <= 0x1f801dff) { // printf("SPU2 wrote %x to SPU1 address %x!\n", data, offset); if (mem_mask == 0xffff0000) { SPUwriteRegister(cpu, offset, data); return; } else if (mem_mask == 0x0000ffff) { SPUwriteRegister(cpu, offset, data>>16); return; } else printf("SPU: write unknown mask %08x\n", mem_mask); } if (offset >= 0xbf900000 && offset <= 0xbf9007ff) { if (mem_mask == 0xffff0000) { SPU2write(cpu, offset, data); return; } else if (mem_mask == 0x0000ffff) { SPU2write(cpu, offset, data>>16); return; } else if (mem_mask == 0) { SPU2write(cpu, offset, data & 0xffff); SPU2write(cpu, offset+2, data>>16); return; } else printf("SPU2: write unknown mask %08x\n", mem_mask); } if (offset >= 0x1f801100 && offset <= 0x1f801128) { int cnt = (offset>>4) & 0xf; switch (offset & 0xf) { case 0: root_cnts[cnt].count = data; // printf("RC: counter %d count = %x\n", cnt, data); break; case 4: root_cnts[cnt].mode = data; // printf("RC: counter %d mode = %x\n", cnt, data); break; case 8: root_cnts[cnt].target = data; // printf("RC: counter %d target = %x\n", cnt, data); break; } return; } // DMA4 if (offset == 0x1f8010c0) { dma4_madr = data; return; } else if (offset == 0x1f8010c4) { dma4_bcr = data; return; } else if (offset == 0x1f8010c8) { dma4_chcr = data; psx_dma4(cpu, dma4_madr, dma4_bcr, dma4_chcr); if (dma_icr & (1 << (16+4))) { dma_timer = 3; } return; } else if (offset == 0x1f8010f4) { dma_icr = ( dma_icr & mem_mask ) | ( ~mem_mask & 0x80000000 & dma_icr) | ( ~data & ~mem_mask & 0x7f000000 & dma_icr) | ( data & ~mem_mask & 0x00ffffff); if ((dma_icr & 0x7f000000) != 0) { dma_icr &= ~0x80000000; } return; } else if (offset == 0x1f801070) { irq_data = (irq_data & mem_mask) | (irq_data & irq_mask & data); psx_irq_update(cpu); return; } else if (offset == 0x1f801074) { irq_mask &= mem_mask; irq_mask |= data; psx_irq_update(cpu); return; } // PS2 DMA4 if (offset == 0xbf8010c0) { dma4_madr = data; return; } else if (offset == 0xbf8010c8) { dma4_chcr = data; ps2_dma4(cpu, dma4_madr, dma4_bcr, dma4_chcr); if (dma_icr & (1 << (16+4))) { dma_timer = 3; } return; } if (offset == 0xbf8010c4 || offset == 0xbf8010c6) { dma4_bcr &= mem_mask; dma4_bcr |= data; return; } // PS2 DMA7 if (offset == 0xbf801500) { dma7_madr = data; return; } else if (offset == 0xbf801504) { dma7_chcr = data; ps2_dma7(cpu, dma7_madr, dma7_bcr, dma7_chcr); return; } if (offset == 0xbf801508 || offset == 0xbf80150a) { dma7_bcr &= mem_mask; dma7_bcr |= data; return; } #if DEBUG_UNK_RW { union cpuinfo mipsinfo; mips_get_info(CPUINFO_INT_PC, &mipsinfo); printf("Unknown write: %08x to %08x, mask %08x (PC=%x)\n", data, offset&~3, mem_mask, mipsinfo.i); } #endif } // called per sample, 1/44100th of a second (768 clock cycles) void psx_hw_slice(mips_cpu_context *cpu) { psx_hw_runcounters(cpu); if (!WAI) mips_execute(cpu, 768/CLOCK_DIV); if (dma_timer) { dma_timer--; if (dma_timer == 0) { dma_icr |= (1 << (24+4)); psx_irq_set(cpu, 0x0008); } } } void ps2_hw_slice(mips_cpu_context *cpu) { int i = 0; timerexp = 0; psx_hw_runcounters(cpu); if (iCurThread != -1) { mips_execute(cpu, 836/CLOCK_DIV); } else // no thread, don't run CPU, just update counters { if (timerexp) { ps2_reschedule(cpu); if (iCurThread != -1) { mips_execute(cpu, (836/CLOCK_DIV)-i); i = (836/CLOCK_DIV); } } } } static int fcnt = 0; void psx_hw_frame(mips_cpu_context *cpu) { if (psf_refresh == 50) { fcnt++;; if (fcnt < 6) { psx_irq_set(cpu, 1); } else { fcnt = 0; } } else // NTSC { psx_irq_set(cpu, 1); } } void ps2_hw_frame(mips_cpu_context *cpu) { ps2_reschedule(cpu); } // BIOS HLE // heap block struct offsets enum { BLK_STAT = 0, BLK_SIZE = 4, BLK_FD = 8, BLK_BK = 12 }; static uint32 heap_addr, entry_int = 0; static uint32 irq_regs[37]; static int irq_mutex = 0; static void call_irq_routine(mips_cpu_context *cpu, uint32 routine, uint32 parameter) { int j, oldICount; union cpuinfo mipsinfo; if (!irq_mutex) { irq_mutex = 1; } else { printf("IOP: ERROR! IRQ reentry!\n"); return; } // save regs for IRQ for (j = 0; j < 32; j++) { mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R0 + j, &mipsinfo); irq_regs[j] = mipsinfo.i; } mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo); irq_regs[32] = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo); irq_regs[33] = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_PC, &mipsinfo); irq_regs[34] = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo); irq_regs[35] = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo); irq_regs[36] = mipsinfo.i; // PC = timer handler routine mipsinfo.i = routine; mips_set_info(cpu, CPUINFO_INT_PC, &mipsinfo); // parameter in a0 mipsinfo.i = parameter; mips_set_info(cpu, CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo); // RA = a trap address we can set mipsinfo.i = 0x80001000; mips_set_info(cpu, CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); // make sure we're set cpu->psx_ram[0x1000/4] = LE32(FUNCT_HLECALL); softcall_target = 0; oldICount = mips_get_icount(cpu); while (!softcall_target) { mips_execute(cpu, 10); } mips_set_icount(cpu,oldICount); // restore IRQ regs for (j = 0; j < 32; j++) { mipsinfo.i = irq_regs[j]; mips_set_info(cpu, CPUINFO_INT_REGISTER + MIPS_R0 + j, &mipsinfo); } mipsinfo.i = irq_regs[32]; mips_set_info(cpu, CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo); mipsinfo.i = irq_regs[33]; mips_set_info(cpu, CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo); mipsinfo.i = irq_regs[34]; mips_set_info(cpu, CPUINFO_INT_PC, &mipsinfo); mipsinfo.i = irq_regs[35]; mips_set_info(cpu, CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo); mipsinfo.i = irq_regs[36]; mips_set_info(cpu, CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo); irq_mutex = 0; } void psx_bios_exception(mips_cpu_context *cpu, uint32 pc) { uint32 a0, status; union cpuinfo mipsinfo; int i, oldICount; // printf("bios_exception: cause %x\n", mips_get_cause() & 0x3c); // get a0 mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo); a0 = mipsinfo.i; switch (mips_get_cause(cpu) & 0x3c) { case 0: // IRQ // printf("IRQ: %x, mask %x\n", irq_data, irq_mask); // save all regs for (i = 0; i < 32; i++) { mips_get_info(cpu,CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo); irq_regs[i] = mipsinfo.i; } mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo); irq_regs[32] = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo); irq_regs[33] = mipsinfo.i; // check BIOS-driven interrupts if (irq_data & 1) // VSync { if (CounterEvent[3][1].status == LE32(EvStACTIVE)) { // run the handler mipsinfo.i = LE32(CounterEvent[3][1].fhandler); // printf("Cause = %x, ePC = %x\n", mips_get_cause(), mips_get_ePC()); // printf("VBL running handler @ %x\n", mipsinfo.i); mips_set_info(cpu,CPUINFO_INT_PC, &mipsinfo); mipsinfo.i = 0x80001000; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); // make sure we're set cpu->psx_ram[0x1000/4] = LE32(FUNCT_HLECALL); softcall_target = 0; oldICount = mips_get_icount(cpu); while (!softcall_target) { mips_execute(cpu, 10); } mips_set_icount(cpu, oldICount); // printf("Exiting softcall handler\n"); irq_data &= ~1; // clear the VBL IRQ if we handled it } } else if (irq_data & 0x70) // root counters { for (i = 0; i < 3; i++) { if (irq_data & (1 << (i+4))) { if (CounterEvent[i][1].status == LE32(EvStACTIVE)) { // run the handler mipsinfo.i = LE32(CounterEvent[i][1].fhandler); // printf("Cause = %x, ePC = %x\n", mips_get_cause(), mips_get_ePC()); // printf("Counter %d running handler @ %x\n", i, mipsinfo.i); mips_set_info(cpu,CPUINFO_INT_PC, &mipsinfo); mipsinfo.i = 0x80001000; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); // make sure we're set cpu->psx_ram[0x1000/4] = LE32(FUNCT_HLECALL); softcall_target = 0; oldICount = mips_get_icount(cpu); while (!softcall_target) { mips_execute(cpu, 10); } mips_set_icount(cpu,oldICount); // printf("Exiting softcall handler\n"); irq_data &= ~(1 << (i+4)); } else { // printf("CEvt %d not active\n", i); } } } } if (entry_int) { psx_hw_write(cpu, 0x1f801070, 0xffffffff, 0); a0 = entry_int; // printf("taking entry_int\n"); // RA (and PC) mipsinfo.i = LE32(cpu->psx_ram[((a0&0x1fffff)+0)/4]); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); mips_set_info(cpu,CPUINFO_INT_PC, &mipsinfo); // SP mipsinfo.i = LE32(cpu->psx_ram[((a0&0x1fffff)+4)/4]); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R29, &mipsinfo); // FP mipsinfo.i = LE32(cpu->psx_ram[((a0&0x1fffff)+8)/4]); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R30, &mipsinfo); // S0-S7 are next for (i = 0; i < 8; i++) { mipsinfo.i = LE32(cpu->psx_ram[((a0&0x1fffff)+12+(i*4))/4]); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R16 + i, &mipsinfo); } // GP mipsinfo.i = LE32(cpu->psx_ram[((a0&0x1fffff)+44)/4]); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R28, &mipsinfo); // v0 = 1 mipsinfo.i = 1; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } else { psx_hw_write(cpu, 0x1f801070, 0, 0xffff0000); // note: the entry_int won't be bailing us out here, so do it ourselves for (i = 0; i < 32; i++) { mipsinfo.i = irq_regs[i]; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo); } mipsinfo.i = irq_regs[32]; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo); mipsinfo.i = irq_regs[33]; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo); mipsinfo.i = mips_get_ePC(cpu); mips_set_info(cpu,CPUINFO_INT_PC, &mipsinfo); status = mips_get_status(cpu); status = (status & 0xfffffff0) | ((status & 0x3c)>>2); mips_set_status(cpu, status); } break; case 0x20: // syscall // syscall always farks with the status, so get it now status = mips_get_status(cpu); switch (a0) { case 1: // EnterCritical #if DEBUG_HLE_BIOS printf("HLEBIOS: EnterCritical\n"); #endif status &= ~0x0404; break; case 2: // ExitCritical #if DEBUG_HLE_BIOS printf("HLEBIOS: ExitCritical\n"); #endif status |= 0x0404; break; default: #if DEBUG_HLE_BIOS printf("HLEBIOS: Unknown syscall %x\n", a0); #endif break; } // PC = ePC + 4 mipsinfo.i = mips_get_ePC(cpu) + 4; mips_set_info(cpu,CPUINFO_INT_PC, &mipsinfo); // and update the status accordingly status = (status & 0xfffffff0) | ((status & 0x3c)>>2); mips_set_status(cpu, status); break; default: #if DEBUG_HLE_BIOS printf("HLEBIOS: Unknown exception %x\n", mips_get_cause()); #endif break; } } static uint32 calc_ev(uint32 a0) { uint32 ev; ev = (a0 >> 24) & 0xf; if (ev == 0xf) { ev = 0x5; } ev *= 32; ev += (a0 & 0x1f); return ev; } static uint32 calc_spec(uint32 a1) { uint32 spec = 0; int i; if (a1 == 0x301) { spec = 16; } else if (a1 == 0x302) { spec = 17; } else { for (i = 0; i < 16; i++) { if (a1 & (1<psx_ram[0xa0/4] = LE32(FUNCT_HLECALL); cpu->psx_ram[0xb0/4] = LE32(FUNCT_HLECALL); cpu->psx_ram[0xc0/4] = LE32(FUNCT_HLECALL); Event = (EvtCtrlBlk *)&cpu->psx_ram[0x1000/4]; CounterEvent = (Event + (32*2)); dma_icr = 0; spu_delay = 0; irq_data = 0; irq_mask = 0; softcall_target = 0; gpu_stat = 0; dma4_madr = dma4_bcr = dma4_chcr = 0; heap_addr = 0; entry_int = 0; WAI = 0; root_cnts[0].mode = RC_EN; root_cnts[1].mode = RC_EN; root_cnts[2].mode = RC_EN; root_cnts[0].sysclock = 0; root_cnts[1].sysclock = 0; root_cnts[2].sysclock = 0; } void psx_bios_hle(mips_cpu_context *cpu, uint32 pc) { uint32 subcall, status; union cpuinfo mipsinfo; uint32 a0, a1, a2, a3; int i; if ((pc == 0) || (pc == 0x80000000)) // IOP "null" state { #if DEBUG_HLE_IOP printf("IOP 'null' state\n"); #endif // ao_song_done = 1; return; } if (pc == 0xbfc00180 || pc == 0xbfc00184) // exception, not BIOS call { psx_bios_exception(cpu, pc); return; } if (pc == 0x80001000) { // printf("hit softcall target\n"); softcall_target = 1; return; } mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R9, &mipsinfo); subcall = mipsinfo.i & 0xff; // most calls have a0/a1 as parameters, so prefetch them mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo); a0 = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R5, &mipsinfo); a1 = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R6, &mipsinfo); a2 = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R7, &mipsinfo); a3 = mipsinfo.i; // mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); // printf("HLEBIOS: return is %08x\n", mipsinfo.i); switch (pc) { case 0xa0: // a0 syscalls switch (subcall) { case 0x13: // setjmp // RA mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); cpu->psx_ram[((a0&0x1fffff)+0)/4] = LE32(mipsinfo.i); #if DEBUG_HLE_BIOS printf("HLEBIOS: setjmp(%08x) => PC %08x\n", a0, mipsinfo.i); #endif // SP mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R29, &mipsinfo); cpu->psx_ram[((a0&0x1fffff)+4)/4] = LE32(mipsinfo.i); // FP mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R30, &mipsinfo); cpu->psx_ram[((a0&0x1fffff)+8)/4] = LE32(mipsinfo.i); // S0-S7 are next for (i = 0; i < 8; i++) { mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R16 + i, &mipsinfo); cpu->psx_ram[((a0&0x1fffff)+12+(i*4))/4] = LE32(mipsinfo.i); } // GP mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R28, &mipsinfo); cpu->psx_ram[((a0&0x1fffff)+44)/4] = LE32(mipsinfo.i); // v0 = 0 mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 0x18: // strncmp { uint8 *dst, *src; #if DEBUG_HLE_BIOS printf("HLEBIOS: strncmp(%08x, %08x, %d)\n", a0, a1, a2); #endif dst = (uint8 *)cpu->psx_ram; src = (uint8 *)cpu->psx_ram; dst += (a0 & 0x1fffff); src += (a1 & 0x1fffff); // v0 = result mipsinfo.i = strncmp((char *)dst, (char *)src, a2); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 0x19: // strcpy { uint8 *dst, *src; #if DEBUG_HLE_BIOS printf("HLEBIOS: strcpy(%08x, %08x)\n", a0, a1); #endif dst = (uint8 *)cpu->psx_ram; src = (uint8 *)cpu->psx_ram; dst += (a0 & 0x1fffff); src += (a1 & 0x1fffff); while (*src) { *dst = *src; dst++; src++; } // v0 = a0 mipsinfo.i = a0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 0x28: // bzero { uint8 *dst; #if DEBUG_HLE_BIOS printf("HLEBIOS: bzero(%08x, %08x)\n", a0, a1); #endif dst = (uint8 *)cpu->psx_ram; dst += (a0 & 0x1fffff); memset(dst, 0, a1); } break; case 0x2a: // memcpy { uint8 *dst, *src; #if DEBUG_HLE_BIOS printf("HLEBIOS: memcpy(%08x, %08x, %08x)\n", a0, a1, a2); #endif dst = (uint8 *)cpu->psx_ram; src = (uint8 *)cpu->psx_ram; dst += (a0 & 0x1fffff); src += (a1 & 0x1fffff); while (a2) { *dst = *src; dst++; src++; a2--; } // v0 = a0 mipsinfo.i = a0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 0x2b: // memset { uint8 *dst; #if DEBUG_HLE_BIOS printf("HLEBIOS: memset(%08x, %08x, %08x)\n", a0, a1, a2); #endif dst = (uint8 *)cpu->psx_ram; dst += (a0 & 0x1fffff); while (a2) { *dst = a1; dst++; a2--; } // v0 = a0 mipsinfo.i = a0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 0x2f: // rand #if DEBUG_HLE_BIOS printf("HLEBIOS: rand\n"); #endif // v0 = result mipsinfo.i = 1 + (int)(32767.0*rand()/(RAND_MAX+1.0)); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 0x30: // srand #if DEBUG_HLE_BIOS printf("HLEBIOS: srand(%x)\n", a0); #endif srand(a0); break; case 0x33: // malloc { uint32 chunk, fd; #if DEBUG_HLE_BIOS printf("HLEBIOS: malloc(%x)\n", a0); #endif chunk = heap_addr; // find a free block that's big enough while ((a0 > LE32(cpu->psx_ram[(chunk+BLK_SIZE)/4])) || (LE32(cpu->psx_ram[(chunk+BLK_STAT)/4]) == 1)) { chunk = LE32(cpu->psx_ram[(chunk+BLK_FD)]); } // split free block fd = chunk + 16 + a0; // free block starts after block record and allocation size cpu->psx_ram[(fd+BLK_STAT)/4] = cpu->psx_ram[(chunk+BLK_STAT)/4]; cpu->psx_ram[(fd+BLK_SIZE)/4] = LE32(LE32(cpu->psx_ram[(chunk+BLK_SIZE)/4]) - a0); cpu->psx_ram[(fd+BLK_FD)/4] = cpu->psx_ram[(chunk+BLK_FD)/4]; cpu->psx_ram[(fd+BLK_BK)/4] = chunk; cpu->psx_ram[(chunk+BLK_STAT)/4] = LE32(1); cpu->psx_ram[(chunk+BLK_SIZE)/4] = LE32(a0); cpu->psx_ram[(chunk+BLK_FD)/4] = LE32(fd); mipsinfo.i = chunk + 16; mipsinfo.i |= 0x80000000; #if DEBUG_HLE_BIOS printf("== %08x\n", mipsinfo.i); #endif mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 0x39: // InitHeap // heap address in A0, length in A1 #if DEBUG_HLE_BIOS printf("HLEBIOS: InitHeap(%08x, %08x)\n", a0, a1); #endif heap_addr = a0 & 0x3fffffff; cpu->psx_ram[(heap_addr+BLK_STAT)/4] = LE32(0); cpu->psx_ram[(heap_addr+BLK_FD)/4] = LE32(0); cpu->psx_ram[(heap_addr+BLK_BK)/4] = LE32(0); // if heap size out of range, clamp it if (((a0 & 0x1fffff) + a1) >= 2*1024*1024) { cpu->psx_ram[(heap_addr+BLK_SIZE)/4] = LE32(0x1ffffc - (a0 & 0x1fffff)); } else { cpu->psx_ram[(heap_addr+BLK_SIZE)/4] = LE32(a1); } break; case 0x3f: // printf #if DEBUG_HLE_BIOS printf("HLEBIOS: printf(%08x) = %s\n", a0, &cpu->psx_ram[(a0&0x1fffff)/4]); #endif break; case 0x72: //__96_remove #if DEBUG_HLE_BIOS printf("HLEBIOS: __96_remove\n"); #endif break; default: #if DEBUG_HLE_BIOS printf("Unknown BIOS A0 call = %x\n", subcall); #endif break; } break; case 0xb0: // b0 syscalls switch (subcall) { case 0x07: // DeliverEvent { int ev, spec; ev = calc_ev(a0); spec = calc_spec(a1); #if DEBUG_HLE_BIOS printf("HLEBIOS: DeliverEvent(ev %d, spec %d)\n", ev, spec); #endif if (Event[ev][spec].status != LE32(EvStACTIVE)) { #if DEBUG_HLE_BIOS printf("event not active\n"); #endif return; } // if interrupt mode, do the call if (Event[ev][spec].mode == LE32(EvMdINTR)) { #if DEBUG_HLE_BIOS printf("INTR type, need to call handler %x\n", LE32(Event[ev][spec].fhandler)); #endif } else { Event[ev][spec].status = LE32(EvStALREADY); } } break; case 0x08: // OpenEvent { int ev, spec; ev = calc_ev(a0); spec = calc_spec(a1); #if DEBUG_HLE_BIOS printf("HLEBIOS: OpenEvent(%08x, %08x, %08x, %08x) = ev %d spec %d\n", a0, a1, a2, a3, ev, spec); if (ev >= 64 && ev <= 67) { printf("HLEBIOS: event %d maps to root counter %d\n", ev, ev-64); } #endif Event[ev][spec].status = LE32(EvStWAIT); Event[ev][spec].mode = LE32(a2); Event[ev][spec].fhandler = LE32(a3); // v0 = ev | spec<<8; mipsinfo.i = ev | (spec<<8); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 0x0a: // WaitEvent { int ev, spec; ev = a0 & 0xff; spec = (a0 >> 8) & 0xff; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); #if DEBUG_HLE_BIOS printf("HLEBIOS: WaitEvent(ev %d spec %d) PC=%x\n", ev, spec, mipsinfo.i); #endif Event[ev][spec].status = LE32(EvStACTIVE); // v0 = 1 mipsinfo.i = 1; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); WAI = 1; mips_shorten_frame(cpu); } break; case 0x0b: // TestEvent { int ev, spec; ev = a0 & 0xff; spec = (a0 >> 8) & 0xff; #if DEBUG_HLE_BIOS printf("HLEBIOS: TestEvent(ev %d spec %d)\n", ev, spec); #endif // v0 = (is event ready?) if (Event[ev][spec].status == LE32(EvStALREADY)) { Event[ev][spec].status = LE32(EvStACTIVE); mipsinfo.i = 1; } else { mipsinfo.i = 0; } WAI = 1; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); // it looks like this sets v1 to something non-zero too // (code in Crash 2 & 3 actually relies on that behavior) mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R3, &mipsinfo); } break; case 0x0c: // EnableEvent { int ev, spec; ev = a0 & 0xff; spec = (a0 >> 8) & 0xff; #if DEBUG_HLE_BIOS printf("HLEBIOS: EnableEvent(ev %d spec %d)\n", ev, spec); #endif Event[ev][spec].status = LE32(EvStACTIVE); // v0 = 1 mipsinfo.i = 1; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 0x0d: // DisableEvent { int ev, spec; ev = a0 & 0xff; spec = (a0 >> 8) & 0xff; #if DEBUG_HLE_BIOS printf("HLEBIOS: DisableEvent(ev %d spec %d)\n", ev, spec); #endif Event[ev][spec].status = LE32(EvStWAIT); // v0 = 1 mipsinfo.i = 1; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 0x17: // ReturnFromException for (i = 0; i < 32; i++) { mipsinfo.i = irq_regs[i]; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo); } mipsinfo.i = irq_regs[32]; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo); mipsinfo.i = irq_regs[33]; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo); mipsinfo.i = mips_get_ePC(cpu); // printf("ReturnFromException: IRQ state %x\n", irq_data & irq_mask); // printf("HLEBIOS: ReturnFromException, cause = %08x, PC = %08x\n", mips_get_cause(), mipsinfo.i); mips_set_info(cpu,CPUINFO_INT_PC, &mipsinfo); status = mips_get_status(cpu); status = (status & 0xfffffff0) | ((status & 0x3c)>>2); mips_set_status(cpu, status); return; // force return to avoid PC=RA below break; case 0x19: // HookEntryInt #if DEBUG_HLE_BIOS printf("HLEBIOS: HookEntryInt(%08x)\n", a0); #endif entry_int = a0; break; case 0x3f: // puts // printf("HLEBIOS: puts\n"); break; case 0x5b: // ChangeClearPAD #if DEBUG_HLE_BIOS printf("HLEBIOS: ChangeClearPAD\n"); #endif break; default: #if DEBUG_HLE_BIOS printf("Unknown BIOS B0 call = %x\n", subcall); #endif break; } break; case 0xc0: // c0 syscalls switch (subcall) { case 0xa: // ChangeClearRCnt #if DEBUG_HLE_BIOS printf("HLEBIOS: ChangeClearRCnt(%08x, %08x)\n", a0, a1); #endif // v0 = (a0*4)+0x8600 mipsinfo.i = LE32(cpu->psx_ram[((a0<<2) + 0x8600)/4]); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); // (a0*4)+0x8600 = a1; cpu->psx_ram[((a0<<2) + 0x8600)/4] = LE32(a1); break; default: #if DEBUG_HLE_BIOS printf("Unknown BIOS C0 call = %x\n", subcall); #endif break; } break; } // PC = RA mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); mips_set_info(cpu,CPUINFO_INT_PC, &mipsinfo); } // root counters void psx_hw_runcounters(mips_cpu_context *cpu) { int i, j; union cpuinfo mipsinfo; // don't process any IRQ sources when interrupts are suspended if (!intr_susp) { if (dma4_delay) { dma4_delay--; if (dma4_delay == 0) { SPU2interruptDMA4(cpu); if (dma4_cb) { call_irq_routine(cpu, dma4_cb, dma4_flag); } } } if (dma7_delay) { dma7_delay--; if (dma7_delay == 0) { SPU2interruptDMA7(cpu); if (dma7_cb) { call_irq_routine(cpu, dma7_cb, dma7_flag); } } } for (i = 0; i < iNumThreads; i++) { if (threads[i].iState == TS_WAITDELAY) { if (threads[i].waitparm > CLOCK_DIV) { threads[i].waitparm -= CLOCK_DIV; } else // time's up { threads[i].waitparm = 0; threads[i].iState = TS_READY; timerexp = 1; ps2_reschedule(cpu); } } } sys_time += 836; if (iNumTimers > 0) { for (i = 0; i < iNumTimers; i++) { if (iop_timers[i].iActive > 0) { iop_timers[i].count += 836; if (iop_timers[i].count >= iop_timers[i].target) { iop_timers[i].count -= iop_timers[i].target; // printf("Timer %d: handler = %08x, param = %08x\n", i, iop_timers[i].handler, iop_timers[i].hparam); call_irq_routine(cpu, iop_timers[i].handler, iop_timers[i].hparam); timerexp = 1; } } } } } // PS1 root counters for (i = 0; i < 3; i++) { if ((!(root_cnts[i].mode & RC_EN)) && (root_cnts[i].mode != 0)) { if (root_cnts[i].mode & RC_DIV8) { root_cnts[i].count += 768/8; } else { root_cnts[i].count += 768; } if (root_cnts[i].count >= root_cnts[i].target) { if (!(root_cnts[i].mode & RC_RESET)) { root_cnts[i].mode |= RC_EN; } else { root_cnts[i].count %= root_cnts[i].target; } psx_irq_set(cpu, 1<<(4+i)); } } } } // PEOpS callbacks void SPUirq(void) { // psx_irq_set(0x200); } // PSXCPU callbacks uint8 program_read_byte_32le(mips_cpu_context *cpu, offs_t address) { switch (address & 0x3) { case 0: return psx_hw_read(cpu, address, 0xffffff00); break; case 1: return psx_hw_read(cpu, address, 0xffff00ff)>>8; break; case 2: return psx_hw_read(cpu, address, 0xff00ffff)>>16; break; case 3: return psx_hw_read(cpu, address, 0x00ffffff)>>24; break; } return 0; } uint16 program_read_word_32le(mips_cpu_context *cpu, offs_t address) { if (address & 2) return psx_hw_read(cpu, address, 0x0000ffff)>>16; return psx_hw_read(cpu, address, 0xffff0000); } uint32 program_read_dword_32le(mips_cpu_context *cpu, offs_t address) { return psx_hw_read(cpu, address, 0); } void program_write_byte_32le(mips_cpu_context *cpu, offs_t address, uint8 data) { switch (address & 0x3) { case 0: psx_hw_write(cpu, address, data, 0xffffff00); break; case 1: psx_hw_write(cpu, address, data<<8, 0xffff00ff); break; case 2: psx_hw_write(cpu, address, data<<16, 0xff00ffff); break; case 3: psx_hw_write(cpu, address, data<<24, 0x00ffffff); break; } } void program_write_word_32le(mips_cpu_context *cpu, offs_t address, uint16 data) { if (address & 2) { psx_hw_write(cpu, address, data<<16, 0x0000ffff); return; } psx_hw_write(cpu, address, data, 0xffff0000); } void program_write_dword_32le(mips_cpu_context *cpu, offs_t address, uint32 data) { psx_hw_write(cpu, address, data, 0); } // sprintf replacement static void iop_sprintf(mips_cpu_context *cpu, char *out, char *fmt, uint32 pstart) { char temp[64], tfmt[64]; char *cf, *pstr; union cpuinfo mipsinfo; int curparm, fp, isnum; curparm = pstart; cf = fmt; while (*cf != '\0') { if (*cf != '%') { if (*cf == 27) { *out++ = '['; *out++ = 'E'; *out++ = 'S'; *out++ = 'C'; *out = ']'; } else { *out = *cf; } out++; cf++; } else // got format { cf++; tfmt[0] = '%'; fp = 1; while (((*cf >= '0') && (*cf <= '9')) || (*cf == '.')) { tfmt[fp] = *cf; fp++; cf++; } tfmt[fp] = *cf; tfmt[fp+1] = '\0'; isnum = 0; switch (*cf) { case 'x': case 'X': case 'd': case 'D': case 'c': case 'C': case 'u': case 'U': isnum = 1; break; } // printf("]]] temp format: [%s] [%d]\n", tfmt, isnum); if (isnum) { mips_get_info(cpu, curparm, &mipsinfo); // printf("parameter %d = %x\n", curparm-pstart, mipsinfo.i); curparm++; sprintf(temp, tfmt, (int32)mipsinfo.i); } else { mips_get_info(cpu, curparm, &mipsinfo); curparm++; pstr = (char *)cpu->psx_ram; pstr += (mipsinfo.i & 0x1fffff); sprintf(temp, tfmt, pstr); } pstr = &temp[0]; while (*pstr != '\0') { *out = *pstr; out++; pstr++; } cf++; } } *out = '\0'; } // PS2 IOP callbacks void psx_iop_call(mips_cpu_context *cpu, uint32 pc, uint32 callnum) { uint32 scan; char *mname, *str1, *str2, *str3, name[9], out[512]; uint32 a0, a1, a2, a3; union cpuinfo mipsinfo; int i; // printf("IOP call @ %08x\n", pc); // prefetch parameters mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo); a0 = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R5, &mipsinfo); a1 = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R6, &mipsinfo); a2 = mipsinfo.i; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R7, &mipsinfo); a3 = mipsinfo.i; scan = (pc&0x0fffffff)/4; while ((cpu->psx_ram[scan] != LE32(0x41e00000)) && (scan >= (0x10000/4))) { scan--; } if (cpu->psx_ram[scan] != LE32(0x41e00000)) { printf("FATAL ERROR: couldn't find IOP link signature\n"); return; } scan += 3; // skip zero and version memcpy(name, &cpu->psx_ram[scan], 8); name[8] = '\0'; // printf("IOP: call module [%s] service %d (PC=%08x)\n", name, callnum, pc); if (!strcmp(name, "stdio")) { switch (callnum) { case 4: // printf mname = (char *)cpu->psx_ram; mname += a0 & 0x1fffff; mname += (a0 & 3); iop_sprintf(cpu, out, mname, CPUINFO_INT_REGISTER + MIPS_R5); // a1 is first parm /* if (out[strlen(out)-1] != '\n') { strcat(out, "\n"); }*/ #if DEBUG_HLE_IOP printf("%s", out); #endif break; default: printf("IOP: Unhandled service %d for module %s\n", callnum, name); break; } } else if (!strcmp(name, "sifman")) { switch (callnum) { case 5: // sceSifInit #if DEBUG_HLE_IOP printf("IOP: sceSifInit()\n"); #endif mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 7: // sceSifSetDma #if DEBUG_HLE_IOP printf("IOP: sceSifSetDma(%08x %08x)\n", a0, a1); #endif mipsinfo.i = 1; // nonzero = success mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 8: // sceSifDmaStat #if DEBUG_HLE_IOP printf("IOP: sceSifDmaStat(%08x)\n", a0); #endif mipsinfo.i = -1; // dma completed mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 29: // sceSifCheckInit #if DEBUG_HLE_IOP printf("IOP: sceSifCheckInit()\n"); #endif mipsinfo.i = 1; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; default: printf("IOP: Unhandled service %d for module %s\n", callnum, name); break; } } else if (!strcmp(name, "thbase")) { uint32 newAlloc; switch (callnum) { case 4: // CreateThread #if DEBUG_THREADING printf("IOP: CreateThread(%08x)\n", a0); #endif a0 &= 0x1fffff; a0 /= 4; #if DEBUG_THREADING printf(" : flags %x routine %08x pri %x stacksize %d refCon %08x\n", cpu->psx_ram[a0], cpu->psx_ram[a0+1], cpu->psx_ram[a0+2], cpu->psx_ram[a0+3], cpu->psx_ram[a0+4]); #endif newAlloc = psf2_get_loadaddr(); // force 16-byte alignment if (newAlloc & 0xf) { newAlloc &= ~0xf; newAlloc += 16; } psf2_set_loadaddr(newAlloc + LE32(cpu->psx_ram[a0+3])); threads[iNumThreads].iState = TS_CREATED; threads[iNumThreads].stackloc = newAlloc; threads[iNumThreads].flags = LE32(cpu->psx_ram[a0]); threads[iNumThreads].routine = LE32(cpu->psx_ram[a0+2]); threads[iNumThreads].stacksize = LE32(cpu->psx_ram[a0+3]); threads[iNumThreads].refCon = LE32(cpu->psx_ram[a0+4]); mipsinfo.i = iNumThreads; iNumThreads++; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 6: // StartThread #if DEBUG_THREADING printf("IOP: StartThread(%d %d)\n", a0, a1); #endif FreezeThread(cpu, iCurThread, 1); ThawThread(cpu, a0); iCurThread = a0; break; case 20:// GetThreadID #if DEBUG_THREADING printf("IOP: GetThreadId()\n"); #endif mipsinfo.i = iCurThread; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 24:// SleepThread #if DEBUG_THREADING mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); printf("IOP: SleepThread() [curThread %d, PC=%x]\n", iCurThread, mipsinfo.i); #endif FreezeThread(cpu, iCurThread, 1); threads[iCurThread].iState = TS_SLEEPING; iCurThread = -1; ps2_reschedule(cpu); break; case 25:// WakeupThread #if DEBUG_THREADING printf("IOP: WakeupThread(%d)\n", a0); #endif // set thread to "ready to go" threads[a0].iState = TS_READY; break; case 26:// iWakeupThread #if DEBUG_THREADING printf("IOP: iWakeupThread(%d)\n", a0); #endif // set thread to "ready to go" if it's not running if (threads[a0].iState != TS_RUNNING) { threads[a0].iState = TS_READY; } break; case 33:// DelayThread { double dTicks; int i; #if DEBUG_THREADING mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); printf("IOP: DelayThread(%d) (PC=%x) [curthread = %d]\n", a0, mipsinfo.i, iCurThread); #endif if (a0 < 100) { a0 = 100; } dTicks = (double)a0; FreezeThread(cpu, iCurThread, 1); threads[iCurThread].iState = TS_WAITDELAY; dTicks /= (double)1000000.0; dTicks *= (double)36864000.0; // 768*48000 = IOP native-mode clock rate threads[iCurThread].waitparm = (uint32)dTicks; iCurThread = -1; ps2_reschedule(cpu); } break; case 34://GetSystemTime #if DEBUG_HLE_IOP printf("IOP: GetSystemTime(%x)\n", a0); #endif a0 &= 0x1fffff; a0 /= 4; cpu->psx_ram[a0] = LE32(sys_time & 0xffffffff); // low cpu->psx_ram[a0+1] = LE32(sys_time >> 32); // high mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 39:// USec2SysClock { uint64 dTicks = (uint64)a0; uint32 hi, lo; #if DEBUG_HLE_IOP printf("IOP: USec2SysClock(%d %08x)\n", a0, a1); #endif dTicks *= (uint64)36864000; dTicks /= (uint64)1000000; hi = dTicks>>32; lo = dTicks & 0xffffffff; cpu->psx_ram[((a1 & 0x1fffff)/4)] = LE32(lo); cpu->psx_ram[((a1 & 0x1fffff)/4)+1] = LE32(hi); mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 40://SysClock2USec { uint64 temp; uint32 seconds, usec; #if DEBUG_HLE_IOP printf("IOP: SysClock2USec(%08x %08x %08x)\n", a0, a1, a2); #endif a0 &= 0x1fffff; a1 &= 0x1fffff; a2 &= 0x1fffff; a0 /= 4; a1 /= 4; a2 /= 4; temp = LE32(cpu->psx_ram[a0]); temp |= (uint64)LE32(cpu->psx_ram[a0+1])<<32; temp *= (uint64)1000000; temp /= (uint64)36864000; // temp now is USec seconds = (temp / 1000000) & 0xffffffff; usec = (temp % 1000000) & 0xffffffff; cpu->psx_ram[a1] = LE32(seconds); cpu->psx_ram[a2] = LE32(usec); } break; default: printf("IOP: Unhandled service %d for module %s\n", callnum, name); break; } } else if (!strcmp(name, "thevent")) { switch (callnum) { case 4: // CreateEventFlag mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); #if DEBUG_HLE_IOP printf("IOP: CreateEventFlag(%08x) (PC=%x)\n", a0, mipsinfo.i); #endif a0 &= 0x1fffff; a0 /= 4; evflags[iNumFlags].type = LE32(cpu->psx_ram[a0]); evflags[iNumFlags].value = LE32(cpu->psx_ram[a0+1]); evflags[iNumFlags].param = LE32(cpu->psx_ram[a0+2]); evflags[iNumFlags].inUse = 1; #if DEBUG_HLE_IOP printf(" Flag %02d: type %d init %08x param %08x\n", iNumFlags, evflags[iNumFlags].type, evflags[iNumFlags].value, evflags[iNumFlags].param); #endif mipsinfo.i = iNumFlags+1; iNumFlags++; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 6: // SetEventFlag a0--; #if DEBUG_HLE_IOP printf("IOP: SetEventFlag(%d %08x)\n", a0, a1); #endif evflags[a0].value |= a1; mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 7: // iSetEventFlag a0--; #if DEBUG_HLE_IOP printf("IOP: iSetEventFlag(%08x %08x)\n", a0, a1); #endif evflags[a0].value |= a1; mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); for (i=0; i < iNumThreads; i++) { if ((threads[i].iState == TS_WAITEVFLAG) && (threads[i].waitparm == a0)) { threads[i].iState = TS_READY; } } break; case 8: // ClearEventFlag a0--; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); #if DEBUG_HLE_IOP printf("IOP: ClearEventFlag(%d %08x) (PC=%x)\n", a0, a1, mipsinfo.i); #endif evflags[a0].value &= a1; mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 9: // iClearEventFlag a0--; #if DEBUG_HLE_IOP printf("IOP: iClearEventFlag(%d %08x)\n", a0, a1); #endif evflags[a0].value &= a1; mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 10:// WaitEventFlag a0--; #if DEBUG_HLE_IOP mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); printf("IOP: WaitEventFlag(%d %08x %d %08x PC=%x)\n", a0, a1, a2, a3, mipsinfo.i); #endif // if we're not set, freeze this thread if (!(evflags[a0].value & a1)) { FreezeThread(cpu, iCurThread, 1); threads[iCurThread].iState = TS_WAITEVFLAG; threads[iCurThread].waitparm = a0; iCurThread = -1; ps2_reschedule(cpu); } else { mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; default: printf("IOP: Unhandled service %d for module %s\n", callnum, name); break; } } else if (!strcmp(name, "thsemap")) { int foundthread; switch (callnum) { case 4: // CreateSema #if DEBUG_HLE_IOP printf("IOP: CreateSema(%08x)\n", a0); #endif mipsinfo.i = -1; for (i = 0; i < SEMA_MAX; i++) { if (!semaphores[i].inuse) { mipsinfo.i = i; break; } } if (mipsinfo.i == -1) { printf("IOP: out of semaphores!\n"); } a0 &= 0x7fffffff; a0 /= 4; // printf("Sema %d Parms: %08x %08x %08x %08x\n", mipsinfo.i, cpu->psx_ram[a0], cpu->psx_ram[a0+1], cpu->psx_ram[a0+2], cpu->psx_ram[a0+3]); if (mipsinfo.i != -1) { semaphores[mipsinfo.i].attr = LE32(cpu->psx_ram[a0]); semaphores[mipsinfo.i].option = LE32(cpu->psx_ram[a0+1]); semaphores[mipsinfo.i].init = LE32(cpu->psx_ram[a0+2]); semaphores[mipsinfo.i].max = LE32(cpu->psx_ram[a0+3]); semaphores[mipsinfo.i].current = semaphores[mipsinfo.i].init; semaphores[mipsinfo.i].inuse = 1; } mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 6: // SignalSema #if DEBUG_HLE_IOP printf("IOP: SignalSema(%d) (current %d)\n", a0, semaphores[a0].current); #endif foundthread = 0; for (i=0; i < iNumThreads; i++) { if ((threads[i].iState == TS_WAITSEMA) && (threads[i].waitparm == a0)) { threads[i].iState = TS_READY; semaphores[a0].threadsWaiting--; foundthread = 1; break; } } mipsinfo.i = 0; if (!foundthread) { if (semaphores[a0].current < semaphores[a0].max) { semaphores[a0].current++; } else { mipsinfo.i = -420; // semaphore overflow } } mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 7: // iSignalSema #if DEBUG_HLE_IOP printf("IOP: iSignalSema(%d)\n", a0); #endif foundthread = 0; for (i=0; i < iNumThreads; i++) { if ((threads[i].iState == TS_WAITSEMA) && (threads[i].waitparm == a0)) { threads[i].iState = TS_READY; semaphores[a0].threadsWaiting--; foundthread = 1; break; } } mipsinfo.i = 0; if (!foundthread) { if (semaphores[a0].current < semaphores[a0].max) { semaphores[a0].current++; } else { mipsinfo.i = -420; // semaphore overflow } } mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 8: // WaitSema #if DEBUG_HLE_IOP mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); printf("IOP: WaitSema(%d) (cnt %d) (th %d) (PC=%x)\n", a0, iCurThread, semaphores[a0].current, mipsinfo.i); #endif if (semaphores[a0].current > 0) { semaphores[a0].current--; } else { FreezeThread(cpu,iCurThread, 1); threads[iCurThread].iState = TS_WAITSEMA; threads[iCurThread].waitparm = a0; ps2_reschedule(cpu); } mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; default: printf("IOP: Unhandled service %d for module %s\n", callnum, name); break; } } else if (!strcmp(name, "timrman")) { switch (callnum) { case 4: // AllocHardTimer #if DEBUG_HLE_IOP printf("IOP: AllocHardTimer(%d %d %d)\n", a0, a1, a2); #endif // source, size, prescale if (a1 != 32) { printf("IOP: AllocHardTimer doesn't support 16-bit timers!\n"); } iop_timers[iNumTimers].source = a0; iop_timers[iNumTimers].prescale = a2; mipsinfo.i = iNumTimers+1; iNumTimers++; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 6: // FreeHardTimer #if DEBUG_HLE_IOP printf("IOP: FreeHardTimer(%d)\n", a0); #endif mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 10:// GetTimerCounter mipsinfo.i = iop_timers[a0-1].count; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 20: // SetTimerHandler #if DEBUG_HLE_IOP printf("IOP: SetTimerHandler(%d %d %08x %08x)\n", a0, a1, a2, a3); #endif // id, compare, handler, common (last is param for handler) iop_timers[a0-1].target = a1; iop_timers[a0-1].handler = a2; iop_timers[a0-1].hparam = a3; mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 22: // SetupHardTimer #if DEBUG_HLE_IOP printf("IOP: SetupHardTimer(%d %d %d %d)\n", a0, a1, a2, a3); #endif // id, source, mode, prescale iop_timers[a0-1].source = a1; iop_timers[a0-1].mode = a2; iop_timers[a0-1].prescale = a3; mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 23: // StartHardTimer #if DEBUG_HLE_IOP printf("IOP: StartHardTimer(%d)\n", a0); #endif iop_timers[a0-1].iActive = 1; iop_timers[a0-1].count = 0; mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 24: // StopHardTimer #if DEBUG_HLE_IOP printf("IOP: StopHardTimer(%d)\n", a0); #endif iop_timers[a0-1].iActive = 0; mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; default: printf("IOP: Unhandled service %d for module %s\n", callnum, name); break; } } else if (!strcmp(name, "sysclib")) { switch (callnum) { case 12: // memcpy { uint8 *dst, *src; #if DEBUG_HLE_IOP printf("IOP: memcpy(%08x, %08x, %d)\n", a0, a1, a2); #endif dst = (uint8 *)&cpu->psx_ram[(a0&0x1fffff)/4]; src = (uint8 *)&cpu->psx_ram[(a1&0x1fffff)/4]; // get exact byte alignment dst += a0 % 4; src += a1 % 4; while (a2) { *dst = *src; dst++; src++; a2--; } // v0 = a0 mipsinfo.i = a0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 13: // memmove { uint8 *dst, *src; #if DEBUG_HLE_IOP printf("IOP: memmove(%08x, %08x, %d)\n", a0, a1, a2); #endif dst = (uint8 *)&cpu->psx_ram[(a0&0x1fffff)/4]; src = (uint8 *)&cpu->psx_ram[(a1&0x1fffff)/4]; // get exact byte alignment dst += a0 % 4; src += a1 % 4; dst += a2 - 1; src += a2 - 1; while (a2) { *dst = *src; dst--; src--; a2--; } // v0 = a0 mipsinfo.i = a0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 14: // memset { uint8 *dst; #if DEBUG_HLE_IOP mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); printf("IOP: memset(%08x, %02x, %d) [PC=%x]\n", a0, a1, a2, mipsinfo.i); #endif dst = (uint8 *)&cpu->psx_ram[(a0&0x1fffff)/4]; dst += (a0 & 3); memset(dst, a1, a2); } break; case 17: // bzero { uint8 *dst; #if DEBUG_HLE_IOP printf("IOP: bzero(%08x, %08x)\n", a0, a1); #endif dst = (uint8 *)&cpu->psx_ram[(a0&0x1fffff)/4]; dst += (a0 & 3); memset(dst, 0, a1); } break; case 19: // sprintf mname = (char *)cpu->psx_ram; str1 = (char *)cpu->psx_ram; mname += a0 & 0x1fffff; str1 += a1 & 0x1fffff; #if DEBUG_HLE_IOP mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); printf("IOP: sprintf(%08x, %s, ...) [PC=%08x]\n", a0, str1, (uint32)mipsinfo.i); printf("%x %x %x %x\n", a0, a1, a2, a3); #endif iop_sprintf(cpu, mname, str1, CPUINFO_INT_REGISTER + MIPS_R6); // a2 is first parameter #if DEBUG_HLE_IOP printf(" = [%s]\n", mname); #endif break; case 23: // strcpy { uint8 *dst, *src; #if DEBUG_HLE_IOP printf("IOP: strcpy(%08x, %08x)\n", a0, a1); #endif dst = (uint8 *)&cpu->psx_ram[(a0&0x1fffff)/4]; src = (uint8 *)&cpu->psx_ram[(a1&0x1fffff)/4]; // get exact byte alignment dst += a0 % 4; src += a1 % 4; while (*src != '\0') { *dst = *src; dst++; src++; } *dst = '\0'; // v0 = a0 mipsinfo.i = a0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 27: // strlen { char *dst; #if DEBUG_HLE_IOP mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); printf("IOP: strlen(%08x) [PC=%x]\n", a0, mipsinfo.i); #endif dst = (char *)&cpu->psx_ram[(a0&0x1fffff)/4]; dst += (a0 & 3); mipsinfo.i = strlen(dst); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 30: // strncpy { char *dst, *src; #if DEBUG_HLE_IOP printf("IOP: strncpy(%08x, %08x, %d)\n", a0, a1, a2); #endif dst = (char *)&cpu->psx_ram[(a0&0x1fffff)/4]; src = (char *)&cpu->psx_ram[(a1&0x1fffff)/4]; // get exact byte alignment dst += a0 % 4; src += a1 % 4; while ((*src != '\0') && (a2 > 0)) { *dst = *src; dst++; src++; a2--; } *dst = '\0'; // v0 = a0 mipsinfo.i = a0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); } break; case 36: // strtol mname = (char *)&cpu->psx_ram[(a0 & 0x1fffff)/4]; mname += (a0 & 3); if (a1) { printf("IOP: Unhandled strtol with non-NULL second parm\n"); } mipsinfo.i = strtol(mname, NULL, a2); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; default: printf("IOP: Unhandled service %d for module %s\n", callnum, name); break; } } else if (!strcmp(name, "intrman")) { switch (callnum) { case 4: // RegisterIntrHandler #if DEBUG_HLE_IOP printf("IOP: RegisterIntrHandler(%d %08x %08x %08x)\n", a0, a1, a2, a3); #endif if (a0 == 9) { irq9_fval = a1; irq9_cb = a2; irq9_flag = a3; } // DMA4? if (a0 == 36) { dma4_fval = a1; dma4_cb = a2; dma4_flag = a3; } // DMA7? if (a0 == 40) { dma7_fval = a1; dma7_cb = a2; dma7_flag = a3; } break; case 5: // ReleaseIntrHandler #if DEBUG_HLE_IOP printf("IOP: ReleaseIntrHandler(%d)\n", a0); #endif break; case 6: // EnableIntr #if DEBUG_HLE_IOP printf("IOP: EnableIntr(%d)\n", a0); #endif break; case 7: // DisableIntr #if DEBUG_HLE_IOP printf("IOP: DisableIntr(%d)\n", a0); #endif break; case 8: // CpuDisableIntr #if DEBUG_HLE_IOP printf("IOP: CpuDisableIntr(%d)\n", a0); #endif break; case 9: // CpuEnableIntr #if DEBUG_HLE_IOP printf("IOP: CpuEnableIntr(%d)\n", a0); #endif break; case 17: // CpuSuspendIntr #if DEBUG_HLE_IOP printf("IOP: CpuSuspendIntr\n"); #endif // if already suspended, return an error code if (intr_susp) { mipsinfo.i = -102; } else { mipsinfo.i = 0; } intr_susp = 1; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 18: // CpuResumeIntr #if DEBUG_HLE_IOP printf("IOP: CpuResumeIntr\n"); #endif intr_susp = 0; mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 23: // QueryIntrContext #if DEBUG_HLE_IOP mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); printf("IOP: QueryIntrContext(PC=%x)\n", mipsinfo.i); #endif mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; default: printf("IOP: Unhandled service %d for module %s\n", callnum, name); break; } } else if (!strcmp(name, "loadcore")) { switch (callnum) { case 5: // FlushDcache #if DEBUG_HLE_IOP printf("IOP: FlushDcache()\n"); #endif break; case 6: // RegisterLibraryEntries a0 &= 0x1fffff; #if DEBUG_HLE_IOP mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); printf("IOP: RegisterLibraryEntries(%08x) (PC=%x)\n", a0, mipsinfo.i); #endif if (cpu->psx_ram[a0/4] == LE32(0x41c00000)) { a0 += 3*4; memcpy(®libs[iNumLibs].name, &cpu->psx_ram[a0/4], 8); reglibs[iNumLibs].name[8] = '\0'; #if DEBUG_HLE_IOP printf("Lib name [%s]\n", ®libs[iNumLibs].name); #endif a0 += 2*4; reglibs[iNumLibs].dispatch = a0; iNumLibs++; } else { printf("ERROR: Entry table signature missing\n"); } mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; default: printf("IOP: Unhandled service %d for module %s\n", callnum, name); break; } } else if (!strcmp(name, "sysmem")) { uint32 newAlloc; switch (callnum) { case 4: // AllocMemory newAlloc = psf2_get_loadaddr(); // make sure we're 16-byte aligned if (newAlloc & 15) { newAlloc &= ~15; newAlloc += 16; } if (a1 & 15) { a1 &= ~15; a1 += 16; } if (a1 == 1114112) // HACK for crappy code in Shadow Hearts rip that assumes the buffer address { printf("SH Hack: was %x now %x\n", newAlloc, 0x60000); newAlloc = 0x60000; } psf2_set_loadaddr(newAlloc + a1); #if DEBUG_HLE_IOP printf("IOP: AllocMemory(%d, %d, %x) = %08x\n", a0, a1, a2, newAlloc|0x80000000); #endif mipsinfo.i = newAlloc; // | 0x80000000; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 5: // FreeMemory #if DEBUG_HLE_IOP printf("IOP: FreeMemory(%x)\n", a0); #endif break; case 7: // QueryMaxFreeMemSize #if DEBUG_HLE_IOP printf("IOP: QueryMaxFreeMemSize\n"); #endif mipsinfo.i = (2*1024*1024) - psf2_get_loadaddr(); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 8: // QueryTotalFreeMemSize #if DEBUG_HLE_IOP printf("IOP: QueryTotalFreeMemSize\n"); #endif mipsinfo.i = (2*1024*1024) - psf2_get_loadaddr(); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 14: // Kprintf mname = (char *)cpu->psx_ram; mname += a0 & 0x1fffff; mname += (a0 & 3); iop_sprintf(cpu, out, mname, CPUINFO_INT_REGISTER + MIPS_R5); // a1 is first parm if (out[strlen(out)-1] != '\n') { strcat(out, "\n"); } // filter out ESC characters { int ch; for (ch = 0; ch < strlen(out); ch++) { if (out[ch] == 27) { out[ch] = ']'; } } } #if DEBUG_HLE_IOP mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); printf("KTTY: %s [PC=%x]\n", out, mipsinfo.i); #endif #if 0 { FILE *f; f = fopen("psxram.bin", "wb"); fwrite(cpu->psx_ram, 2*1024*1024, 1, f); fclose(f); } #endif break; default: printf("IOP: Unhandled service %d for module %s\n", callnum, name); break; } } else if (!strcmp(name, "modload")) { uint8 *tempmem; uint32 newAlloc; switch (callnum) { case 7: // LoadStartModule mname = (char *)&cpu->psx_ram[(a0 & 0x1fffff)/4]; mname += 8; str1 = (char *)&cpu->psx_ram[(a2 & 0x1fffff)/4]; #if DEBUG_HLE_IOP printf("LoadStartModule: %s\n", mname); #endif // get 2k for our parameters newAlloc = psf2_get_loadaddr(); // force 16-byte alignment if (newAlloc & 0xf) { newAlloc &= ~0xf; newAlloc += 16; } psf2_set_loadaddr(newAlloc + 2048); tempmem = (uint8 *)malloc(2*1024*1024); if (psf2_load_file(cpu, mname, tempmem, 2*1024*1024) != 0xffffffff) { uint32 start; int i; start = psf2_load_elf(cpu, tempmem, 2*1024*1024); if (start != 0xffffffff) { uint32 args[20], numargs = 1, argofs; uint8 *argwalk = (uint8 *)cpu->psx_ram, *argbase; argwalk += (a2 & 0x1fffff); argbase = argwalk; args[0] = a0; // program name is argc[0] argofs = 0; if (a1 > 0) { args[numargs] = a2; numargs++; while (a1) { if ((*argwalk == 0) && (a1 > 1)) { args[numargs] = a2 + argofs + 1; numargs++; } argwalk++; argofs++; a1--; } } for (i = 0; i < numargs; i++) { #if DEBUG_HLE_IOP // printf("Arg %d: %08x [%s]\n", i, args[i], &argbase[args[i]-a2]); #endif cpu->psx_ram[(newAlloc/4)+i] = LE32(args[i]); } // set argv and argc mipsinfo.i = numargs; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo); mipsinfo.i = 0x80000000 | newAlloc; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R5, &mipsinfo); // leave RA alone, PC = module start // (NOTE: we get called in the delay slot!) mipsinfo.i = start - 4; mips_set_info(cpu,CPUINFO_INT_PC, &mipsinfo); } } free(tempmem); break; default: printf("IOP: Unhandled service %d for module %s\n", callnum, name); break; } } else if (!strcmp(name, "ioman")) { switch (callnum) { case 4: // open { int i, slot2use; slot2use = -1; for (i = 0; i < MAX_FILE_SLOTS; i++) { if (filestat[i] == 0) { slot2use = i; break; } } if (slot2use == -1) { printf("IOP: out of file slots!\n"); mipsinfo.i = 0xffffffff; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); return; } mname = (char *)cpu->psx_ram; mname += (a0 & 0x1fffff); if (!strncmp(mname, "aofile:", 7)) { mname += 8; } else if (!strncmp(mname, "hefile:", 7)) { mname += 8; } else if (!strncmp(mname, "host0:", 6)) { mname += 7; } mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); #if DEBUG_HLE_IOP printf("IOP: open(\"%s\") (PC=%08x)\n", mname, mipsinfo.i); #endif filedata[slot2use] = malloc(6*1024*1024); filesize[slot2use] = psf2_load_file(cpu, mname, filedata[slot2use], 6*1024*1024); filepos[slot2use] = 0; filestat[slot2use] = 1; if (filesize[slot2use] == 0xffffffff) { mipsinfo.i = filesize[slot2use]; } else { mipsinfo.i = slot2use; } } mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 5: // close #if DEBUG_HLE_IOP mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); printf("IOP: close(%d) (PC=%08x)\n", a0, mipsinfo.i); #endif free(filedata[a0]); filedata[a0] = (uint8 *)NULL; filepos[a0] = 0; filesize[a0] = 0; filestat[a0] = 0; break; case 6: // read #if DEBUG_HLE_IOP printf("IOP: read(%x %x %d) [pos %d size %d]\n", a0, a1, a2, filepos[a0], filesize[a0]); #endif if (filepos[a0] >= filesize[a0]) { mipsinfo.i = 0; } else { uint8 *rp; if ((filepos[a0] + a2) > filesize[a0]) { a2 = filesize[a0] - filepos[a0]; } rp = (uint8 *)cpu->psx_ram; rp += (a1 & 0x1fffff); memcpy(rp, &filedata[a0][filepos[a0]], a2); filepos[a0] += a2; mipsinfo.i = a2; } mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 8: // lseek #if DEBUG_HLE_IOP mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); printf("IOP: lseek(%d, %d, %s) (PC=%08x)\n", a0, a1, seek_types[a2], mipsinfo.i); #endif switch (a2) { case 0: // SEEK_SET if (a1 <= filesize[a0]) { filepos[a0] = a1; } break; case 1: // SEEK_CUR if ((a1 + filepos[a0]) < filesize[a0]) { filepos[a0] += a1; } break; case 2: // SEEK_END filepos[a0] = filesize[a0] - a1; break; } mipsinfo.i = filepos[a0]; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 20: // AddDrv #if DEBUG_HLE_IOP printf("IOP: AddDrv(%x)\n", a0); #endif mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; case 21: // DelDrv #if DEBUG_HLE_IOP printf("IOP: DelDrv(%x)\n", a0); #endif mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo); break; default: printf("IOP: Unhandled service %d for module %s\n", callnum, name); } } else { int lib; if (iNumLibs > 0) { for (lib = 0; lib < iNumLibs; lib++) { if (!strcmp(name, reglibs[lib].name)) { #if DEBUG_HLE_IOP uint32 PC; mips_get_info(cpu, CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo); PC = mipsinfo.i; #endif // zap the delay slot handling mipsinfo.i = 0; mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo); mips_set_info(cpu,CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo); mipsinfo.i = LE32(cpu->psx_ram[(reglibs[lib].dispatch/4) + callnum]); // (NOTE: we get called in the delay slot!) #if DEBUG_HLE_IOP printf("IOP: Calling %s (%d) service %d => %08x (parms %08x %08x %08x %08x) (PC=%x)\n", reglibs[lib].name, lib, callnum, (uint32)mipsinfo.i, a0, a1, a2, a3, PC); #endif #if 0 if (!strcmp(reglibs[lib].name, "ssd")) { if (callnum == 37) { psxcpu_verbose = 4096; } } #endif mipsinfo.i -= 4; mips_set_info(cpu,CPUINFO_INT_PC, &mipsinfo); return; } } } printf("IOP: Unhandled service %d for module %s\n", callnum, name); } }