Cog/Frameworks/AudioOverload/aosdk/eng_psf/psx_hw.c
2009-02-28 22:04:03 -08:00

3540 lines
76 KiB
C

/*
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 <stdio.h>
#include "ao.h"
#include "cpuintrf.h"
#include "psx.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 void mips_get_info(UINT32 state, union cpuinfo *info);
extern void mips_set_info(UINT32 state, union cpuinfo *info);
extern int psxcpu_verbose;
extern uint16 SPUreadRegister(uint32 reg);
extern void SPUwriteRegister(uint32 reg, uint16 val);
extern void SPUwriteDMAMem(uint32 usPSXMem,int iSize);
extern void SPUreadDMAMem(uint32 usPSXMem,int iSize);
extern void mips_shorten_frame(void);
extern int mips_execute( int cycles );
extern uint32 psf2_load_file(char *file, uint8 *buf, uint32 buflen);
extern uint32 psf2_load_elf(uint8 *start, uint32 len);
void psx_hw_runcounters(void);
int mips_get_icount(void);
void mips_set_icount(int count);
extern int psf_refresh;
static int skipyet = 0;
// SPU2
extern void SPU2write(unsigned long reg, unsigned short val);
extern unsigned short SPU2read(unsigned long reg);
extern void SPU2readDMA4Mem(uint32 usPSXMem,int iSize);
extern void SPU2writeDMA4Mem(uint32 usPSXMem,int iSize);
extern void SPU2readDMA7Mem(uint32 usPSXMem,int iSize);
extern void SPU2writeDMA7Mem(uint32 usPSXMem,int iSize);
extern void SPU2interruptDMA4(void);
extern void SPU2interruptDMA7(void);
#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(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
// PSX main RAM
uint32 psx_ram[(2*1024*1024)/4];
uint32 psx_scratch[0x400];
// backup image to restart songs
uint32 initial_ram[(2*1024*1024)/4];
uint32 initial_scratch[0x400];
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(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(CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo);
threads[iThread].save_regs[i] = mipsinfo.i;
}
mips_get_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
threads[iThread].save_regs[32] = mipsinfo.i;
mips_get_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
threads[iThread].save_regs[33] = mipsinfo.i;
mips_get_info(CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo);
threads[iThread].save_regs[35] = mipsinfo.i;
mips_get_info(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(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
}
else
{
mips_get_info(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(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(CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo);
}
mipsinfo.i = threads[iThread].save_regs[32];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
mipsinfo.i = threads[iThread].save_regs[33];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
mipsinfo.i = threads[iThread].save_regs[34];
mips_set_info(CPUINFO_INT_PC, &mipsinfo);
mipsinfo.i = threads[iThread].save_regs[35];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo);
mipsinfo.i = threads[iThread].save_regs[36];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo);
threads[iThread].iState = TS_RUNNING;
}
// find a new thread to run
static void ps2_reschedule(void)
{
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(iCurThread, 0);
}
ThawThread(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(); // kill the CPU
iCurThread = -1; // no threads are active
}
}
else
{
mips_shorten_frame(); // kill the CPU
iCurThread = -1; // no threads are active
}
}
}
static void psx_irq_update(void)
{
union cpuinfo mipsinfo;
if ((irq_data & irq_mask) != 0)
{ // assert the line
WAI = 0;
mipsinfo.i = ASSERT_LINE;
mips_set_info( CPUINFO_INT_INPUT_STATE + MIPS_IRQ0, &mipsinfo );
}
else
{
// clear the line
mipsinfo.i = CLEAR_LINE;
mips_set_info( CPUINFO_INT_INPUT_STATE + MIPS_IRQ0, &mipsinfo );
}
}
void psx_irq_set(uint32 irq)
{
irq_data |= irq;
psx_irq_update();
}
static uint32 gpu_stat = 0;
uint32 psx_hw_read(offs_t offset, uint32 mem_mask)
{
if (offset >= 0x00000000 && offset <= 0x007fffff)
{
offset &= 0x1fffff;
return LE32(psx_ram[offset>>2]);
}
if (offset >= 0x80000000 && offset <= 0x807fffff)
{
offset &= 0x1fffff;
return LE32(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(offset) & ~mem_mask;
}
else if (mem_mask == 0x0000ffff)
{
return SPUreadRegister(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(offset) & ~mem_mask;
}
else if (mem_mask == 0x0000ffff)
{
return SPU2read(offset)<<16;
}
else if (mem_mask == 0)
{
return SPU2read(offset) | SPU2read(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(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(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(madr&0x1fffff, bcr);
}
else
{
// printf("DMA4: SPU to RAM %08x\n", madr);
bcr = (bcr>>16) * (bcr & 0xffff) * 2;
SPUreadDMAMem(madr&0x1fffff, bcr);
}
}
static void ps2_dma4(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(madr&0x1fffff, bcr);
}
else
{
#if DEBUG_HLE_IOP
printf("DMA4: SPU2 to RAM %08x\n", madr);
#endif
bcr = (bcr>>16) * (bcr & 0xffff) * 4;
SPU2readDMA4Mem(madr&0x1fffff, bcr);
}
dma4_delay = 80;
}
static void ps2_dma7(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(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(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(CPUINFO_INT_PC, &mipsinfo);
psx_ram[offset>>2] &= LE32(mem_mask);
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(CPUINFO_INT_PC, &mipsinfo);
psx_ram[offset>>2] &= LE32(mem_mask);
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(offset, data);
return;
}
else if (mem_mask == 0x0000ffff)
{
SPUwriteRegister(offset, data>>16);
return;
}
else printf("SPU: write unknown mask %08x\n", mem_mask);
}
if (offset >= 0xbf900000 && offset <= 0xbf9007ff)
{
if (mem_mask == 0xffff0000)
{
SPU2write(offset, data);
return;
}
else if (mem_mask == 0x0000ffff)
{
SPU2write(offset, data>>16);
return;
}
else if (mem_mask == 0)
{
SPU2write(offset, data & 0xffff);
SPU2write(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(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();
return;
}
else if (offset == 0x1f801074)
{
irq_mask &= mem_mask;
irq_mask |= data;
psx_irq_update();
return;
}
// PS2 DMA4
if (offset == 0xbf8010c0)
{
dma4_madr = data;
return;
}
else if (offset == 0xbf8010c8)
{
dma4_chcr = data;
ps2_dma4(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(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(void)
{
psx_hw_runcounters();
if (!WAI)
mips_execute(768/CLOCK_DIV);
if (dma_timer)
{
dma_timer--;
if (dma_timer == 0)
{
dma_icr |= (1 << (24+4));
psx_irq_set(0x0008);
}
}
}
void ps2_hw_slice(void)
{
int i = 0;
timerexp = 0;
psx_hw_runcounters();
if (iCurThread != -1)
{
mips_execute(836/CLOCK_DIV);
}
else // no thread, don't run CPU, just update counters
{
if (timerexp)
{
ps2_reschedule();
if (iCurThread != -1)
{
mips_execute((836/CLOCK_DIV)-i);
i = (836/CLOCK_DIV);
}
}
}
}
static int fcnt = 0;
void psx_hw_frame(void)
{
if (psf_refresh == 50)
{
fcnt++;;
if (fcnt < 6)
{
psx_irq_set(1);
}
else
{
fcnt = 0;
}
}
else // NTSC
{
psx_irq_set(1);
}
}
void ps2_hw_frame(void)
{
ps2_reschedule();
}
// 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;
extern uint32 mips_get_cause(void);
extern uint32 mips_get_status(void);
extern void mips_set_status(uint32 status);
extern uint32 mips_get_ePC(void);
static uint32 irq_regs[37];
static int irq_mutex = 0;
static void call_irq_routine(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(CPUINFO_INT_REGISTER + MIPS_R0 + j, &mipsinfo);
irq_regs[j] = mipsinfo.i;
}
mips_get_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
irq_regs[32] = mipsinfo.i;
mips_get_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
irq_regs[33] = mipsinfo.i;
mips_get_info(CPUINFO_INT_PC, &mipsinfo);
irq_regs[34] = mipsinfo.i;
mips_get_info(CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo);
irq_regs[35] = mipsinfo.i;
mips_get_info(CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo);
irq_regs[36] = mipsinfo.i;
// PC = timer handler routine
mipsinfo.i = routine;
mips_set_info(CPUINFO_INT_PC, &mipsinfo);
// parameter in a0
mipsinfo.i = parameter;
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo);
// RA = a trap address we can set
mipsinfo.i = 0x80001000;
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
// make sure we're set
psx_ram[0x1000/4] = LE32(FUNCT_HLECALL);
softcall_target = 0;
oldICount = mips_get_icount();
while (!softcall_target)
{
mips_execute(10);
}
mips_set_icount(oldICount);
// restore IRQ regs
for (j = 0; j < 32; j++)
{
mipsinfo.i = irq_regs[j];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R0 + j, &mipsinfo);
}
mipsinfo.i = irq_regs[32];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
mipsinfo.i = irq_regs[33];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
mipsinfo.i = irq_regs[34];
mips_set_info(CPUINFO_INT_PC, &mipsinfo);
mipsinfo.i = irq_regs[35];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo);
mipsinfo.i = irq_regs[36];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo);
irq_mutex = 0;
}
void psx_bios_exception(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(CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo);
a0 = mipsinfo.i;
switch (mips_get_cause() & 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(CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo);
irq_regs[i] = mipsinfo.i;
}
mips_get_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
irq_regs[32] = mipsinfo.i;
mips_get_info(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(CPUINFO_INT_PC, &mipsinfo);
mipsinfo.i = 0x80001000;
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
// make sure we're set
psx_ram[0x1000/4] = LE32(FUNCT_HLECALL);
softcall_target = 0;
oldICount = mips_get_icount();
while (!softcall_target)
{
mips_execute(10);
}
mips_set_icount(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(CPUINFO_INT_PC, &mipsinfo);
mipsinfo.i = 0x80001000;
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
// make sure we're set
psx_ram[0x1000/4] = LE32(FUNCT_HLECALL);
softcall_target = 0;
oldICount = mips_get_icount();
while (!softcall_target)
{
mips_execute(10);
}
mips_set_icount(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(0x1f801070, 0xffffffff, 0);
a0 = entry_int;
// printf("taking entry_int\n");
// RA (and PC)
mipsinfo.i = LE32(psx_ram[((a0&0x1fffff)+0)/4]);
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
mips_set_info(CPUINFO_INT_PC, &mipsinfo);
// SP
mipsinfo.i = LE32(psx_ram[((a0&0x1fffff)+4)/4]);
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R29, &mipsinfo);
// FP
mipsinfo.i = LE32(psx_ram[((a0&0x1fffff)+8)/4]);
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R30, &mipsinfo);
// S0-S7 are next
for (i = 0; i < 8; i++)
{
mipsinfo.i = LE32(psx_ram[((a0&0x1fffff)+12+(i*4))/4]);
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R16 + i, &mipsinfo);
}
// GP
mipsinfo.i = LE32(psx_ram[((a0&0x1fffff)+44)/4]);
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R28, &mipsinfo);
// v0 = 1
mipsinfo.i = 1;
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
}
else
{
psx_hw_write(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(CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo);
}
mipsinfo.i = irq_regs[32];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
mipsinfo.i = irq_regs[33];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
mipsinfo.i = mips_get_ePC();
mips_set_info(CPUINFO_INT_PC, &mipsinfo);
status = mips_get_status();
status = (status & 0xfffffff0) | ((status & 0x3c)>>2);
mips_set_status(status);
}
break;
case 0x20: // syscall
// syscall always farks with the status, so get it now
status = mips_get_status();
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() + 4;
mips_set_info(CPUINFO_INT_PC, &mipsinfo);
// and update the status accordingly
status = (status & 0xfffffff0) | ((status & 0x3c)>>2);
mips_set_status(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<<i))
{
spec = i;
break;
}
}
}
return spec;
}
void psx_hw_init(void)
{
timerexp = 0;
memset(filestat, 0, sizeof(filestat));
memset(filedata, 0, sizeof(filedata));
dma4_cb = dma7_cb = 0;
sys_time = 0;
// clear registered libraries table
memset(reglibs, 0, sizeof(reglibs));
iNumLibs = 0;
memset(evflags, 0, sizeof(evflags));
iNumFlags = 0;
memset(threads, 0, sizeof(threads));
iNumThreads = 1; // we always have at least one thread
memset(semaphores, 0, sizeof(semaphores));
iNumSema = 0;
// set the initial thread to "RUNNING"
threads[0].iState = TS_RUNNING;
iCurThread = 0;
memset(iop_timers, 0, sizeof(iop_timers));
iNumTimers = 0;
// set PS1 BIOS HLE breakpoints
psx_ram[0xa0/4] = LE32(FUNCT_HLECALL);
psx_ram[0xb0/4] = LE32(FUNCT_HLECALL);
psx_ram[0xc0/4] = LE32(FUNCT_HLECALL);
Event = (EvtCtrlBlk *)&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(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(pc);
return;
}
if (pc == 0x80001000)
{
// printf("hit softcall target\n");
softcall_target = 1;
return;
}
mips_get_info(CPUINFO_INT_REGISTER + MIPS_R9, &mipsinfo);
subcall = mipsinfo.i & 0xff;
// most calls have a0/a1 as parameters, so prefetch them
mips_get_info(CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo);
a0 = mipsinfo.i;
mips_get_info(CPUINFO_INT_REGISTER + MIPS_R5, &mipsinfo);
a1 = mipsinfo.i;
mips_get_info(CPUINFO_INT_REGISTER + MIPS_R6, &mipsinfo);
a2 = mipsinfo.i;
mips_get_info(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(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
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(CPUINFO_INT_REGISTER + MIPS_R29, &mipsinfo);
psx_ram[((a0&0x1fffff)+4)/4] = LE32(mipsinfo.i);
// FP
mips_get_info(CPUINFO_INT_REGISTER + MIPS_R30, &mipsinfo);
psx_ram[((a0&0x1fffff)+8)/4] = LE32(mipsinfo.i);
// S0-S7 are next
for (i = 0; i < 8; i++)
{
mips_get_info(CPUINFO_INT_REGISTER + MIPS_R16 + i, &mipsinfo);
psx_ram[((a0&0x1fffff)+12+(i*4))/4] = LE32(mipsinfo.i);
}
// GP
mips_get_info(CPUINFO_INT_REGISTER + MIPS_R28, &mipsinfo);
psx_ram[((a0&0x1fffff)+44)/4] = LE32(mipsinfo.i);
// v0 = 0
mipsinfo.i = 0;
mips_set_info(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 *)psx_ram;
src = (uint8 *)psx_ram;
dst += (a0 & 0x1fffff);
src += (a1 & 0x1fffff);
// v0 = result
mipsinfo.i = strncmp((char *)dst, (char *)src, a2);
mips_set_info(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 *)psx_ram;
src = (uint8 *)psx_ram;
dst += (a0 & 0x1fffff);
src += (a1 & 0x1fffff);
while (*src)
{
*dst = *src;
dst++;
src++;
}
// v0 = a0
mipsinfo.i = a0;
mips_set_info(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 *)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 *)psx_ram;
src = (uint8 *)psx_ram;
dst += (a0 & 0x1fffff);
src += (a1 & 0x1fffff);
while (a2)
{
*dst = *src;
dst++;
src++;
a2--;
}
// v0 = a0
mipsinfo.i = a0;
mips_set_info(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 *)psx_ram;
dst += (a0 & 0x1fffff);
while (a2)
{
*dst = a1;
dst++;
a2--;
}
// v0 = a0
mipsinfo.i = a0;
mips_set_info(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(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(psx_ram[(chunk+BLK_SIZE)/4])) ||
(LE32(psx_ram[(chunk+BLK_STAT)/4]) == 1))
{
chunk = LE32(psx_ram[(chunk+BLK_FD)]);
}
// split free block
fd = chunk + 16 + a0; // free block starts after block record and allocation size
psx_ram[(fd+BLK_STAT)/4] = psx_ram[(chunk+BLK_STAT)/4];
psx_ram[(fd+BLK_SIZE)/4] = LE32(LE32(psx_ram[(chunk+BLK_SIZE)/4]) - a0);
psx_ram[(fd+BLK_FD)/4] = psx_ram[(chunk+BLK_FD)/4];
psx_ram[(fd+BLK_BK)/4] = chunk;
psx_ram[(chunk+BLK_STAT)/4] = LE32(1);
psx_ram[(chunk+BLK_SIZE)/4] = LE32(a0);
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(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;
psx_ram[(heap_addr+BLK_STAT)/4] = LE32(0);
psx_ram[(heap_addr+BLK_FD)/4] = LE32(0);
psx_ram[(heap_addr+BLK_BK)/4] = LE32(0);
// if heap size out of range, clamp it
if (((a0 & 0x1fffff) + a1) >= 2*1024*1024)
{
psx_ram[(heap_addr+BLK_SIZE)/4] = LE32(0x1ffffc - (a0 & 0x1fffff));
}
else
{
psx_ram[(heap_addr+BLK_SIZE)/4] = LE32(a1);
}
break;
case 0x3f: // printf
#if DEBUG_HLE_BIOS
printf("HLEBIOS: printf(%08x) = %s\n", a0, &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(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
}
break;
case 0x0a: // WaitEvent
{
int ev, spec;
ev = a0 & 0xff;
spec = (a0 >> 8) & 0xff;
mips_get_info(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(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
WAI = 1;
mips_shorten_frame();
}
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(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(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(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(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
}
break;
case 0x17: // ReturnFromException
for (i = 0; i < 32; i++)
{
mipsinfo.i = irq_regs[i];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo);
}
mipsinfo.i = irq_regs[32];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
mipsinfo.i = irq_regs[33];
mips_set_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
mipsinfo.i = mips_get_ePC();
// 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(CPUINFO_INT_PC, &mipsinfo);
status = mips_get_status();
status = (status & 0xfffffff0) | ((status & 0x3c)>>2);
mips_set_status(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(psx_ram[((a0<<2) + 0x8600)/4]);
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
// (a0*4)+0x8600 = a1;
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(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
mips_set_info(CPUINFO_INT_PC, &mipsinfo);
}
// root counters
void psx_hw_runcounters(void)
{
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();
if (dma4_cb)
{
call_irq_routine(dma4_cb, dma4_flag);
}
}
}
if (dma7_delay)
{
dma7_delay--;
if (dma7_delay == 0)
{
SPU2interruptDMA7();
if (dma7_cb)
{
call_irq_routine(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();
}
}
}
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(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(1<<(4+i));
}
}
}
}
// PEOpS callbacks
void SPUirq(void)
{
// psx_irq_set(0x200);
}
// PSXCPU callbacks
uint8 program_read_byte_32le(offs_t address)
{
switch (address & 0x3)
{
case 0:
return psx_hw_read(address, 0xffffff00);
break;
case 1:
return psx_hw_read(address, 0xffff00ff)>>8;
break;
case 2:
return psx_hw_read(address, 0xff00ffff)>>16;
break;
case 3:
return psx_hw_read(address, 0x00ffffff)>>24;
break;
}
}
uint16 program_read_word_32le(offs_t address)
{
if (address & 2)
return psx_hw_read(address, 0x0000ffff)>>16;
return psx_hw_read(address, 0xffff0000);
}
uint32 program_read_dword_32le(offs_t address)
{
return psx_hw_read(address, 0);
}
void program_write_byte_32le(offs_t address, uint8 data)
{
switch (address & 0x3)
{
case 0:
psx_hw_write(address, data, 0xffffff00);
break;
case 1:
psx_hw_write(address, data<<8, 0xffff00ff);
break;
case 2:
psx_hw_write(address, data<<16, 0xff00ffff);
break;
case 3:
psx_hw_write(address, data<<24, 0x00ffffff);
break;
}
}
void program_write_word_32le(offs_t address, uint16 data)
{
if (address & 2)
{
psx_hw_write(address, data<<16, 0x0000ffff);
return;
}
psx_hw_write(address, data, 0xffff0000);
}
void program_write_dword_32le(offs_t address, uint32 data)
{
psx_hw_write(address, data, 0);
}
// sprintf replacement
static iop_sprintf(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(curparm, &mipsinfo);
// printf("parameter %d = %x\n", curparm-pstart, mipsinfo.i);
curparm++;
sprintf(temp, tfmt, (int32)mipsinfo.i);
}
else
{
mips_get_info(curparm, &mipsinfo);
curparm++;
pstr = (char *)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(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(CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo);
a0 = mipsinfo.i;
mips_get_info(CPUINFO_INT_REGISTER + MIPS_R5, &mipsinfo);
a1 = mipsinfo.i;
mips_get_info(CPUINFO_INT_REGISTER + MIPS_R6, &mipsinfo);
a2 = mipsinfo.i;
mips_get_info(CPUINFO_INT_REGISTER + MIPS_R7, &mipsinfo);
a3 = mipsinfo.i;
scan = (pc&0x0fffffff)/4;
while ((psx_ram[scan] != LE32(0x41e00000)) && (scan >= (0x10000/4)))
{
scan--;
}
if (psx_ram[scan] != LE32(0x41e00000))
{
printf("FATAL ERROR: couldn't find IOP link signature\n");
return;
}
scan += 3; // skip zero and version
memcpy(name, &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 *)psx_ram;
mname += a0 & 0x1fffff;
mname += (a0 & 3);
iop_sprintf(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(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(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(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(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",
psx_ram[a0], psx_ram[a0+1], psx_ram[a0+2], psx_ram[a0+3], 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(psx_ram[a0+3]));
threads[iNumThreads].iState = TS_CREATED;
threads[iNumThreads].stackloc = newAlloc;
threads[iNumThreads].flags = LE32(psx_ram[a0]);
threads[iNumThreads].routine = LE32(psx_ram[a0+2]);
threads[iNumThreads].stacksize = LE32(psx_ram[a0+3]);
threads[iNumThreads].refCon = LE32(psx_ram[a0+4]);
mipsinfo.i = iNumThreads;
iNumThreads++;
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
break;
case 6: // StartThread
#if DEBUG_THREADING
printf("IOP: StartThread(%d %d)\n", a0, a1);
#endif
FreezeThread(iCurThread, 1);
ThawThread(a0);
iCurThread = a0;
break;
case 20:// GetThreadID
#if DEBUG_THREADING
printf("IOP: GetThreadId()\n");
#endif
mipsinfo.i = iCurThread;
mips_set_info(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(iCurThread, 1);
threads[iCurThread].iState = TS_SLEEPING;
iCurThread = -1;
ps2_reschedule();
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(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();
}
break;
case 34://GetSystemTime
#if DEBUG_HLE_IOP
printf("IOP: GetSystemTime(%x)\n", a0);
#endif
a0 &= 0x1fffff;
a0 /= 4;
psx_ram[a0] = LE32(sys_time & 0xffffffff); // low
psx_ram[a0+1] = LE32(sys_time >> 32); // high
mipsinfo.i = 0;
mips_set_info(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;
psx_ram[((a1 & 0x1fffff)/4)] = LE32(lo);
psx_ram[((a1 & 0x1fffff)/4)+1] = LE32(hi);
mipsinfo.i = 0;
mips_set_info(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(psx_ram[a0]);
temp |= (uint64)LE32(psx_ram[a0+1])<<32;
temp *= (uint64)1000000;
temp /= (uint64)36864000;
// temp now is USec
seconds = (temp / 1000000) & 0xffffffff;
usec = (temp % 1000000) & 0xffffffff;
psx_ram[a1] = LE32(seconds);
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(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(psx_ram[a0]);
evflags[iNumFlags].value = LE32(psx_ram[a0+1]);
evflags[iNumFlags].param = LE32(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(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(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(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(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(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(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(iCurThread, 1);
threads[iCurThread].iState = TS_WAITEVFLAG;
threads[iCurThread].waitparm = a0;
iCurThread = -1;
ps2_reschedule();
}
else
{
mipsinfo.i = 0;
mips_set_info(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, psx_ram[a0], psx_ram[a0+1], psx_ram[a0+2], psx_ram[a0+3]);
if (mipsinfo.i != -1)
{
semaphores[mipsinfo.i].attr = LE32(psx_ram[a0]);
semaphores[mipsinfo.i].option = LE32(psx_ram[a0+1]);
semaphores[mipsinfo.i].init = LE32(psx_ram[a0+2]);
semaphores[mipsinfo.i].max = LE32(psx_ram[a0+3]);
semaphores[mipsinfo.i].current = semaphores[mipsinfo.i].init;
semaphores[mipsinfo.i].inuse = 1;
}
mips_set_info(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(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(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(iCurThread, 1);
threads[iCurThread].iState = TS_WAITSEMA;
threads[iCurThread].waitparm = a0;
ps2_reschedule();
}
mipsinfo.i = 0;
mips_set_info(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(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(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
break;
case 10:// GetTimerCounter
mipsinfo.i = iop_timers[a0-1].count;
mips_set_info(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(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(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(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(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 *)&psx_ram[(a0&0x1fffff)/4];
src = (uint8 *)&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(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 *)&psx_ram[(a0&0x1fffff)/4];
src = (uint8 *)&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(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 *)&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 *)&psx_ram[(a0&0x1fffff)/4];
dst += (a0 & 3);
memset(dst, 0, a1);
}
break;
case 19: // sprintf
mname = (char *)psx_ram;
str1 = (char *)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(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 *)&psx_ram[(a0&0x1fffff)/4];
src = (uint8 *)&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(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 *)&psx_ram[(a0&0x1fffff)/4];
dst += (a0 & 3);
mipsinfo.i = strlen(dst);
mips_set_info(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 *)&psx_ram[(a0&0x1fffff)/4];
src = (char *)&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(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
}
break;
case 36: // strtol
mname = (char *)&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(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(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(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(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 (psx_ram[a0/4] == LE32(0x41c00000))
{
a0 += 3*4;
memcpy(&reglibs[iNumLibs].name, &psx_ram[a0/4], 8);
reglibs[iNumLibs].name[8] = '\0';
#if DEBUG_HLE_IOP
printf("Lib name [%s]\n", &reglibs[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(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(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(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(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
break;
case 14: // Kprintf
mname = (char *)psx_ram;
mname += a0 & 0x1fffff;
mname += (a0 & 3);
iop_sprintf(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(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 *)&psx_ram[(a0 & 0x1fffff)/4];
mname += 8;
str1 = (char *)&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(mname, tempmem, 2*1024*1024) != 0xffffffff)
{
uint32 start;
int i;
start = psf2_load_elf(tempmem, 2*1024*1024);
if (start != 0xffffffff)
{
uint32 args[20], numargs = 1, argofs;
uint8 *argwalk = (uint8 *)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
psx_ram[(newAlloc/4)+i] = LE32(args[i]);
}
// set argv and argc
mipsinfo.i = numargs;
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo);
mipsinfo.i = 0x80000000 | newAlloc;
mips_set_info(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(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(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
return;
}
mname = (char *)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(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(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(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
break;
case 5: // close
#if DEBUG_HLE_IOP
mips_get_info(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 *)psx_ram;
rp += (a1 & 0x1fffff);
memcpy(rp, &filedata[a0][filepos[a0]], a2);
filepos[a0] += a2;
mipsinfo.i = a2;
}
mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
break;
case 8: // lseek
#if DEBUG_HLE_IOP
mips_get_info(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(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(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(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(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
PC = mipsinfo.i;
#endif
// zap the delay slot handling
mipsinfo.i = 0;
mips_set_info(CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo);
mips_set_info(CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo);
mipsinfo.i = LE32(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(CPUINFO_INT_PC, &mipsinfo);
return;
}
}
}
printf("IOP: Unhandled service %d for module %s\n", callnum, name);
}
}