/* 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.kernel.managers; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Vector; import jpcsp.Emulator; import jpcsp.HLE.Modules; import jpcsp.HLE.kernel.types.IAction; import jpcsp.HLE.kernel.types.SceKernelErrors; import jpcsp.HLE.kernel.types.interrupts.AbstractAllegrexInterruptHandler; import jpcsp.HLE.kernel.types.interrupts.AbstractInterruptHandler; import jpcsp.HLE.kernel.types.interrupts.AfterSubIntrAction; import jpcsp.HLE.kernel.types.interrupts.InterruptState; import jpcsp.HLE.kernel.types.interrupts.IntrHandler; import jpcsp.HLE.kernel.types.interrupts.SubIntrHandler; import jpcsp.HLE.kernel.types.interrupts.VBlankInterruptHandler; import jpcsp.hardware.Interrupts; import jpcsp.scheduler.Scheduler; import org.apache.log4j.Logger; public class IntrManager { protected static Logger log = Modules.getLogger("ThreadManForUser"); public static final int PSP_GPIO_INTR = 4; public static final int PSP_ATA_INTR = 5; public static final int PSP_UMD_INTR = 6; public static final int PSP_MSCM0_INTR = 7; public static final int PSP_WLAN_INTR = 8; public static final int PSP_AUDIO_INTR = 10; public static final int PSP_I2C_INTR = 12; public static final int PSP_SIRS_INTR = 14; public static final int PSP_SYSTIMER0_INTR = 15; public static final int PSP_SYSTIMER1_INTR = 16; public static final int PSP_SYSTIMER2_INTR = 17; public static final int PSP_SYSTIMER3_INTR = 18; public static final int PSP_THREAD0_INTR = 19; public static final int PSP_NAND_INTR = 20; public static final int PSP_DMACPLUS_INTR = 21; public static final int PSP_DMA0_INTR = 22; public static final int PSP_DMA1_INTR = 23; public static final int PSP_MEMLMD_INTR = 24; public static final int PSP_GE_INTR = 25; public static final int PSP_VBLANK_INTR = 30; public static final int PSP_MECODEC_INTR = 31; public static final int PSP_HPREMOTE_INTR = 36; public static final int PSP_MSCM1_INTR = 60; public static final int PSP_MSCM2_INTR = 61; public static final int PSP_THREAD1_INTR = 65; public static final int PSP_INTERRUPT_INTR = 66; public static final int PSP_NUMBER_INTERRUPTS = 67; private static String[] PSP_INTERRUPT_NAMES; public static final int VBLANK_SCHEDULE_MICROS = (1000000 + 30) / 60; // 1/60 second (rounded) protected static IntrManager instance = null; private Vector<LinkedList<AbstractInterruptHandler>> interrupts; protected IntrHandler[] intrHandlers; protected boolean insideInterrupt; protected List<AbstractAllegrexInterruptHandler> allegrexInterruptHandlers; // Deferred interrupts are interrupts that were triggered by the scheduler // while the interrupts were disabled. // They have to be processed as soon as the interrupts are re-enabled. protected List<AbstractInterruptHandler> deferredInterrupts; private VBlankInterruptHandler vblankInterruptHandler; public static IntrManager getInstance() { if (instance == null) { instance = new IntrManager(); } return instance; } private IntrManager() { vblankInterruptHandler = new VBlankInterruptHandler(); } public void reset() { stop(); installDefaultInterrupts(); } public void stop() { interrupts = new Vector<LinkedList<AbstractInterruptHandler>>(PSP_NUMBER_INTERRUPTS); interrupts.setSize(PSP_NUMBER_INTERRUPTS); intrHandlers = new IntrHandler[IntrManager.PSP_NUMBER_INTERRUPTS]; allegrexInterruptHandlers = new LinkedList<AbstractAllegrexInterruptHandler>(); deferredInterrupts = new LinkedList<AbstractInterruptHandler>(); } public static String getInterruptName(int interruptNumber) { if (PSP_INTERRUPT_NAMES == null) { PSP_INTERRUPT_NAMES = new String[PSP_NUMBER_INTERRUPTS]; PSP_INTERRUPT_NAMES[PSP_GPIO_INTR] = "GPIO"; PSP_INTERRUPT_NAMES[PSP_ATA_INTR] = "ATA"; PSP_INTERRUPT_NAMES[PSP_UMD_INTR] = "UMD"; PSP_INTERRUPT_NAMES[PSP_MSCM0_INTR] = "MSCM0"; PSP_INTERRUPT_NAMES[PSP_WLAN_INTR] = "WLAN"; PSP_INTERRUPT_NAMES[PSP_AUDIO_INTR] = "AUDIO"; PSP_INTERRUPT_NAMES[PSP_I2C_INTR] = "I2C"; PSP_INTERRUPT_NAMES[PSP_SIRS_INTR] = "SIRS"; PSP_INTERRUPT_NAMES[PSP_SYSTIMER0_INTR] = "SYSTIMER0"; PSP_INTERRUPT_NAMES[PSP_SYSTIMER1_INTR] = "SYSTIMER1"; PSP_INTERRUPT_NAMES[PSP_SYSTIMER2_INTR] = "SYSTIMER2"; PSP_INTERRUPT_NAMES[PSP_SYSTIMER3_INTR] = "SYSTIMER3"; PSP_INTERRUPT_NAMES[PSP_THREAD0_INTR] = "THREAD0"; PSP_INTERRUPT_NAMES[PSP_NAND_INTR] = "NAND"; PSP_INTERRUPT_NAMES[PSP_DMACPLUS_INTR] = "DMACPLUS"; PSP_INTERRUPT_NAMES[PSP_DMA0_INTR] = "DMA0"; PSP_INTERRUPT_NAMES[PSP_DMA1_INTR] = "DMA1"; PSP_INTERRUPT_NAMES[PSP_MEMLMD_INTR] = "MEMLMD"; PSP_INTERRUPT_NAMES[PSP_GE_INTR] = "GE"; PSP_INTERRUPT_NAMES[PSP_VBLANK_INTR] = "VBLANK"; PSP_INTERRUPT_NAMES[PSP_MECODEC_INTR] = "MECODEC"; PSP_INTERRUPT_NAMES[PSP_HPREMOTE_INTR] = "HPREMOTE"; PSP_INTERRUPT_NAMES[PSP_MSCM1_INTR] = "MSCM1"; PSP_INTERRUPT_NAMES[PSP_MSCM2_INTR] = "MSCM2"; PSP_INTERRUPT_NAMES[PSP_THREAD1_INTR] = "THREAD1"; PSP_INTERRUPT_NAMES[PSP_INTERRUPT_INTR] = "INTERRUPT"; } String name = null; if (interruptNumber >= 0 && interruptNumber < PSP_INTERRUPT_NAMES.length) { name = PSP_INTERRUPT_NAMES[interruptNumber]; } if (name == null) { name = String.format("INTERRUPT_%X", interruptNumber); } return name; } private void installDefaultInterrupts() { Scheduler scheduler = Emulator.getScheduler(); // install VBLANK interrupt every 1/60 second scheduler.addAction(Scheduler.getNow() + VBLANK_SCHEDULE_MICROS, vblankInterruptHandler); } public void addDeferredInterrupt(AbstractInterruptHandler interruptHandler) { if (log.isDebugEnabled()) { log.debug(String.format("addDeferredInterrupt insideInterrupt=%b, interruptsEnabled=%b", isInsideInterrupt(), Interrupts.isInterruptsEnabled())); } deferredInterrupts.add(interruptHandler); } public boolean canExecuteInterruptNow() { return !isInsideInterrupt() && Interrupts.isInterruptsEnabled(); } public void onInterruptsEnabled() { if (!deferredInterrupts.isEmpty() && canExecuteInterruptNow()) { if (log.isDebugEnabled()) { log.debug("Executing deferred interrupts"); } List<AbstractInterruptHandler> copyDeferredInterrupts = new LinkedList<AbstractInterruptHandler>(deferredInterrupts); deferredInterrupts.clear(); executeInterrupts(copyDeferredInterrupts, null, null); } } public LinkedList<AbstractInterruptHandler> getInterruptHandlers(int intrNumber) { if (intrNumber < 0 || intrNumber >= PSP_NUMBER_INTERRUPTS) { return null; } return interrupts.get(intrNumber); } public void addInterruptHandler(int interruptNumber, AbstractInterruptHandler interruptHandler) { if (interruptNumber < 0 || interruptNumber >= PSP_NUMBER_INTERRUPTS) { return; } LinkedList<AbstractInterruptHandler> interruptHandlers = interrupts.get(interruptNumber); if (interruptHandlers == null) { interruptHandlers = new LinkedList<AbstractInterruptHandler>(); interrupts.set(interruptNumber, interruptHandlers); } interruptHandlers.add(interruptHandler); } public boolean removeInterruptHandler(int intrNumber, AbstractInterruptHandler interruptHandler) { if (intrNumber < 0 || intrNumber >= PSP_NUMBER_INTERRUPTS) { return false; } LinkedList<AbstractInterruptHandler> interruptHandlers = interrupts.get(intrNumber); if (interruptHandlers == null) { return false; } return interruptHandlers.remove(interruptHandler); } protected void onEndOfInterrupt() { if (log.isDebugEnabled()) { log.debug("End of Interrupt"); } allegrexInterruptHandlers.clear(); // Schedule to a thread having a higher priority if one is ready to run Modules.ThreadManForUserModule.hleRescheduleCurrentThread(); onInterruptsEnabled(); } public void pushAllegrexInterruptHandler(AbstractAllegrexInterruptHandler allegrexInterruptHandler) { allegrexInterruptHandlers.add(allegrexInterruptHandler); } public void continueCallAllegrexInterruptHandler(InterruptState interruptState, Iterator<AbstractAllegrexInterruptHandler> allegrexInterruptHandlersIterator, IAction continueAction) { boolean somethingExecuted = false; do { if (allegrexInterruptHandlersIterator != null && allegrexInterruptHandlersIterator.hasNext()) { AbstractAllegrexInterruptHandler allegrexInterruptHandler = allegrexInterruptHandlersIterator.next(); if (allegrexInterruptHandler != null) { if (log.isDebugEnabled()) { log.debug("Calling InterruptHandler " + allegrexInterruptHandler.toString()); } allegrexInterruptHandler.copyArgumentsToCpu(Emulator.getProcessor().cpu); Modules.ThreadManForUserModule.callAddress(allegrexInterruptHandler.getAddress(), continueAction, true); somethingExecuted = true; } } else { break; } } while (!somethingExecuted); if (!somethingExecuted) { // No more handlers, end of interrupt setInsideInterrupt(interruptState.restore(Emulator.getProcessor().cpu)); IAction afterInterruptAction = interruptState.getAfterInterruptAction(); if (afterInterruptAction != null) { afterInterruptAction.execute(); } onEndOfInterrupt(); } } protected void executeInterrupts(List<AbstractInterruptHandler> interruptHandlers, IAction afterInterruptAction, IAction afterHandlerAction) { if (interruptHandlers != null) { for (Iterator<AbstractInterruptHandler> it = interruptHandlers.iterator(); it.hasNext(); ) { AbstractInterruptHandler interruptHandler = it.next(); if (interruptHandler != null) { interruptHandler.execute(); } } } if (allegrexInterruptHandlers.isEmpty()) { if (afterInterruptAction != null) { afterInterruptAction.execute(); } onEndOfInterrupt(); } else { InterruptState interruptState = new InterruptState(); interruptState.save(insideInterrupt, Emulator.getProcessor().cpu, afterInterruptAction, afterHandlerAction); setInsideInterrupt(true); Iterator<AbstractAllegrexInterruptHandler> allegrexInterruptHandlersIterator = allegrexInterruptHandlers.iterator(); IAction continueAction = new AfterSubIntrAction(this, interruptState, allegrexInterruptHandlersIterator); continueCallAllegrexInterruptHandler(interruptState, allegrexInterruptHandlersIterator, continueAction); } } public void triggerInterrupt(int interruptNumber, IAction afterInterruptAction, IAction afterHandlerAction) { if (log.isDebugEnabled()) { log.debug(String.format("Triggering Interrupt %s(0x%X)", getInterruptName(interruptNumber), interruptNumber)); } executeInterrupts(getInterruptHandlers(interruptNumber), afterInterruptAction, afterHandlerAction); } public void triggerInterrupt(int interruptNumber, IAction afterInterruptAction, IAction afterHandlerAction, AbstractAllegrexInterruptHandler allegrexInterruptHandler) { if (log.isDebugEnabled()) { log.debug(String.format("Triggering Interrupt %s(0x%X) at 0x%08X", getInterruptName(interruptNumber), interruptNumber, allegrexInterruptHandler.getAddress())); } // Trigger only this interrupt handler allegrexInterruptHandlers.add(allegrexInterruptHandler); executeInterrupts(null, afterInterruptAction, afterHandlerAction); } public boolean isInsideInterrupt() { return insideInterrupt; } public void setInsideInterrupt(boolean insideInterrupt) { this.insideInterrupt = insideInterrupt; } public void addVBlankAction(IAction action) { vblankInterruptHandler.addVBlankAction(action); } public boolean removeVBlankAction(IAction action) { return vblankInterruptHandler.removeVBlankAction(action); } public void addVBlankActionOnce(IAction action) { vblankInterruptHandler.addVBlankActionOnce(action); } public boolean removeVBlankActionOnce(IAction action) { return vblankInterruptHandler.removeVBlankActionOnce(action); } public int sceKernelRegisterSubIntrHandler(int intrNumber, int subIntrNumber, int handlerAddress, int handlerArgument) { if (log.isDebugEnabled()) { log.debug(String.format("sceKernelRegisterSubIntrHandler(%d, %d, 0x%08X, 0x%08X)", intrNumber, subIntrNumber, handlerAddress, handlerArgument)); } if (intrNumber < 0 || intrNumber >= IntrManager.PSP_NUMBER_INTERRUPTS || subIntrNumber < 0) { return SceKernelErrors.ERROR_KERNEL_INVALID_INTR_NUMBER; } if (intrHandlers[intrNumber] == null) { IntrHandler intrHandler = new IntrHandler(); intrHandlers[intrNumber] = intrHandler; addInterruptHandler(intrNumber, intrHandler); } else if (intrHandlers[intrNumber].getSubIntrHandler(subIntrNumber) != null) { return SceKernelErrors.ERROR_KERNEL_SUBINTR_ALREADY_REGISTERED; } SubIntrHandler subIntrHandler = new SubIntrHandler(handlerAddress, subIntrNumber, handlerArgument); subIntrHandler.setEnabled(false); intrHandlers[intrNumber].addSubIntrHandler(subIntrNumber, subIntrHandler); return 0; } public int sceKernelReleaseSubIntrHandler(int intrNumber, int subIntrNumber) { if (log.isDebugEnabled()) { log.debug(String.format("sceKernelReleaseSubIntrHandler(%d, %d)", intrNumber, subIntrNumber)); } if (intrNumber < 0 || intrNumber >= IntrManager.PSP_NUMBER_INTERRUPTS || subIntrNumber < 0) { return SceKernelErrors.ERROR_KERNEL_INVALID_INTR_NUMBER; } if (intrHandlers[intrNumber] == null) { return SceKernelErrors.ERROR_KERNEL_SUBINTR_NOT_REGISTERED; } if (!intrHandlers[intrNumber].removeSubIntrHandler(subIntrNumber)) { return SceKernelErrors.ERROR_KERNEL_SUBINTR_NOT_REGISTERED; } return 0; } protected int hleKernelEnableDisableSubIntr(int intrNumber, int subIntrNumber, boolean enabled) { if (log.isDebugEnabled()) { log.debug(String.format("sceKernel%sSubIntr(%d, %d)", enabled ? "Enable" : "Disable", intrNumber, subIntrNumber)); } if (intrNumber < 0 || intrNumber >= IntrManager.PSP_NUMBER_INTERRUPTS) { return -1; } if (intrHandlers[intrNumber] == null) { return -1; } SubIntrHandler subIntrHandler = intrHandlers[intrNumber].getSubIntrHandler(subIntrNumber); if (subIntrHandler == null) { return -1; } subIntrHandler.setEnabled(enabled); return 0; } public int sceKernelEnableSubIntr(int intrNumber, int subIntrNumber) { return hleKernelEnableDisableSubIntr(intrNumber, subIntrNumber, true); } public int sceKernelDisableSubIntr(int intrNumber, int subIntrNumber) { return hleKernelEnableDisableSubIntr(intrNumber, subIntrNumber, false); } }