621 lines
12 KiB
C
621 lines
12 KiB
C
/*
|
|
* PSXSDK Library
|
|
*
|
|
* Free and open source library to develop for the Sony PlayStation
|
|
*/
|
|
|
|
#include <psx.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "exception.h"
|
|
|
|
static const char *sysromver_unavail = "System ROM Version Unavailable";
|
|
|
|
void (*vblank_handler_callback)();
|
|
|
|
extern int *vblank_handler();
|
|
|
|
void (*rcnt_handler_callback)();
|
|
|
|
extern int *rcnt_handler();
|
|
|
|
/*static unsigned int vblank_queue_buf[4] = {0x0, // Will contain next interrupt handler in queue
|
|
0x0, // func1
|
|
(unsigned int)vblank_handler, // func2
|
|
0x0, // pad
|
|
};*/
|
|
|
|
static int vblank_handler_set = 0;
|
|
static unsigned int vblank_handler_event_id = 0;
|
|
|
|
static int rcnt_handler_set = 0;
|
|
static unsigned int rcnt_handler_event_id = 0;
|
|
unsigned int rcnt_handler_evfield;
|
|
|
|
void _internal_cdromlib_init(void);
|
|
|
|
static unsigned int psxSdkFlags = 0;
|
|
|
|
static unsigned char *psxBiosState;
|
|
|
|
extern void _96_remove(void);
|
|
extern void _96_init(void);
|
|
extern void InitCARD(void);
|
|
extern void StartCARD(void);
|
|
extern void StopCARD(void);
|
|
extern void _bu_init(void);
|
|
extern void BIOSWarmReboot(void);
|
|
|
|
void PSX_InitEx(unsigned int flags)
|
|
{
|
|
if(flags & PSX_INIT_NOBIOS)
|
|
{
|
|
printf("Entering No BIOS mode...\n");
|
|
|
|
__PSX_Init_NoBios();
|
|
goto _initex_end;
|
|
}
|
|
|
|
if(flags & PSX_INIT_SAVESTATE)
|
|
{
|
|
// Save BIOS state
|
|
// This simply copies the entire section of RAM used by the BIOS
|
|
// in a buffer.
|
|
EnterCriticalSection();
|
|
psxBiosState = malloc(0x10000);
|
|
memcpy(psxBiosState, (void*)0x80000000, 0x10000);
|
|
ExitCriticalSection();
|
|
}
|
|
|
|
/* Reinitialize ISO 9660 filesystem driver */
|
|
|
|
if(flags & PSX_INIT_CD)
|
|
{
|
|
EnterCriticalSection();
|
|
_96_remove();
|
|
ExitCriticalSection();
|
|
|
|
_96_init();
|
|
}
|
|
|
|
|
|
/*This is needed, otherwise PSX will crash when VBlank handler is set*/
|
|
/*InitCARD(1);
|
|
StartCARD();
|
|
StopCARD();*/
|
|
|
|
if(flags & PSX_INIT_CD)
|
|
_internal_cdromlib_init();
|
|
|
|
printf("PSXSDK testing version !!!\n");
|
|
|
|
vblank_handler_set = 0;
|
|
_initex_end:
|
|
psxSdkFlags = flags;
|
|
}
|
|
|
|
void PSX_Init(void)
|
|
{
|
|
PSX_InitEx(PSX_INIT_CD);
|
|
}
|
|
|
|
void PSX_DeInit(void)
|
|
{
|
|
if(psxSdkFlags & PSX_INIT_CD)
|
|
{
|
|
EnterCriticalSection();
|
|
_96_remove();
|
|
ExitCriticalSection();
|
|
}
|
|
|
|
RemoveVBlankHandler();
|
|
|
|
if(psxSdkFlags & PSX_INIT_SAVESTATE)// This must always be the last to be called!
|
|
PSX_RestoreBiosState();
|
|
}
|
|
|
|
void PSX_ReadPad(unsigned short *padbuf, unsigned short *padbuf2)
|
|
{
|
|
int x;
|
|
unsigned char arr[PAD_READ_RAW_SIZE];
|
|
unsigned short *padbuf_a[2];
|
|
|
|
// Now uses low level pad routines...
|
|
padbuf_a[0] = padbuf;
|
|
padbuf_a[1] = padbuf2;
|
|
|
|
for(x = 0; x < 2; x++)
|
|
{
|
|
pad_read_raw(x, arr);
|
|
|
|
if(arr[2] == 0x5a)
|
|
{
|
|
*padbuf_a[x] = (arr[3]<<8)|arr[4];
|
|
*padbuf_a[x] = ~*padbuf_a[x];
|
|
}
|
|
else
|
|
*padbuf_a[x] = 0;
|
|
}
|
|
}
|
|
|
|
unsigned char psxsdkPadArr[PAD_READ_RAW_SIZE][2];
|
|
|
|
void PSX_ReadMouse(unsigned short* dig_pad1, unsigned short* adc_pad1)
|
|
{
|
|
unsigned char* arr = psxsdkPadArr[0];
|
|
|
|
const unsigned char pad_cmd[PAD_READ_RAW_SIZE] = {1,0x42,0,0,0,0,0}; // 2 extra bytes than digital pad
|
|
|
|
QueryPAD(0, pad_cmd, arr, sizeof(pad_cmd));
|
|
|
|
if(arr[2] == 0x5A)
|
|
{
|
|
*dig_pad1 = (arr[3]<<8)|arr[4];
|
|
*dig_pad1 = ~*dig_pad1;
|
|
*adc_pad1 = (arr[5]<<8)|arr[6];
|
|
}
|
|
else
|
|
{
|
|
*dig_pad1 = 0;
|
|
*adc_pad1 = 0;
|
|
}
|
|
}
|
|
|
|
void PSX_PollPad_Fast_Ex(const unsigned char* const arr, psx_pad_state* const pad_state)
|
|
{
|
|
//Rely on pad_read_raw being called AFTER PSX_ReadPad(),
|
|
//so that pad_read_raw is only called once.
|
|
pad_state->status = arr[0];
|
|
pad_state->id = arr[1];
|
|
|
|
pad_state->buttons = (arr[3]<<8)|arr[4];
|
|
pad_state->buttons = ~pad_state->buttons;
|
|
|
|
//dprintf("Pad Status: 0x%.2X\n",pad_state->status);
|
|
|
|
switch(pad_state->id)
|
|
{
|
|
case 0xFF:
|
|
pad_state->type = PADTYPE_NONE;
|
|
break;
|
|
case 0x41:
|
|
pad_state->type = PADTYPE_NORMALPAD;
|
|
break;
|
|
case 0x53:
|
|
pad_state->type = PADTYPE_ANALOGJOY;
|
|
pad_state->extra.analogJoy.x[0] = arr[5]-128;
|
|
pad_state->extra.analogJoy.y[0] = arr[6]-128;
|
|
pad_state->extra.analogJoy.x[1] = arr[7]-128;
|
|
pad_state->extra.analogJoy.y[1] = arr[8]-128;
|
|
break;
|
|
case 0x73:
|
|
pad_state->type = PADTYPE_ANALOGPAD;
|
|
pad_state->extra.analogPad.x[0] = arr[5]-128;
|
|
pad_state->extra.analogPad.y[0] = arr[6]-128;
|
|
pad_state->extra.analogPad.x[1] = arr[7]-128;
|
|
pad_state->extra.analogPad.y[1] = arr[8]-128;
|
|
break;
|
|
case 0x23:
|
|
pad_state->type = PADTYPE_NEGCON;
|
|
pad_state->extra.negCon.steering = arr[5]-128;
|
|
pad_state->extra.negCon.one = arr[6];
|
|
pad_state->extra.negCon.two = arr[7];
|
|
pad_state->extra.negCon.shoulder = arr[8];
|
|
break;
|
|
case 0x31:
|
|
pad_state->type = PADTYPE_KONAMIGUN;
|
|
break;
|
|
case 0x12:
|
|
pad_state->type = PADTYPE_MOUSE;
|
|
break;
|
|
default:
|
|
pad_state->type = PADTYPE_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
void PSX_PollPad_Fast(int pad_num, psx_pad_state *pad_state)
|
|
{
|
|
PSX_PollPad_Fast_Ex(psxsdkPadArr[pad_num], pad_state);
|
|
}
|
|
|
|
void PSX_PollPad(int pad_num, psx_pad_state *pad_state)
|
|
{
|
|
// int x;
|
|
/*unsigned short *padbuf_a[2];
|
|
|
|
// Now uses low level pad routines...
|
|
padbuf_a[0] = padbuf;
|
|
padbuf_a[1] = padbuf2;
|
|
|
|
for(x = 0; x < 2; x++)
|
|
{
|
|
pad_read_raw(x, arr);
|
|
|
|
if(arr[2] == 0x5a)
|
|
{
|
|
*padbuf_a[x] = (arr[3]<<8)|arr[4];
|
|
*padbuf_a[x] = ~*padbuf_a[x];
|
|
}
|
|
else
|
|
*padbuf_a[x] = 0;
|
|
}*/
|
|
|
|
unsigned char *arr = psxsdkPadArr[pad_num];
|
|
|
|
pad_read_raw(pad_num, arr);
|
|
|
|
pad_state->status = arr[0];
|
|
pad_state->id = arr[1];
|
|
|
|
pad_state->buttons = (arr[3]<<8)|arr[4];
|
|
pad_state->buttons = ~pad_state->buttons;
|
|
|
|
switch(pad_state->id)
|
|
{
|
|
case 0xFF:
|
|
pad_state->type = PADTYPE_NONE;
|
|
break;
|
|
case 0x41:
|
|
pad_state->type = PADTYPE_NORMALPAD;
|
|
break;
|
|
case 0x53:
|
|
pad_state->type = PADTYPE_ANALOGJOY;
|
|
pad_state->extra.analogJoy.x[0] = arr[5]-128;
|
|
pad_state->extra.analogJoy.y[0] = arr[6]-128;
|
|
pad_state->extra.analogJoy.x[1] = arr[7]-128;
|
|
pad_state->extra.analogJoy.y[1] = arr[8]-128;
|
|
break;
|
|
case 0x73:
|
|
pad_state->type = PADTYPE_ANALOGPAD;
|
|
pad_state->extra.analogPad.x[0] = arr[5]-128;
|
|
pad_state->extra.analogPad.y[0] = arr[6]-128;
|
|
pad_state->extra.analogPad.x[1] = arr[7]-128;
|
|
pad_state->extra.analogPad.y[1] = arr[8]-128;
|
|
break;
|
|
case 0x23:
|
|
pad_state->type = PADTYPE_NEGCON;
|
|
pad_state->extra.negCon.steering = arr[5]-128;
|
|
pad_state->extra.negCon.one = arr[6];
|
|
pad_state->extra.negCon.two = arr[7];
|
|
pad_state->extra.negCon.shoulder = arr[8];
|
|
break;
|
|
case 0x31:
|
|
pad_state->type = PADTYPE_KONAMIGUN;
|
|
break;
|
|
default:
|
|
pad_state->type = PADTYPE_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
/*int PSX_GetPadType(unsigned int pad_num)
|
|
//{
|
|
//#warning "Function does not currently work!"
|
|
// unsigned char arr[16];
|
|
|
|
pad_read_raw(pad_num, arr);
|
|
|
|
switch(arr[1])
|
|
{
|
|
case 0xFF:
|
|
return PADTYPE_NONE;
|
|
break;
|
|
case 0x41:
|
|
return PADTYPE_NORMALPAD;
|
|
break;
|
|
case 0x53:
|
|
return PADTYPE_ANALOGJOY;
|
|
break;
|
|
case 0x73:
|
|
return PADTYPE_ANALOGPAD;
|
|
break;
|
|
}
|
|
|
|
return PADTYPE_UNKNOWN;
|
|
}*/
|
|
|
|
|
|
void PSX_GetSysInfo(struct psx_info *info)
|
|
{
|
|
unsigned long i,i2;
|
|
|
|
info->kernel.version = GetKernelRomVersion();
|
|
|
|
i = GetKernelDate();
|
|
|
|
/*
|
|
* Convert year from BCD to decimal
|
|
*/
|
|
|
|
i2 = i >> 16;
|
|
|
|
info->kernel.year = i2 & 0xf;
|
|
info->kernel.year+= ((i2>>4)&0xf)*10;
|
|
info->kernel.year+= ((i2>>8)&0xf)*100;
|
|
info->kernel.year+= ((i2>>12)&0xf)*1000;
|
|
|
|
/*
|
|
* Convert month from BCD to decimal
|
|
*/
|
|
i2 = (i >> 8) & 0xff;
|
|
|
|
info->kernel.month = i2 & 0xf;
|
|
info->kernel.month+= (i2>>4) * 10;
|
|
|
|
/*
|
|
* Convert day from BCD to decimal
|
|
*/
|
|
i2 = i & 0xff;
|
|
|
|
info->kernel.day = i2 & 0xf;
|
|
info->kernel.day+= (i2>>4) * 10;
|
|
|
|
/*
|
|
* Unless we receive something in the range >= 1 && <= 16,
|
|
* RAM size will be reported as 2 Megabytes
|
|
*/
|
|
|
|
i = GetRamSize();
|
|
|
|
if(i == 0 || i > 16)
|
|
info->system.memory = 2<<20; /* 2 Megabytes */
|
|
else
|
|
info->system.memory <<= 20;
|
|
}
|
|
|
|
|
|
|
|
int get_real_file_size(const char *name)
|
|
{
|
|
struct DIRENTRY dirent_buf;
|
|
|
|
if(firstfile(name, &dirent_buf) == &dirent_buf)
|
|
return dirent_buf.size;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int get_file_size(const char *name)
|
|
{
|
|
int i = get_real_file_size(name);
|
|
|
|
if(strncmp(name, "cdrom:", 6) == 0)
|
|
{
|
|
if(i & 0x7ff)
|
|
{
|
|
i += 0x800;
|
|
i &= ~0x7ff;
|
|
}
|
|
}else if(strncmp(name, "bu", 2) == 0)
|
|
{
|
|
if(i & 0x7f)
|
|
{
|
|
i += 0x80;
|
|
i &= ~0x7f;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
int SetRCnt(int spec, unsigned short target, unsigned int mode)
|
|
{
|
|
spec &= 0xf;
|
|
|
|
if(spec >= 3)
|
|
return 0;
|
|
|
|
RCNT_MODE(spec)=0;
|
|
RCNT_TARGET(spec)=target;
|
|
RCNT_MODE(spec)=mode;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int GetRCnt(int spec)
|
|
{
|
|
spec &= 0xf;
|
|
|
|
if(spec >= 4)
|
|
return -1;
|
|
|
|
return (RCNT_COUNT(spec) & 0xffff);
|
|
}
|
|
|
|
int StartRCnt(int spec)
|
|
{
|
|
spec &= 0xf;
|
|
|
|
if(spec >= 3)
|
|
return 0;
|
|
|
|
IMASK |= 1 << (spec + 4);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int StopRCnt(int spec)
|
|
{
|
|
spec &= 0xf;
|
|
|
|
if(spec >= 3)
|
|
return 0;
|
|
|
|
IMASK ^= 1 << (spec + 4);
|
|
|
|
return 1;
|
|
}
|
|
|
|
void SetVBlankHandler(void (*callback)())
|
|
{
|
|
if(psxSdkFlags & PSX_INIT_NOBIOS)
|
|
{
|
|
_EXC_vblank_handler_set = 0;
|
|
_EXC_vblank_handler = callback;
|
|
_EXC_vblank_handler_set = 1;
|
|
return;
|
|
}
|
|
|
|
if(vblank_handler_set == 1)
|
|
{
|
|
EnterCriticalSection();
|
|
|
|
vblank_handler_callback = callback;
|
|
|
|
ExitCriticalSection();
|
|
|
|
return;
|
|
}
|
|
|
|
// Enter critical section
|
|
|
|
EnterCriticalSection();
|
|
|
|
IMASK|=1;
|
|
|
|
vblank_handler_event_id = OpenEvent(RCntCNT3, 2, 0x1000, vblank_handler);
|
|
EnableEvent(vblank_handler_event_id);
|
|
|
|
vblank_handler_callback = callback;
|
|
vblank_handler_set = 1;
|
|
|
|
// Exit critical section
|
|
|
|
ExitCriticalSection();
|
|
}
|
|
|
|
void RemoveVBlankHandler(void)
|
|
{
|
|
if(psxSdkFlags & PSX_INIT_NOBIOS)
|
|
{
|
|
_EXC_vblank_handler_set = 0;
|
|
_EXC_vblank_handler = NULL;
|
|
return;
|
|
}
|
|
|
|
if(vblank_handler_set)
|
|
{
|
|
EnterCriticalSection();
|
|
|
|
DisableEvent(vblank_handler_event_id);
|
|
CloseEvent(vblank_handler_event_id);
|
|
|
|
//IMASK^=1;
|
|
// ^ commented because masking out vblank could give problems to other bios functions
|
|
|
|
vblank_handler_set = 0;
|
|
|
|
ExitCriticalSection();
|
|
}
|
|
}
|
|
|
|
void SetRCntHandler(void (*callback)(), int spec, unsigned short target)
|
|
{
|
|
if(psxSdkFlags & PSX_INIT_NOBIOS)
|
|
return; // Not yet supported in No-Bios Mode
|
|
|
|
if(rcnt_handler_set)
|
|
{
|
|
EnterCriticalSection();
|
|
|
|
rcnt_handler_callback = callback;
|
|
|
|
ExitCriticalSection();
|
|
|
|
return;
|
|
}
|
|
|
|
// Enter critical section
|
|
|
|
SetRCnt(spec, target, RCntIntr | 0x08 | 0x10 | 0x40);
|
|
StartRCnt(spec);
|
|
|
|
EnterCriticalSection();
|
|
rcnt_handler_event_id = OpenEvent(spec, 2, 0x1000, rcnt_handler);
|
|
EnableEvent(rcnt_handler_event_id);
|
|
|
|
rcnt_handler_callback = callback;
|
|
rcnt_handler_set = spec;
|
|
|
|
switch(spec)
|
|
{
|
|
case RCntCNT0: rcnt_handler_evfield = 1 << 4; break;
|
|
case RCntCNT1: rcnt_handler_evfield = 1 << 5; break;
|
|
case RCntCNT2: rcnt_handler_evfield = 1 << 6; break;
|
|
case RCntCNT3: rcnt_handler_evfield = 1; break;
|
|
}
|
|
|
|
// Exit critical section
|
|
|
|
ExitCriticalSection();
|
|
}
|
|
|
|
void RemoveRCntHandler(int spec)
|
|
{
|
|
if(psxSdkFlags & PSX_INIT_NOBIOS)
|
|
return; // Not yet supported in No-Bios Mode
|
|
|
|
if(rcnt_handler_set)
|
|
{
|
|
EnterCriticalSection();
|
|
|
|
DisableEvent(rcnt_handler_event_id);
|
|
CloseEvent(rcnt_handler_event_id);
|
|
|
|
rcnt_handler_set = 0;
|
|
|
|
ExitCriticalSection();
|
|
}
|
|
}
|
|
|
|
const char *GetSystemRomVersion(void)
|
|
{
|
|
// Get pointer to zero-terminated string containing System ROM Version which is embedded in
|
|
// most PlayStation BIOSes.
|
|
|
|
// If getting the pointer is not possible, a pointer to a string saying "System ROM Unavailable" is returned.
|
|
|
|
int x;
|
|
|
|
for(x = 0x7ffee; x >= 0; x--)
|
|
if(memcmp("System ROM Version", (void*)(0xbfc00000 + x), 18) == 0)
|
|
return (char*)(0xbfc00000 + x);
|
|
|
|
return sysromver_unavail;
|
|
}
|
|
|
|
int PSX_RestoreBiosState(void)
|
|
{
|
|
if(!(psxSdkFlags & PSX_INIT_SAVESTATE))
|
|
return 0; // can't restore BIOS state if it was not saved previously
|
|
|
|
EnterCriticalSection();
|
|
memcpy((void*)0x80000000, psxBiosState, 0x10000);
|
|
ExitCriticalSection();
|
|
|
|
return 1;
|
|
}
|
|
|
|
unsigned int PSX_GetInitFlags(void)
|
|
{
|
|
return psxSdkFlags;
|
|
}
|
|
|
|
void PSX_WarmReboot(void)
|
|
{
|
|
if(psxSdkFlags & PSX_INIT_NOBIOS)
|
|
{
|
|
psx_warmreboot_nobios:
|
|
PSX_DeInit();
|
|
__asm__("j _start");
|
|
__asm__("nop");
|
|
}
|
|
else
|
|
{
|
|
if(!(psxSdkFlags & PSX_INIT_CD))
|
|
goto psx_warmreboot_nobios;
|
|
|
|
BIOSWarmReboot();
|
|
}
|
|
}
|