/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.HLE;
import org.apache.log4j.Logger;
import jpcsp.Emulator;
import jpcsp.Memory;
import jpcsp.NIDMapper;
import jpcsp.Allegrex.Common.Instruction;
import jpcsp.Allegrex.compiler.RuntimeContext;
import jpcsp.Allegrex.Common;
import jpcsp.Allegrex.CpuState;
import jpcsp.Allegrex.Decoder;
import jpcsp.HLE.kernel.Managers;
import jpcsp.HLE.kernel.types.SceModule;
import jpcsp.format.DeferredStub;
import jpcsp.settings.AbstractBoolSettingsListener;
import jpcsp.settings.Settings;
import jpcsp.util.Utilities;
public class SyscallHandler {
private static Logger log = Modules.log;
public static boolean ignoreUnmappedImports = false;
public static final int syscallUnmappedImport = 0xFFFFF;
private static IgnoreUnmappedImportsSettingsListerner ignoreUnmappedImportsSettingsListerner;
private static class IgnoreUnmappedImportsSettingsListerner extends AbstractBoolSettingsListener {
@Override
protected void settingsValueChanged(boolean value) {
setEnableIgnoreUnmappedImports(value);
}
}
private static boolean isEnableIgnoreUnmappedImports(){
return ignoreUnmappedImports;
}
private static void setEnableIgnoreUnmappedImports(boolean enable){
ignoreUnmappedImports = enable;
if (enable) {
log.info("Ignore Unmapped Imports enabled");
}
}
private static void logMem(Memory mem, int address, String registerName) {
if (Memory.isAddressGood(address)) {
log.error(String.format("Memory at %s:%s", registerName, Utilities.getMemoryDump(address, 64)));
}
}
private static void unsupportedSyscall(int code) throws Exception {
if (ignoreUnmappedImportsSettingsListerner == null) {
ignoreUnmappedImportsSettingsListerner = new IgnoreUnmappedImportsSettingsListerner();
Settings.getInstance().registerSettingsListener("SyscallHandler", "emu.ignoreUnmappedImports", ignoreUnmappedImportsSettingsListerner);
}
NIDMapper nidMapper = NIDMapper.getInstance();
if (code == syscallUnmappedImport) { // special code for unmapped imports
CpuState cpu = Emulator.getProcessor().cpu;
String description = String.format("0x%08X", cpu.pc);
// Search for the module & NID to provide a better description
for (SceModule module : Managers.modules.values()) {
for (DeferredStub deferredStub : module.unresolvedImports) {
if (deferredStub.getImportAddress() == cpu.pc || deferredStub.getImportAddress() == cpu.pc - 4) {
description = deferredStub.toString();
break;
}
}
}
if (isEnableIgnoreUnmappedImports()) {
log.warn(String.format("IGNORING: Unmapped import at %s - $a0=0x%08X $a1=0x%08X $a2=0x%08X", description, cpu._a0, cpu._a1, cpu._a2));
} else {
log.error(String.format("Unmapped import at %s:", description));
log.error(String.format("Registers: $a0=0x%08X, $a1=0x%08X, $a2=0x%08X, $a3=0x%08X", cpu._a0, cpu._a1, cpu._a2, cpu._a3));
log.error(String.format(" $t0=0x%08X, $t1=0x%08X, $t2=0x%08X, $t3=0x%08X", cpu._t0, cpu._t1, cpu._t2, cpu._t3));
log.error(String.format(" $ra=0x%08X, $sp=0x%08X", cpu._ra, cpu._sp));
Memory mem = Emulator.getMemory();
log.error(String.format("Caller code:"));
for (int i = -96; i <= 40; i += 4) {
int address = cpu._ra + i;
int opcode = mem.read32(address);
Instruction insn = Decoder.instruction(opcode);
String disasm = insn.disasm(address, opcode);
log.error(String.format("%c 0x%08X:[%08X]: %s", i == -8 ? '>' : ' ', address, opcode, disasm));
}
logMem(mem, cpu._a0, Common.gprNames[Common._a0]);
logMem(mem, cpu._a1, Common.gprNames[Common._a1]);
logMem(mem, cpu._a2, Common.gprNames[Common._a2]);
logMem(mem, cpu._a3, Common.gprNames[Common._a3]);
logMem(mem, cpu._t0, Common.gprNames[Common._t0]);
logMem(mem, cpu._t1, Common.gprNames[Common._t1]);
logMem(mem, cpu._t2, Common.gprNames[Common._t2]);
logMem(mem, cpu._t3, Common.gprNames[Common._t3]);
Emulator.PauseEmu();
}
cpu._v0 = 0;
} else {
int address = nidMapper.getAddressBySyscall(code);
if (address != 0) {
if (log.isDebugEnabled()) {
String name = nidMapper.getNameBySyscall(code);
int nid = nidMapper.getNidBySyscall(code);
if (name != null) {
log.debug(String.format("Jumping to 0x%08X instead of overwritten syscall %s[0x%08X]", address, name, nid));
} else {
log.debug(String.format("Jumping to 0x%08X instead of overwritten syscall NID 0x%08X", address, nid));
}
}
RuntimeContext.executeFunction(address);
} else {
// Check if this is the syscall
// for an HLE function currently being uninstalled
String name = nidMapper.getNameBySyscall(code);
if (name != null) {
log.error(String.format("HLE Function %s not activated by default for Firmware Version %d", name, Emulator.getInstance().getFirmwareVersion()));
} else {
int nid = nidMapper.getNidBySyscall(code);
log.error(String.format("NID 0x%08X not activated by default for Firmware Version %d", nid, Emulator.getInstance().getFirmwareVersion()));
}
}
}
}
public static void syscall(int code) throws Exception {
// All syscalls are now implemented natively in the compiler
unsupportedSyscall(code);
}
}