///////////////////////////////////////////////////////////////////////////// // // Upload PS-X EXE - must INCLUDE the header. // If the header includes the strings "North America", "Japan", or "Europe", // the appropriate refresh rate is set. // Returns nonzero on error. // Will return error for PS2. // sint32 EMU_CALL psx_upload_psxexe(void *state, void *program, uint32 size) { uint32 init_pc; uint32 init_sp; uint32 text_start; uint32 text_size; if(PSXSTATE->version != 1) return -1; if(size < 0x801) return -1; if(memcmp(program, "PS-X EXE", 8)) return -1; text_start = get32lsb(((uint8*)program) + 0x18); text_size = get32lsb(((uint8*)program) + 0x1C); init_pc = get32lsb(((uint8*)program) + 0x10); init_sp = get32lsb(((uint8*)program) + 0x30); // Try to determine the region, or leave it at the default if it's not found if(string_exists(program, 0x800, "North America")) { psx_set_refresh(state, 60); } else if(string_exists(program, 0x800, "Japan" )) { psx_set_refresh(state, 60); } else if(string_exists(program, 0x800, "Europe" )) { psx_set_refresh(state, 50); } if(text_size > (size - 0x800)) { text_size = (size - 0x800); } iop_upload_to_ram(IOPSTATE, text_start, ((uint8*)program) + 0x800, text_size); r3000_setreg(iop_get_r3000_state(IOPSTATE), R3000_REG_PC , init_pc); r3000_setreg(iop_get_r3000_state(IOPSTATE), R3000_REG_GEN+29, init_sp); return 0; }
void EMU_CALL psx_clear_state(void *state, uint8 version) { uint32 offset; // // If we haven't initialized, we really SHOULD have. // if(!library_was_initialized) psx_hang("library not initialized"); if(version != 2) version = 1; // Clear local struct memset(state, 0, sizeof(struct PSX_STATE)); // Set local version PSXSTATE->version = version; // Set up offsets offset = sizeof(struct PSX_STATE); if(version == 2) { PSXSTATE->offset_to_vfs = offset; offset += vfs_get_state_size(); } PSXSTATE->offset_to_iop = offset; offset += iop_get_state_size(version); // Take care of VFS state if(HAVE_VFS) vfs_clear_state(VFSSTATE); // Take care of IOP state if(HAVE_IOP) iop_clear_state(IOPSTATE, version); // // Do some final inits // switch(version) { case 1: // // preboot for PS1 // preboot(PSXSTATE, 0xBFC00000 | (ps1preboot & 0x003FFFFF)); r3000_setreg(iop_get_r3000_state(IOPSTATE), R3000_REG_PC , 0x80010000); r3000_setreg(iop_get_r3000_state(IOPSTATE), R3000_REG_GEN+29, 0x801FFFF0); break; case 2: // // preboot for PS2 // preboot(PSXSTATE, 0xBFC00000 | (ps2preboot & 0x003FFFFF)); // // simulate jr $v0 to transfer execution to loadcore // r3000_setreg(iop_get_r3000_state(IOPSTATE), R3000_REG_PC, r3000_getreg(iop_get_r3000_state(IOPSTATE), R3000_REG_GEN+2) ); break; } // Done }
int psf1_load(void * context, const uint8_t * exe, size_t exe_size, const uint8_t * reserved, size_t reserved_size) { struct psf_load_state * state = ( struct psf_load_state * ) context; psxexe_hdr_t *psx = (psxexe_hdr_t *) exe; if ( exe_size < 0x800 ) return -1; uint32_t addr = get_le32( &psx->exec.t_addr ); uint32_t size = exe_size - 0x800; addr &= 0x1fffff; if ( ( addr < 0x10000 ) || ( size > 0x1f0000 ) || ( addr + size > 0x200000 ) ) return -1; void * pIOP = psx_get_iop_state( state->emu ); iop_upload_to_ram( pIOP, addr, exe + 0x800, size ); if ( !state->refresh ) { if (!strncasecmp((const char *) exe + 113, "Japan", 5)) state->refresh = 60; else if (!strncasecmp((const char *) exe + 113, "Europe", 6)) state->refresh = 50; else if (!strncasecmp((const char *) exe + 113, "North America", 13)) state->refresh = 60; } if ( state->first ) { void * pR3000 = iop_get_r3000_state( pIOP ); r3000_setreg(pR3000, R3000_REG_PC, get_le32( &psx->exec.pc0 ) ); r3000_setreg(pR3000, R3000_REG_GEN+29, get_le32( &psx->exec.s_ptr ) ); state->first = 0; } return 0; }
static EMU_INLINE void EMU_CALL preboot(struct PSX_STATE *state, uint32 target_address) { sint32 r; for(;;) { uint32 s = 10000; r = iop_execute(IOPSTATE, state, 10000, NULL, &s, 0); if(r < 0) break; if(r3000_getreg(iop_get_r3000_state(IOPSTATE), R3000_REG_PC) == target_address) break; } }