pcsx2/fps2bios/kernel/iopload/intrman/int_handler.s

374 lines
9.0 KiB
ArmAsm

.set noreorder
.set noat
.global EXCEP_Sys_handler
.ent EXCEP_Sys_handler
EXCEP_Sys_handler:
.word 0
.word 0
jal EXCEP_Sys_8
nop # branch delay
# restore status
lw $k0, EX_ST($0)
mtc0 $k0, $12
lw $k0, EX_PC($0)
add $k0, $k0, 4
jr $k0
cop0 0x10
nop
.end EXCEP_Sys_handler
.equiv STACK_FRAME_SIZE, 0x90
.equiv MODE0_ID, 0xAC0000FE
.equiv MODE1_ID, 0xFF00FFFE
.equiv MODE2_ID, 0xFFFFFFFE
.equiv EX_AT, 0x400
.equiv EX_PC, 0x404
.equiv EX_ST, 0x408
.equiv EX_CAUSE, 0x40C
.equiv INTR_STACK_SIZE, 0x800
.equiv I_REG, 0x1070
.equiv I_MASK, 0x1074
# ------------------------------------------------------------
# This is installed as priority 3 - the end of the chain.
# does nothing.
#
.global EXCEP_Int_priority_exception_handler
.ent EXCEP_Int_priority_exception_handler
EXCEP_Int_priority_exception_handler:
.word 0
.word 0
lw $at, EX_AT($0)
# restore status
lw $k0, EX_ST($0)
mtc0 $k0, $12
lw $k0, EX_PC($0)
jr $k0
cop0 0x10
nop
.end EXCEP_Int_priority_exception_handler
# ------------------------------------------------------------
.global EXCEP_Int_handler
.ent EXCEP_Int_handler
EXCEP_Int_handler:
.word 0
.word 0
# Initialise the mode 0 stack frame
sw $sp, 0x74-STACK_FRAME_SIZE($sp)
addiu $sp, $sp, -STACK_FRAME_SIZE
lw $at, EX_AT($0)
sw $at, 0x04($sp)
sw $v0, 0x08($sp)
sw $v1, 0x0C($sp)
sw $a0, 0x10($sp)
sw $a1, 0x14($sp)
sw $a2, 0x18($sp)
sw $a3, 0x1C($sp)
sw $ra, 0x7C($sp)
mfhi $v0
sw $v0, 0x80($sp)
mflo $v0
sw $v0, 0x84($sp)
lw $v0, EX_ST($0)
sw $v0, 0x88($sp)
lw $v0, EX_PC($0)
sw $v0, 0x8C($sp)
la $v0, MODE0_ID
jal QueryIntrContext
sw $v0, 0x00($sp) # branch delay slot
# Check whether this is a nested interrupt
# If it is not a nested interrupt then switch the stack to the interrupt context.
beq $v0, $0, $L_not_nested
or $v0, $sp, $sp # branch delay slot
# it is a nested interrupt
j $L1
sub $sp, $sp, 0x0C # branch delay slot
$L_not_nested:
# not a nested interrupt, so initialise to the top of the interrupt
# context stack.
la $sp, tempstack
addiu $sp, $sp, INTR_STACK_SIZE - 0x0C
$L1:
sw $v0, 0x4($sp) # link the location of the previous stack frame
sw $0, 0x8($sp) # interrupt number (bit mask)
lw $v0, EX_CAUSE($0)
andi $v0, $v0, 0x30
bne $v0, $0, $L_SoftwareInt
nop
# The interrupt must have been caused by a hardware event
lui $k0, 0xBF80
lw $a0, I_REG($k0)
lw $k0, I_MASK($k0) # k0 = I_MASK
and $a0, $a0, $k0
lw $k1, soft_hw_intr_mask
and $a0, $a0, $k1 # a0 = I_REG & I_MASK & soft_hw_intr_mask
beq $a0, $0, $L_ChainNextHandler # Interrupt is masked
nop
# The interrupt is not masked, so we should try and handle it
# Figure out what interrupt is being handled. Look at the bits from the LSB and
# find the first one that is set. The LSB is interrupt 0
la $a1, 0xFFFFFFFF # The interrupt being handled
$L_countInts:
addi $a1, $a1, 1
andi $a2, $a0, 0x01
beq $a2, $0, $L_countInts
srl $a0, 1
addiu $a0, $0, 0x1
sllv $a0, $a0, $a1 # a0 = the bitmask of the int being handled
sw $a0, 0x8($sp)
nor $a0, $a0, $0
and $k0, $a0, $k0
lui $k1, 0xBF80
sw $k0, I_MASK($k1)
sw $a0, I_REG($k1)
sll $a0, $a1, 3 # Int num * 8
lw $k0, intrtable
add $k0, $a0, $k0
lw $a2, 0($k0) # handler
bne $a2, $0, $L_HandleInterrupt
lw $a3, 4($k0) # arg
$L_ChainNextHandler:
# Pass control to the next exception handler
lw $sp, 0x4($sp) # pop the value of the previous stack frame (could be a context switch)
lw $at, 0x04($sp)
lw $v0, 0x08($sp)
lw $v1, 0x0C($sp)
lw $a0, 0x10($sp)
lw $a1, 0x14($sp)
lw $a2, 0x18($sp)
lw $a3, 0x1C($sp)
lw $ra, 0x7C($sp)
addiu $sp, $sp, STACK_FRAME_SIZE
lw $k0, EXCEP_Int_handler
jr $k0
nop
$L_SoftwareInt:
# $v0 = CAUSE & 0x30 is non-zero so the interrupt must have been caused by a software event
andi $v0, $v0, 0x10
bne $v0, $0, $L2
addiu $a0, $0, 0x0100 # sw Int 1
addiu $a0, $0, 0x0200 # sw Int 2
$L2:
mfc0 $a1, $13 # cause
nor $a2, $a0, $0
and $a1, $a1, $a2 # reset the relevent cause bit
mtc0 $a1, $13
srl $a0, $a0, 8
addiu $a0, $a0, 0x2D # interrupt vector is 0x2E or 0x2F
sll $a0, $a0, 3 # Int num * 8
lw $k0, intrtable
addu $k0, $a0, $k0
lw $a2, 0($k0) # handler
beq $a2, $0, $L_ChainNextHandler # No handler defined
lw $a3, 4($k0) # arg
# Both paths for SW and HW interrupts come together here.
#
# At this point:
# 0x8($sp) = int mask (0x01 << interrupt number)
# $a2 = handler
# $a3 = arg
$L_HandleInterrupt:
andi $a0, $a2, 0x03 # $a0 = mode
srl $a2, $a2, 2
beq $a0, $0, $L_Mode0
sll $a2, $a2, 2 # $a2 = masked handler
la $v1, MODE1_ID
srl $a0, $a0, 1
beq $a0, $0, $L_Mode1
lw $v0, 4($sp)
# Mode 2 or 3 (save t and s)
sw $s0, 0x40($v0)
sw $s1, 0x44($v0)
sw $s2, 0x48($v0)
sw $s3, 0x4C($v0)
sw $s4, 0x50($v0)
sw $s5, 0x54($v0)
sw $s6, 0x58($v0)
sw $s7, 0x5C($v0)
la $v1, MODE2_ID
$L_Mode1:
# Mode 1, save t
sw $t0, 0x20($v0)
sw $t1, 0x24($v0)
sw $t2, 0x28($v0)
sw $t3, 0x2C($v0)
sw $t4, 0x30($v0)
sw $t5, 0x34($v0)
sw $t6, 0x38($v0)
sw $t7, 0x3C($v0)
sw $t8, 0x60($v0)
sw $t9, 0x64($v0)
sw $gp, 0x70($v0)
sw $fp, 0x78($v0)
sw $v1, 0x00($v0) # mark the current state of the stack
$L_Mode0:
or $a0, $a3, $a3
jalr $a2 # call the interrupt handler
mtc0 $0, $12 # status
beq $v0, $0, $L_NotHandled
lui $k0, 0xBF80
lw $a0, I_MASK($k0)
lw $a1, 0x08($sp) # interrupt mask
or $a0, $a0, $a1
sw $a0, I_MASK($k0)
$L_NotHandled:
jal QueryIntrContext
nop
bne $v0, $0, $L_afterContextSwitch
lw $a0, 4($sp) # previous stack frame
# test if a context switch is pending. Returns true ($v0 != 0) if there is.
jal callContextSwitchRequiredHandler
nop
beq $v0, $0, $L_afterContextSwitch
lw $a0, 4($sp) # previous stack frame
lw $v0, 0($a0) # current stack context state
la $a1, MODE2_ID
beq $a1, $v0, $DoContextSwitch
nop
la $a1, MODE1_ID
beq $a1, $v0, $Mode1OnStack
nop
# stack state is currently mode 0
sw $t0, 0x20($a0)
sw $t1, 0x24($a0)
sw $t2, 0x28($a0)
sw $t3, 0x2C($a0)
sw $t4, 0x30($a0)
sw $t5, 0x34($a0)
sw $t6, 0x38($a0)
sw $t7, 0x3C($a0)
sw $t8, 0x60($a0)
sw $t9, 0x64($a0)
sw $gp, 0x70($a0)
sw $fp, 0x78($a0)
$Mode1OnStack:
sw $s0, 0x40($a0)
sw $s1, 0x44($a0)
sw $s2, 0x48($a0)
sw $s3, 0x4C($a0)
sw $s4, 0x50($a0)
sw $s5, 0x54($a0)
sw $s6, 0x58($a0)
sw $s7, 0x5C($a0)
la $v1, MODE2_ID
sw $v1, 0($a0)
$DoContextSwitch:
la $v0, context_switch_handler
lw $v0, 0($v0)
jalr $v0 # do the context switch, returns the new stack in $v0
nop
addu $a0, $v0, $0
$L_afterContextSwitch:
# At this point:
#
# $a0 = stack frame to switch to
or $sp, $a0, $a0
lw $a0, 0($sp)
la $a1, MODE0_ID
beq $a0, $a1, $L_RestoreMode0
nop
la $a1, MODE1_ID
beq $a0, $a1, $L_RestoreMode1
nop
# mode 2, restore t and s
lw $s0, 0x40($sp)
lw $s1, 0x44($sp)
lw $s2, 0x48($sp)
lw $s3, 0x4C($sp)
lw $s4, 0x50($sp)
lw $s5, 0x54($sp)
lw $s6, 0x58($sp)
lw $s7, 0x5C($sp)
$L_RestoreMode1:
# mode 1, restore t
lw $t0, 0x20($sp)
lw $t1, 0x24($sp)
lw $t2, 0x28($sp)
lw $t3, 0x2C($sp)
lw $t4, 0x30($sp)
lw $t5, 0x34($sp)
lw $t6, 0x38($sp)
lw $t7, 0x3C($sp)
lw $t8, 0x60($sp)
lw $t9, 0x64($sp)
lw $gp, 0x70($sp)
lw $fp, 0x78($sp)
$L_RestoreMode0:
lw $a0, 0x80($sp)
mthi $a0
lw $a0, 0x84($sp)
mtlo $a0
lw $a0, 0x88($sp)
srl $a0, $a0, 1
sll $a0, $a0, 1
mtc0 $a0, $12 # status (reset bit 0)
lw $at, 0x04($sp)
lw $v0, 0x08($sp)
lw $v1, 0x0C($sp)
lw $a0, 0x10($sp)
lw $a1, 0x14($sp)
lw $a2, 0x18($sp)
lw $a3, 0x1C($sp)
lw $ra, 0x7C($sp)
lw $k0, 0x8C($sp)
lw $sp, 0x74($sp)
jr $k0
cop0 0x10 # rfe
.end EXCEP_Int_handler