1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2025-08-10 08:50:23 +00:00

Rename and move handwritten asm files (#1254)

* Rename handwritten asm files and move them to src

* Fix progress.py

* Remove handling for asm dir from Makefile
This commit is contained in:
Tharo 2022-06-03 21:33:18 +01:00 committed by GitHub
parent e989cb7ace
commit 4775fd4a7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 47 additions and 59 deletions

View file

@ -0,0 +1,9 @@
#include "ultra64/asm.h"
.section .rodata
.balign 16
DATA(__libm_qnan_f)
.word 0x7F810000
ENDDATA(__libm_qnan_f)

93
src/libultra/libc/bcmp.s Normal file
View file

@ -0,0 +1,93 @@
#include "ultra64/asm.h"
.set noat
.set noreorder
.section .text
.balign 16
LEAF(bcmp)
slti $at, $a2, 0x10
bnez $at, bytecmp
xor $v0, $a0, $a1
andi $v0, $v0, 3
bnez $v0, unaligncmp
negu $t8, $a0
andi $t8, $t8, 3
beqz $t8, wordcmp
subu $a2, $a2, $t8
move $v0, $v1
lwl $v0, ($a0)
lwl $v1, ($a1)
addu $a0, $a0, $t8
addu $a1, $a1, $t8
bne $v0, $v1, cmpne
wordcmp:
li $at, ~3
and $a3, $a2, $at
beqz $a3, bytecmp
subu $a2, $a2, $a3
addu $a3, $a3, $a0
lw $v0, ($a0)
1:
lw $v1, ($a1)
addiu $a0, $a0, 4
addiu $a1, $a1, 4
bne $v0, $v1, cmpne
nop
bnel $a0, $a3, 1b
lw $v0, ($a0)
b bytecmp
nop
unaligncmp:
negu $a3, $a1
andi $a3, $a3, 3
beqz $a3, partaligncmp
subu $a2, $a2, $a3
addu $a3, $a3, $a0
lbu $v0, ($a0)
1:
lbu $v1, ($a1)
addiu $a0, $a0, 1
addiu $a1, $a1, 1
bne $v0, $v1, cmpne
nop
bnel $a0, $a3, 1b
lbu $v0, ($a0)
partaligncmp:
li $at, ~3
and $a3, $a2, $at
beqz $a3, bytecmp
subu $a2, $a2, $a3
addu $a3, $a3, $a0
lwl $v0, ($a0)
1:
lw $v1, ($a1)
lwr $v0, 3($a0)
addiu $a0, $a0, 4
addiu $a1, $a1, 4
bne $v0, $v1, cmpne
nop
bnel $a0, $a3, 1b
lwl $v0, ($a0)
bytecmp:
blez $a2, cmpdone
addu $a3, $a2, $a0
lbu $v0, ($a0)
1:
lbu $v1, ($a1)
addiu $a0, $a0, 1
addiu $a1, $a1, 1
bne $v0, $v1, cmpne
nop
bnel $a0, $a3, 1b
lbu $v0, ($a0)
cmpdone:
jr $ra
move $v0, $zero
cmpne:
jr $ra
li $v0, 1
END(bcmp)

233
src/libultra/libc/bcopy.s Normal file
View file

@ -0,0 +1,233 @@
#include "ultra64/asm.h"
.set noat
.set noreorder
.section .text
.balign 16
LEAF(bcopy)
beqz $a2, ret
move $a3, $a1
beq $a0, $a1, ret
slt $at, $a1, $a0
bnezl $at, goforwards
slti $at, $a2, 0x10
add $v0, $a0, $a2
slt $at, $a1, $v0
beql $at, $zero, goforwards
slti $at, $a2, 0x10
b gobackwards
slti $at, $a2, 0x10
slti $at, $a2, 0x10
goforwards:
bnez $at, forwards_bytecopy
nop
andi $v0, $a0, 3
andi $v1, $a1, 3
beq $v0, $v1, forwalignable
nop
forwards_bytecopy:
beqz $a2, ret
nop
addu $v1, $a0, $a2
99:
lb $v0, ($a0)
addiu $a0, $a0, 1
addiu $a1, $a1, 1
bne $a0, $v1, 99b
sb $v0, -1($a1)
ret:
jr $ra
move $v0, $a3
forwalignable:
beqz $v0, forwards_32
li $at, 1
beq $v0, $at, forw_copy3
li $at, 2
beql $v0, $at, forw_copy2
lh $v0, ($a0)
lb $v0, ($a0)
addiu $a0, $a0, 1
addiu $a1, $a1, 1
addiu $a2, $a2, -1
b forwards_32
sb $v0, -1($a1)
lh $v0, ($a0)
forw_copy2:
addiu $a0, $a0, 2
addiu $a1, $a1, 2
addiu $a2, $a2, -2
b forwards_32
sh $v0, -2($a1)
forw_copy3:
lb $v0, ($a0)
lh $v1, 1($a0)
addiu $a0, $a0, 3
addiu $a1, $a1, 3
addiu $a2, $a2, -3
sb $v0, -3($a1)
sh $v1, -2($a1)
forwards:
forwards_32:
slti $at, $a2, 0x20
bnezl $at, forwards_16_
slti $at, $a2, 0x10
lw $v0, ($a0)
lw $v1, 4($a0)
lw $t0, 8($a0)
lw $t1, 0xC($a0)
lw $t2, 0x10($a0)
lw $t3, 0x14($a0)
lw $t4, 0x18($a0)
lw $t5, 0x1C($a0)
addiu $a0, $a0, 0x20
addiu $a1, $a1, 0x20
addiu $a2, $a2, -0x20
sw $v0, -0x20($a1)
sw $v1, -0x1C($a1)
sw $t0, -0x18($a1)
sw $t1, -0x14($a1)
sw $t2, -0x10($a1)
sw $t3, -0xC($a1)
sw $t4, -8($a1)
b forwards_32
sw $t5, -4($a1)
forwards_16:
slti $at, $a2, 0x10
forwards_16_: // fake label due to branch likely optimization
bnezl $at, forwards_4_
slti $at, $a2, 4
lw $v0, ($a0)
lw $v1, 4($a0)
lw $t0, 8($a0)
lw $t1, 0xC($a0)
addiu $a0, $a0, 0x10
addiu $a1, $a1, 0x10
addiu $a2, $a2, -0x10
sw $v0, -0x10($a1)
sw $v1, -0xC($a1)
sw $t0, -8($a1)
b forwards_16
sw $t1, -4($a1)
forwards_4:
slti $at, $a2, 4
forwards_4_: // fake label due to branch likely optimization
bnez $at, forwards_bytecopy
nop
lw $v0, ($a0)
addiu $a0, $a0, 4
addiu $a1, $a1, 4
addiu $a2, $a2, -4
b forwards_4
sw $v0, -4($a1)
slti $at, $a2, 0x10
gobackwards:
add $a0, $a0, $a2
bnez $at, backwards_bytecopy
add $a1, $a1, $a2
andi $v0, $a0, 3
andi $v1, $a1, 3
beq $v0, $v1, backalignable
nop
backwards_bytecopy:
beqz $a2, ret
nop
addiu $a0, $a0, -1
addiu $a1, $a1, -1
subu $v1, $a0, $a2
99:
lb $v0, ($a0)
addiu $a0, $a0, -1
addiu $a1, $a1, -1
bne $a0, $v1, 99b
sb $v0, 1($a1)
jr $ra
move $v0, $a3
backalignable:
beqz $v0, backwards_32
li $at, 3
beq $v0, $at, back_copy3
li $at, 2
beql $v0, $at, back_copy2
lh $v0, -2($a0)
lb $v0, -1($a0)
addiu $a0, $a0, -1
addiu $a1, $a1, -1
addiu $a2, $a2, -1
b backwards_32
sb $v0, ($a1)
lh $v0, -2($a0)
back_copy2:
addiu $a0, $a0, -2
addiu $a1, $a1, -2
addiu $a2, $a2, -2
b backwards_32
sh $v0, ($a1)
back_copy3:
lb $v0, -1($a0)
lh $v1, -3($a0)
addiu $a0, $a0, -3
addiu $a1, $a1, -3
addiu $a2, $a2, -3
sb $v0, 2($a1)
sh $v1, ($a1)
backwards:
backwards_32:
slti $at, $a2, 0x20
bnezl $at, backwards_16_
slti $at, $a2, 0x10
lw $v0, -4($a0)
lw $v1, -8($a0)
lw $t0, -0xc($a0)
lw $t1, -0x10($a0)
lw $t2, -0x14($a0)
lw $t3, -0x18($a0)
lw $t4, -0x1c($a0)
lw $t5, -0x20($a0)
addiu $a0, $a0, -0x20
addiu $a1, $a1, -0x20
addiu $a2, $a2, -0x20
sw $v0, 0x1C($a1)
sw $v1, 0x18($a1)
sw $t0, 0x14($a1)
sw $t1, 0x10($a1)
sw $t2, 0xC($a1)
sw $t3, 8($a1)
sw $t4, 4($a1)
b backwards_32
sw $t5, ($a1)
backwards_16:
slti $at, $a2, 0x10
backwards_16_: // fake label due to branch likely optimization
bnezl $at, backwards_4_
slti $at, $a2, 4
lw $v0, -4($a0)
lw $v1, -8($a0)
lw $t0, -0xC($a0)
lw $t1, -0x10($a0)
addiu $a0, $a0, -0x10
addiu $a1, $a1, -0x10
addiu $a2, $a2, -0x10
sw $v0, 0xC($a1)
sw $v1, 8($a1)
sw $t0, 4($a1)
b backwards_16
sw $t1, ($a1)
backwards_4:
slti $at, $a2, 4
backwards_4_: // fake label due to branch likely optimization
bnez $at, backwards_bytecopy
nop
lw $v0, -4($a0)
addiu $a0, $a0, -4
addiu $a1, $a1, -4
addiu $a2, $a2, -4
b backwards_4
sw $v0, ($a1)
END(bcopy)

65
src/libultra/libc/bzero.s Normal file
View file

@ -0,0 +1,65 @@
#include "ultra64/asm.h"
.set noat
.set noreorder
.section .text
.balign 16
LEAF(bzero)
slti $at, $a1, 0xC
bnez $at, bytezero
negu $v1, $a0
andi $v1, $v1, 3
beqz $v1, blkzero
subu $a1, $a1, $v1
swl $zero, ($a0)
addu $a0, $a0, $v1
blkzero:
// align backwards to 0x20
li $at, ~0x1F
and $a3, $a1, $at
// If the result is zero, the amount to zero is less than 0x20 bytes
beqz $a3, wordzero
subu $a1, $a1, $a3
// zero in blocks of 0x20 at a time
addu $a3, $a3, $a0
1:
addiu $a0, $a0, 0x20
sw $zero, -0x20($a0)
sw $zero, -0x1C($a0)
sw $zero, -0x18($a0)
sw $zero, -0x14($a0)
sw $zero, -0x10($a0)
sw $zero, -0xC($a0)
sw $zero, -8($a0)
bne $a0, $a3, 1b
sw $zero, -4($a0)
wordzero:
// align backwards to 0x4
li $at, ~3
and $a3, $a1, $at
// If the result is zero, the amount to zero is less than 0x4 bytes
beqz $a3, bytezero
subu $a1, $a1, $a3
// zero one word at a time
addu $a3, $a3, $a0
1:
addiu $a0, $a0, 4
bne $a0, $a3, 1b
sw $zero, -4($a0)
bytezero:
// test if nothing left to zero
blez $a1, zerodone
nop
// zero one byte at a time
addu $a1, $a1, $a0
1:
addiu $a0, $a0, 1
bne $a0, $a1, 1b
sb $zero, -1($a0)
zerodone:
jr $ra
nop
END(bzero)

40
src/libultra/mgu/mtxf2l.s Normal file
View file

@ -0,0 +1,40 @@
#include "ultra64/asm.h"
.set noat
.set noreorder
.section .text
.balign 32
#define MTX_INTPART 0
#define MTX_FRACPART 0x20
LEAF(guMtxF2L)
li $at, 0x47800000 // 65536.0f
mtc1 $at, $f0
li $t9, 0xFFFF0000
addiu $t8, $a1, MTX_FRACPART
1:
lwc1 $f4, ($a0)
lwc1 $f10, 4($a0)
addiu $a1, $a1, 4
mul.s $f6, $f4, $f0
addiu $a0, $a0, 8
mul.s $f16, $f10, $f0
trunc.w.s $f8, $f6
trunc.w.s $f18, $f16
mfc1 $t0, $f8
mfc1 $t1, $f18
and $t2, $t0, $t9
sll $t5, $t0, 0x10
srl $t3, $t1, 0x10
andi $t6, $t1, 0xFFFF
or $t4, $t2, $t3
or $t7, $t5, $t6
sw $t4, (MTX_INTPART-4)($a1)
bne $a1, $t8, 1b
sw $t7, (MTX_FRACPART-4)($a1)
jr $ra
nop
END(guMtxF2L)

View file

@ -0,0 +1,29 @@
#include "ultra64/asm.h"
.set noreorder
.section .text
.balign 32
LEAF(guMtxIdent)
addi $t0, $zero, 1
sll $t1, $t0, 0x10
sw $t1, ($a0)
sw $zero, 4($a0)
sw $t0, 8($a0)
sw $zero, 0xc($a0)
sw $zero, 0x10($a0)
sw $t1, 0x14($a0)
sw $zero, 0x18($a0)
sw $t0, 0x1C($a0)
sw $zero, 0x20($a0)
sw $zero, 0x24($a0)
sw $zero, 0x28($a0)
sw $zero, 0x2c($a0)
sw $zero, 0x30($a0)
sw $zero, 0x34($a0)
sw $zero, 0x38($a0)
jr $ra
sw $zero, 0x3C($a0)
END(guMtxIdent)

View file

@ -0,0 +1,28 @@
#include "ultra64/asm.h"
.set noreorder
.section .text
.balign 32
LEAF(guMtxIdentF)
li $t0, 0x3F800000 // 1.0f
sw $t0, ($a0)
sw $zero, 4($a0)
sw $zero, 8($a0)
sw $zero, 0xC($a0)
sw $zero, 0x10($a0)
sw $t0, 0x14($a0)
sw $zero, 0x18($a0)
sw $zero, 0x1C($a0)
sw $zero, 0x20($a0)
sw $zero, 0x24($a0)
sw $t0, 0x28($a0)
sw $zero, 0x2C($a0)
sw $zero, 0x30($a0)
sw $zero, 0x34($a0)
sw $zero, 0x38($a0)
jr $ra
sw $t0, 0x3C($a0)
END(guMtxIdentF)

41
src/libultra/mgu/mtxl2f.s Normal file
View file

@ -0,0 +1,41 @@
#include "ultra64/asm.h"
.set noat
.set noreorder
.section .text
.balign 32
#define MTX_INTPART 0
#define MTX_FRACPART 0x20
LEAF(guMtxL2F)
li $at, 0x37800000 // 1.0f / 65536.0f
mtc1 $at, $f0
li $t9, 0xFFFF0000
addiu $t8, $a1, MTX_FRACPART
1:
lw $t0, MTX_INTPART($a1)
lw $t1, MTX_FRACPART($a1)
addiu $a1, $a1, 4
and $t2, $t0, $t9
srl $t3, $t1, 0x10
or $t4, $t2, $t3
mtc1 $t4, $f4
sll $t5, $t0, 0x10
andi $t6, $t1, 0xFFFF
or $t7, $t5, $t6
cvt.s.w $f6, $f4
mtc1 $t7, $f10
addiu $a0, $a0, 8
cvt.s.w $f16, $f10
mul.s $f8, $f6, $f0
nop
mul.s $f18, $f16, $f0
swc1 $f8, -8($a0)
bne $a1, $t8, 1b
swc1 $f18, -4($a0)
jr $ra
nop
END(guMtxL2F)

View file

@ -0,0 +1,31 @@
#include "ultra64/asm.h"
.set noreorder
.section .text
.balign 32
LEAF(guNormalize)
lwc1 $f4, ($a0)
lwc1 $f6, ($a1)
lwc1 $f8, ($a2)
mul.s $f10, $f4, $f4
li $t0, 0x3F800000 // 1.0f
mul.s $f16, $f6, $f6
add.s $f18, $f10, $f16
mul.s $f16, $f8, $f8
add.s $f10, $f16, $f18
mtc1 $t0, $f18
sqrt.s $f16, $f10
div.s $f10, $f18, $f16
mul.s $f16, $f4, $f10
nop
mul.s $f18, $f6, $f10
nop
mul.s $f4, $f8, $f10
swc1 $f16, ($a0)
swc1 $f18, ($a1)
jr $ra
swc1 $f4, ($a2)
END(guNormalize)

52
src/libultra/mgu/scale.s Normal file
View file

@ -0,0 +1,52 @@
#include "ultra64/asm.h"
.set noat
.set noreorder
.section .text
.balign 32
LEAF(guScale)
li $at, 0x47800000 // 65536.0f
mtc1 $at, $f4
mtc1 $a1, $f6
sw $zero, 4($a0)
sw $zero, 0xC($a0)
mul.s $f8, $f6, $f4
mtc1 $a2, $f6
sw $zero, 0x10($a0)
sw $zero, 0x18($a0)
sw $zero, 0x24($a0)
sw $zero, 0x2C($a0)
sw $zero, 0x30($a0)
trunc.w.s $f10, $f8
mul.s $f8, $f6, $f4
mtc1 $a3, $f6
sw $zero, 0x38($a0)
mfc1 $t1, $f10
sw $zero, 0x3C($a0)
srl $t2, $t1, 0x10
trunc.w.s $f10, $f8
mul.s $f8, $f6, $f4
sll $t0, $t2, 0x10
sll $t2, $t1, 0x10
mfc1 $t1, $f10
sw $t0, ($a0)
sw $t2, 0x20($a0)
srl $t0, $t1, 0x10
trunc.w.s $f10, $f8
andi $t2, $t1, 0xFFFF
sw $t2, 0x28($a0)
sw $t0, 8($a0)
mfc1 $t1, $f10
nop
srl $t2, $t1, 0x10
sll $t0, $t2, 0x10
sw $t0, 0x14($a0)
li $t0, 1
sll $t2, $t1, 0x10
sw $t2, 0x34($a0)
jr $ra
sw $t0, 0x1C($a0)
END(guScale)

View file

@ -0,0 +1,61 @@
#include "ultra64/asm.h"
.set noat
.set noreorder
.section .text
.balign 32
LEAF(guTranslate)
li $at, 0x47800000 // 65536.0f
mtc1 $at, $f4
mtc1 $a1, $f6
sw $zero, ($a0)
sw $zero, 0x14($a0)
mul.s $f8, $f6, $f4
mtc1 $a2, $f6
sw $zero, 8($a0)
sw $zero, 4($a0)
sw $zero, 0xC($a0)
sw $zero, 0x10($a0)
sw $zero, 0x20($a0)
trunc.w.s $f10, $f8
mul.s $f8, $f6, $f4
mtc1 $a3, $f6
sw $zero, 0x24($a0)
mfc1 $t1, $f10
sw $zero, 0x28($a0)
sw $zero, 0x2C($a0)
srl $t2, $t1, 0x10
trunc.w.s $f10, $f8
mul.s $f8, $f6, $f4
sll $t0, $t2, 0x10
sw $zero, 0x30($a0)
mfc1 $t3, $f10
sw $zero, 0x34($a0)
srl $t2, $t3, 0x10
trunc.w.s $f10, $f8
or $t0, $t0, $t2
sw $t0, 0x18($a0)
sll $t0, $t1, 0x10
sll $t2, $t3, 0x10
mfc1 $t1, $f10
srl $t2, $t2, 0x10
or $t0, $t0, $t2
sw $t0, 0x38($a0)
srl $t2, $t1, 0x10
sll $t0, $t2, 0x10
addiu $t0, $t0, 1
sw $t0, 0x1C($a0)
lui $t0, 1
ori $t0, $t0, 0
sw $t0, ($a0)
sw $t0, 0x14($a0)
lui $t0, (0x00000001 >> 16)
ori $t0, (0x00000001 & 0xFFFF)
sll $t2, $t1, 0x10
sw $t2, 0x3C($a0)
jr $ra
sw $t0, 8($a0)
END(guTranslate)

982
src/libultra/os/exceptasm.s Normal file
View file

@ -0,0 +1,982 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
#include "ultra64/rcp.h"
#include "ultra64/rsp.h"
#include "ultra64/message.h"
#include "ultra64/thread.h"
#include "ultra64/exception.h"
.set noat
.set noreorder
.set gp=64
.section .data
.balign 16
DATA(__osHwIntTable)
.word 0, 0
.word 0, 0 // cart
.word 0, 0
.word 0, 0
.word 0, 0
ENDDATA(__osHwIntTable)
DATA(__osPiIntTable)
.word 0, 0
ENDDATA(__osPiIntTable)
.section .rodata
.balign 16
__osIntOffTable:
.byte 0x00 /* redispatch */
.byte 0x14 /* prenmi */
.byte 0x18 /* IP6_Hdlr */
.byte 0x18 /* IP6_Hdlr */
.byte 0x1C /* IP7_Hdlr */
.byte 0x1C /* IP7_Hdlr */
.byte 0x1C /* IP7_Hdlr */
.byte 0x1C /* IP7_Hdlr */
.byte 0x20 /* counter */
.byte 0x20 /* counter */
.byte 0x20 /* counter */
.byte 0x20 /* counter */
.byte 0x20 /* counter */
.byte 0x20 /* counter */
.byte 0x20 /* counter */
.byte 0x20 /* counter */
.byte 0x00 /* redispatch */
.byte 0x04 /* sw1 */
.byte 0x08 /* sw2 */
.byte 0x08 /* sw2 */
.byte 0x0C /* rcp */
.byte 0x0C /* rcp */
.byte 0x0C /* rcp */
.byte 0x0C /* rcp */
.byte 0x10 /* cart */
.byte 0x10 /* cart */
.byte 0x10 /* cart */
.byte 0x10 /* cart */
.byte 0x10 /* cart */
.byte 0x10 /* cart */
.byte 0x10 /* cart */
.byte 0x10 /* cart */
__osIntTable:
.word redispatch
.word sw1
.word sw2
.word rcp
.word cart
.word prenmi
.word IP6_Hdlr
.word IP7_Hdlr
.word counter
.section .text
.balign 16
/**
* The exception preamble is copied to the exception vectors at
* UT_VEC, XUT_VEC, ECC_VEC, E_VEC, to direct execution to __osException
*/
LEAF(__osExceptionPreamble)
lui $k0, %hi(__osException)
addiu $k0, %lo(__osException)
jr $k0
nop
END(__osExceptionPreamble)
LEAF(__osException)
// Load scratch space for thread saving
lui $k0, %hi(__osThreadSave)
addiu $k0, %lo(__osThreadSave)
// Save $at
sd $at, THREAD_AT($k0)
// Save sr
mfc0 $k1, C0_SR
sw $k1, THREAD_SR($k0)
// Disable interrupts
li $at, ~(SR_IE | SR_EXL)
and $k1, $k1, $at
mtc0 $k1, C0_SR
// Save some temp registers for use in the following
sd $t0, THREAD_T0($k0)
sd $t1, THREAD_T1($k0)
sd $t2, THREAD_T2($k0)
// Mark FPU as unused
sw $zero, THREAD_FP($k0)
// Left over from misplaced ifdef, immediately overwritten on next instruction
mfc0 $t0, C0_CAUSE
savecontext:
// Save the previously running thread's context to be restored when it resumes
move $t0, $k0
lui $k0, %hi(__osRunningThread)
lw $k0, %lo(__osRunningThread)($k0)
ld $t1, THREAD_AT($t0)
sd $t1, THREAD_AT($k0)
ld $t1, THREAD_SR($t0)
sd $t1, THREAD_SR($k0)
ld $t1, THREAD_T0($t0)
sd $t1, THREAD_T0($k0)
ld $t1, THREAD_T1($t0)
sd $t1, THREAD_T1($k0)
ld $t1, THREAD_T2($t0)
sd $t1, THREAD_T2($k0)
lw $k1, THREAD_SR($k0)
mflo $t0
sd $t0, THREAD_LO($k0)
mfhi $t0
andi $t1, $k1, SR_IMASK
sd $v0, THREAD_V0($k0)
sd $v1, THREAD_V1($k0)
sd $a0, THREAD_A0($k0)
sd $a1, THREAD_A1($k0)
sd $a2, THREAD_A2($k0)
sd $a3, THREAD_A3($k0)
sd $t3, THREAD_T3($k0)
sd $t4, THREAD_T4($k0)
sd $t5, THREAD_T5($k0)
sd $t6, THREAD_T6($k0)
sd $t7, THREAD_T7($k0)
sd $s0, THREAD_S0($k0)
sd $s1, THREAD_S1($k0)
sd $s2, THREAD_S2($k0)
sd $s3, THREAD_S3($k0)
sd $s4, THREAD_S4($k0)
sd $s5, THREAD_S5($k0)
sd $s6, THREAD_S6($k0)
sd $s7, THREAD_S7($k0)
sd $t8, THREAD_T8($k0)
sd $t9, THREAD_T9($k0)
sd $gp, THREAD_GP($k0)
sd $sp, THREAD_SP($k0)
sd $fp, THREAD_S8($k0)
sd $ra, THREAD_RA($k0)
beqz $t1, savercp
sd $t0, THREAD_HI($k0)
// If any CPU interrupts are enabled in the previous thread's SR, bitwise-OR in the
// disabled CPU interrupts from the global interrupt mask.
// This is an attempt at reverting the effect of masking the thread's SR with the
// global interrupt mask. This is however broken, see comments for osSetIntMask.
lui $t0, %hi(__OSGlobalIntMask)
addiu $t0, %lo(__OSGlobalIntMask)
lw $t0, ($t0)
li $at, ~0
xor $t2, $t0, $at
lui $at, ((~SR_IMASK) >> 0x10) & 0xFFFF
andi $t2, $t2, SR_IMASK
ori $at, (~SR_IMASK) & 0xFFFF
or $t4, $t1, $t2
and $t3, $k1, $at
andi $t0, $t0, SR_IMASK
or $t3, $t3, $t4
and $t1, $t1, $t0
and $k1, $k1, $at
sw $t3, THREAD_SR($k0)
or $k1, $k1, $t1
savercp:
// Save the currently masked RCP interrupts.
lui $t1, %hi(PHYS_TO_K1(MI_INTR_MASK_REG))
lw $t1, %lo(PHYS_TO_K1(MI_INTR_MASK_REG))($t1)
beqz $t1, endrcp
nop
// Similar to the above comment, but for RCP interrupt enable bits rather than CPU.
// This suffers from the same problem as above.
lui $t0, %hi(__OSGlobalIntMask)
addiu $t0, %lo(__OSGlobalIntMask)
lw $t0, ($t0)
lw $t4, THREAD_RCP($k0)
li $at, ~0
srl $t0, $t0, RCP_IMASKSHIFT
xor $t0, $t0, $at
andi $t0, $t0, (RCP_IMASK >> RCP_IMASKSHIFT)
and $t0, $t0, $t4
or $t1, $t1, $t0
endrcp:
sw $t1, THREAD_RCP($k0)
mfc0 $t0, C0_EPC
sw $t0, THREAD_PC($k0)
lw $t0, THREAD_FP($k0)
beqz $t0, handle_interrupt
nop
// Save FP Registers if FPU was used by the thread
cfc1 $t0, C1_FPCSR
nop
sw $t0, THREAD_FPCSR($k0)
sdc1 $f0, THREAD_FP0($k0)
sdc1 $f2, THREAD_FP2($k0)
sdc1 $f4, THREAD_FP4($k0)
sdc1 $f6, THREAD_FP6($k0)
sdc1 $f8, THREAD_FP8($k0)
sdc1 $f10, THREAD_FP10($k0)
sdc1 $f12, THREAD_FP12($k0)
sdc1 $f14, THREAD_FP14($k0)
sdc1 $f16, THREAD_FP16($k0)
sdc1 $f18, THREAD_FP18($k0)
sdc1 $f20, THREAD_FP20($k0)
sdc1 $f22, THREAD_FP22($k0)
sdc1 $f24, THREAD_FP24($k0)
sdc1 $f26, THREAD_FP26($k0)
sdc1 $f28, THREAD_FP28($k0)
sdc1 $f30, THREAD_FP30($k0)
handle_interrupt:
// Determine the cause of the exception or interrupt and
// enter appropriate handling routine
mfc0 $t0, C0_CAUSE
sw $t0, THREAD_CAUSE($k0)
li $t1, OS_STATE_RUNNABLE
sh $t1, THREAD_STATE($k0)
andi $t1, $t0, CAUSE_EXCMASK
// Test for break exception
li $t2, EXC_BREAK
beq $t1, $t2, handle_break
nop
// Test for CpU (coprocessor unusable) exception
li $t2, EXC_CPU
beq $t1, $t2, handle_CpU
nop
// Test for interrupt, if it's not an interrupt, panic
li $t2, EXC_INT
bne $t1, $t2, panic
nop
and $s0, $k1, $t0
next_interrupt:
// Handle external interrupt causes, using a jump table
// to enter into the appropriate handler
andi $t1, $s0, CAUSE_IPMASK
srl $t2, $t1, CAUSE_IPSHIFT + 4
bnez $t2, 1f
nop
srl $t2, $t1, CAUSE_IPSHIFT
addi $t2, $t2, 0x10
1:
lui $at, %hi(__osIntOffTable)
addu $at, $at, $t2
lbu $t2, %lo(__osIntOffTable)($at)
lui $at, %hi(__osIntTable)
addu $at, $at, $t2
lw $t2, %lo(__osIntTable)($at)
jr $t2
nop
/**
* IP6 Interrupt
* Only signalled by development hardware
*/
IP6_Hdlr:
// Mask out interrupt and continue
li $at, ~CAUSE_IP6
b next_interrupt
and $s0, $s0, $at
/**
* IP7 Interrupt
* Only signalled by development hardware
*/
IP7_Hdlr:
// Mask out interrupt and continue
li $at, ~CAUSE_IP7
b next_interrupt
and $s0, $s0, $at
/**
* IP8/Counter Interrupt
* Once the cop0 count register reaches the value of the
* cop0 compare register, this interrupt is triggered
*/
counter:
mfc0 $t1, C0_COMPARE
mtc0 $t1, C0_COMPARE
// Post counter message
jal send_mesg
li $a0, OS_EVENT_COUNTER*8
// Mask out interrupt and continue
li $at, ~CAUSE_IP8
b next_interrupt
and $s0, $s0, $at
/**
* IP4/Cartridge Interrupt
* Signalled by the N64 Disk Drive
*/
cart:
// Load cart callback set by __osSetHWIntrRoutine
lui $t1, %hi(__osHwIntTable)
addiu $t1, %lo(__osHwIntTable)
lw $t2, (OS_INTR_CART*HWINT_SIZE+HWINT_CALLBACK)($t1)
// Mask out interrupt
li $at, ~CAUSE_IP4
and $s0, $s0, $at
// If the callback is NULL, handling is done
beqz $t2, send_cart_mesg
addi $t1, $t1, (OS_INTR_CART*HWINT_SIZE)
// Set up a stack and run the callback
jalr $t2
lw $sp, HWINT_SP($t1)
beqz $v0, send_cart_mesg
nop
// Redispatch immediately if the callback returned nonzero
b redispatch
nop
send_cart_mesg:
// Post a cart event message
jal send_mesg
li $a0, OS_EVENT_CART*8
// Continue
b next_interrupt
nop
/**
* IP3/RCP Interrupt
* Signalled by the RCP for various reasons, described below
*/
rcp:
// Load the MI interrupts and mask with the RCP bits in the global interrupt mask
//! @bug this clobbers the t0 register which is expected to hold the value of the
//! C0_CAUSE register in the sw1 and sw2 handlers. If the sw1 or sw2 handler runs
//! after this, the interrupt will not be cleared properly.
lui $t0, %hi(__OSGlobalIntMask)
addiu $t0, %lo(__OSGlobalIntMask)
lw $t0, ($t0)
lui $s1, %hi(PHYS_TO_K1(MI_INTR_REG))
lw $s1, %lo(PHYS_TO_K1(MI_INTR_REG))($s1)
srl $t0, $t0, RCP_IMASKSHIFT
and $s1, $s1, $t0
/**
* Signal Processor (SP) Interrupt
*/
sp:
// Test for sp interrupt
andi $t1, $s1, MI_INTR_SP
beqz $t1, vi
nop
// Test for yielded or done signals in particular
lui $t4, %hi(PHYS_TO_K1(SP_STATUS_REG))
lw $t4, %lo(PHYS_TO_K1(SP_STATUS_REG))($t4)
li $t1, (SP_CLR_INTR | SP_CLR_SIG3)
lui $at, %hi(PHYS_TO_K1(SP_STATUS_REG))
andi $t4, $t4, (SP_STATUS_YIELDED | SP_STATUS_TASKDONE)
// Mask out SP interrupt
andi $s1, $s1, (MI_INTR_SI | MI_INTR_AI | MI_INTR_VI | MI_INTR_PI | MI_INTR_DP)
beqz $t4, sp_other_break
// Clear interrupt and signal 3
sw $t1, %lo(PHYS_TO_K1(SP_STATUS_REG))($at)
// Post an SP event message
jal send_mesg
li $a0, OS_EVENT_SP*8
beqz $s1, NoMoreRcpInts
nop
// Step over sp_other_break handler
b vi
nop
sp_other_break:
// An sp signal that is not due to yielding or task completion, such as
// an sp breakpoint. Post a different event message
jal send_mesg
li $a0, OS_EVENT_SP_BREAK*8
beqz $s1, NoMoreRcpInts
nop
/**
* Video Interface (VI) Interrupt
*/
vi:
// Test for vi interrupt
andi $t1, $s1, MI_INTR_VI
beqz $t1, ai
lui $at, %hi(PHYS_TO_K1(VI_CURRENT_REG))
// Mask out vi interrupt
andi $s1, $s1, (MI_INTR_SP | MI_INTR_SI | MI_INTR_AI | MI_INTR_PI | MI_INTR_DP)
// Clear interrupt
sw $zero, %lo(PHYS_TO_K1(VI_CURRENT_REG))($at)
// Post vi event message
jal send_mesg
li $a0, OS_EVENT_VI*8
beqz $s1, NoMoreRcpInts
nop
/**
* Audio Interface (AI) Interrupt
*/
ai:
// Test for ai interrupt
andi $t1, $s1, MI_INTR_AI
beqz $t1, si
nop
li $t1, 1
lui $at, %hi(PHYS_TO_K1(AI_STATUS_REG))
// Mask out ai interrupt
andi $s1, $s1, (MI_INTR_SP | MI_INTR_SI | MI_INTR_VI | MI_INTR_PI | MI_INTR_DP)
// Clear interrupt
sw $t1, %lo(PHYS_TO_K1(AI_STATUS_REG))($at)
// Post ai event message
jal send_mesg
li $a0, OS_EVENT_AI*8
beqz $s1, NoMoreRcpInts
nop
/**
* Serial Interface (SI) Interrupt
*/
si:
// Test for si interrupt
andi $t1, $s1, MI_INTR_SI
beqz $t1, pi
lui $at, %hi(PHYS_TO_K1(SI_STATUS_REG))
// Mask out si interrupt
andi $s1, $s1, (MI_INTR_SP | MI_INTR_AI | MI_INTR_VI | MI_INTR_PI | MI_INTR_DP)
// Clear interrupt
sw $zero, %lo(PHYS_TO_K1(SI_STATUS_REG))($at)
// Post si event message
jal send_mesg
li $a0, OS_EVENT_SI*8
beqz $s1, NoMoreRcpInts
nop
/**
* Parallel Interface (PI) Interrupt
*/
pi:
// Test for pi interrupt
andi $t1, $s1, MI_INTR_PI
beqz $t1, dp
nop
// Clear interrupt
li $t1, PI_STATUS_CLR_INTR
lui $at, %hi(PHYS_TO_K1(PI_STATUS_REG))
sw $t1, %lo(PHYS_TO_K1(PI_STATUS_REG))($at)
// Load pi callback
lui $t1, %hi(__osPiIntTable)
addiu $t1, %lo(__osPiIntTable)
lw $t2, HWINT_CALLBACK($t1)
// Mask out pi interrupt
andi $s1, $s1, (MI_INTR_SP | MI_INTR_SI | MI_INTR_AI | MI_INTR_VI | MI_INTR_DP)
// Skip callback if NULL
beqz $t2, no_pi_callback
nop
// Set up a stack and run the callback
lw $sp, HWINT_SP($t1)
jalr $t2
move $a0, $v0
// If the callback returns non-zero, don't post a pi event message
bnez $v0, skip_pi_mesg
nop
no_pi_callback:
// Post pi event message
jal send_mesg
li $a0, OS_EVENT_PI*8
skip_pi_mesg:
beqz $s1, NoMoreRcpInts
nop
/**
* Display Processor (DP) Interrupt
*/
dp:
// Test for dp interrupt
andi $t1, $s1, MI_INTR_DP
beqz $t1, NoMoreRcpInts
nop
// Clear dp interrupt
li $t1, MI_CLR_DP_INTR
lui $at, %hi(PHYS_TO_K1(MI_INIT_MODE_REG))
// Mask out dp interrupt
andi $s1, $s1, (MI_INTR_SP | MI_INTR_SI | MI_INTR_AI | MI_INTR_VI | MI_INTR_PI)
sw $t1, %lo(PHYS_TO_K1(MI_INIT_MODE_REG))($at)
// Post dp event message
jal send_mesg
li $a0, OS_EVENT_DP*8
NoMoreRcpInts:
// Mask out interrupt and continue
li $at, ~CAUSE_IP3
b next_interrupt
and $s0, $s0, $at
/**
* IP5/PreNMI Interrupt
* Reset button has been pressed
*/
prenmi:
// Disable IP5/PreNMI interrupt for the previously running thread
lw $k1, THREAD_SR($k0)
li $at, ~SR_IBIT5
lui $t1, %hi(__osShutdown)
and $k1, $k1, $at
sw $k1, THREAD_SR($k0)
addiu $t1, %lo(__osShutdown)
// Test __osShutdown for first PreNMI event
lw $t2, ($t1)
beqz $t2, firstnmi
li $at, ~CAUSE_IP5
// Mask out interrupt and redispatch immediately
b redispatch
and $s0, $s0, $at
firstnmi:
// Set __osShutdown
li $t2, 1
sw $t2, ($t1)
// Post a PreNMI event message
jal send_mesg
li $a0, OS_EVENT_PRENMI*8
// Mask out and disable IP5/PreNMI interrupt for the highest priority thread
lui $t2, %hi(__osRunQueue)
lw $t2, %lo(__osRunQueue)($t2)
li $at, ~SR_IBIT5
and $s0, $s0, $at
lw $k1, THREAD_SR($t2)
and $k1, $k1, $at
// Redispatch immediately
b redispatch
sw $k1, THREAD_SR($t2)
sw2:
// Mask out interrupt
li $at, ~CAUSE_SW2
and $t0, $t0, $at
mtc0 $t0, C0_CAUSE
// Post sw2 event message
jal send_mesg
li $a0, OS_EVENT_SW2*8
li $at, ~CAUSE_SW2
// Mask out interrupt and continue
b next_interrupt
and $s0, $s0, $at
sw1:
// Mask out interrupt
li $at, ~CAUSE_SW1
and $t0, $t0, $at
mtc0 $t0, C0_CAUSE
// Post sw1 event message
jal send_mesg
li $a0, OS_EVENT_SW1*8
li $at, ~CAUSE_SW1
// Mask out interrupt and continue
b next_interrupt
and $s0, $s0, $at
handle_break:
// Set last thread as having hit a break exception
li $t1, OS_FLAG_CPU_BREAK
sh $t1, THREAD_FLAGS($k0)
// Post a cpu break event message
jal send_mesg
li $a0, OS_EVENT_CPU_BREAK*8
// Redispatch
b redispatch
nop
redispatch:
lui $t2, %hi(__osRunQueue)
lw $t2, %lo(__osRunQueue)($t2)
// Get priority of previously running thread
lw $t1, THREAD_PRI($k0)
// Get highest priority from waiting threads
lw $t3, THREAD_PRI($t2)
slt $at, $t1, $t3
beqz $at, enqueueRunning
nop
// The previously running thread is no longer the highest priority,
// enqueue it to the run queue to wait its turn again
lui $a0, %hi(__osRunQueue)
move $a1, $k0
jal __osEnqueueThread
addiu $a0, $a0, %lo(__osRunQueue)
j __osDispatchThread
nop
/**
* Resume the previously running thread by placing it at the top of
* the run queue and dispatching it
*/
enqueueRunning:
lui $t1, %hi(__osRunQueue)
addiu $t1, $t1, %lo(__osRunQueue)
lw $t2, ($t1)
sw $t2, THREAD_NEXT($k0)
j __osDispatchThread
sw $k0, ($t1)
/**
* Unhandled exceptions & interrupts end up here,
* trap to software by posting a fault message
*/
panic:
// Mark the thread as having faulted
lui $at, %hi(__osFaultedThread)
sw $k0, %lo(__osFaultedThread)($at)
li $t1, OS_STATE_STOPPED
sh $t1, THREAD_STATE($k0)
li $t1, OS_FLAG_FAULT
sh $t1, THREAD_FLAGS($k0)
// Save C0_BADVADDR
mfc0 $t2, C0_BADVADDR
sw $t2, THREAD_BADVADDR($k0)
// Post the fault message
jal send_mesg
li $a0, OS_EVENT_FAULT*8
// Dispatch next thread
j __osDispatchThread
nop
/**
* Handles posting event messages to the listening message queue, if there is one
*/
send_mesg:
// Load pointer to listening message queue
lui $t2, %hi(__osEventStateTab)
addiu $t2, %lo(__osEventStateTab)
addu $t2, $t2, $a0
lw $t1, ($t2)
// Save return address
move $s2, $ra
// If there is no listening message queue, done
beqz $t1, send_done
nop
// Test if the message queue is full, if so don't post the message
lw $t3, MQ_VALIDCOUNT($t1)
lw $t4, MQ_MSGCOUNT($t1)
slt $at, $t3, $t4
beqz $at, send_done
nop
// Add validcount to first and modulo with msgcount
lw $t5, MQ_FIRST($t1)
addu $t5, $t5, $t3
// Modulo
div $zero, $t5, $t4
bnez $t4, 1f
nop
break 7 // div0
1:
li $at, -1
bne $t4, $at, 2f
li $at, -0x80000000
bne $t5, $at, 2f
nop
break 6 // overflow
2:
// End Modulo
lw $t4, MQ_MSG($t1)
mfhi $t5
sll $t5, $t5, 2
addu $t4, $t4, $t5
// Fetch the message to post
lw $t5, 4($t2)
addiu $t2, $t3, 1
// Post the message to the message queue
sw $t5, ($t4)
// Increment the validCount
sw $t2, MQ_VALIDCOUNT($t1)
// If there was a thread blocked on this message queue,
// wake it up
lw $t2, MQ_MTQUEUE($t1)
lw $t3, ($t2)
beqz $t3, send_done
nop
jal __osPopThread
move $a0, $t1
move $t2, $v0
lui $a0, %hi(__osRunQueue)
move $a1, $t2
jal __osEnqueueThread
addiu $a0, %lo(__osRunQueue)
send_done:
jr $s2
nop
/**
* Handle coprocessor unusable exception
*/
handle_CpU:
li $at, CAUSE_CEMASK
and $t1, $t0, $at
srl $t1, $t1, CAUSE_CESHIFT
li $t2, 1 // if not coprocessor 1, panic
bne $t1, $t2, panic
nop
// Mark cop1 as usable for previous thread
lw $k1, THREAD_SR($k0)
li $at, SR_CU1
li $t1, 1
or $k1, $k1, $at
sw $t1, THREAD_FP($k0)
b enqueueRunning
sw $k1, THREAD_SR($k0)
END(__osException)
/**
* void __osEnqueueAndYield(OSThread** threadQueue);
*
* Voluntary thread yielding.
* Enqueues the currently running thread to the top of the
* thread queue `threadQueue` and yields to the highest priority
* unblocked runnable thread.
*/
LEAF(__osEnqueueAndYield)
lui $a1, %hi(__osRunningThread)
lw $a1, %lo(__osRunningThread)($a1)
// Save SR
mfc0 $t0, C0_SR
lw $k1, THREAD_FP($a1)
ori $t0, $t0, SR_EXL
sw $t0, THREAD_SR($a1)
// Save callee-saved registers
sd $s0, THREAD_S0($a1)
sd $s1, THREAD_S1($a1)
sd $s2, THREAD_S2($a1)
sd $s3, THREAD_S3($a1)
sd $s4, THREAD_S4($a1)
sd $s5, THREAD_S5($a1)
sd $s6, THREAD_S6($a1)
sd $s7, THREAD_S7($a1)
sd $gp, THREAD_GP($a1)
sd $sp, THREAD_SP($a1)
sd $fp, THREAD_S8($a1)
sd $ra, THREAD_RA($a1)
// Save FPU callee-saved registers if the current thread has used the FPU
beqz $k1, 1f
sw $ra, THREAD_PC($a1)
cfc1 $k1, C1_FPCSR
sdc1 $f20, THREAD_FP20($a1)
sdc1 $f22, THREAD_FP22($a1)
sdc1 $f24, THREAD_FP24($a1)
sdc1 $f26, THREAD_FP26($a1)
sdc1 $f28, THREAD_FP28($a1)
sdc1 $f30, THREAD_FP30($a1)
sw $k1, THREAD_FPCSR($a1)
1:
lw $k1, THREAD_SR($a1)
andi $t1, $k1, SR_IMASK
beqz $t1, 2f
nop
// This code does the same thing as the block just above the `savercp` label.
// See the comment there for more about this.
lui $t0, %hi(__OSGlobalIntMask)
addiu $t0, %lo(__OSGlobalIntMask)
lw $t0, ($t0)
li $at, ~0
xor $t0, $t0, $at
lui $at, ((~SR_IMASK) >> 0x10) & 0xFFFF
andi $t0, $t0, SR_IMASK
ori $at, (~SR_IMASK) & 0xFFFF
or $t1, $t1, $t0
and $k1, $k1, $at
or $k1, $k1, $t1
sw $k1, THREAD_SR($a1)
2:
lui $k1, %hi(PHYS_TO_K1(MI_INTR_MASK_REG))
lw $k1, %lo(PHYS_TO_K1(MI_INTR_MASK_REG))($k1)
beqz $k1, 3f
nop
// This code does the same thing as the block just below the `savercp` label.
// See the comment there for more about this.
lui $k0, %hi(__OSGlobalIntMask)
addiu $k0, %lo(__OSGlobalIntMask)
lw $k0, ($k0)
lw $t0, THREAD_RCP($a1)
li $at, ~0
srl $k0, $k0, RCP_IMASKSHIFT
xor $k0, $k0, $at
andi $k0, $k0, (RCP_IMASK >> RCP_IMASKSHIFT)
and $k0, $k0, $t0
or $k1, $k1, $k0
3:
// If the specified thread queue is null, skip
// straight to dispatching
beqz $a0, no_enqueue
sw $k1, THREAD_RCP($a1)
jal __osEnqueueThread
nop
no_enqueue:
j __osDispatchThread
nop
END(__osEnqueueAndYield)
/**
* void __osEnqueueThread(OSThread** threadQueue, OSThread* thread);
*
* Enqueues `thread` to the thread queue `threadQueue`, inserted by priority
*/
LEAF(__osEnqueueThread)
lw $t8, ($a0)
lw $t7, THREAD_PRI($a1)
move $t9, $a0
lw $t6, THREAD_PRI($t8)
slt $at, $t6, $t7
// If the current highest priority thread is a lower priority than
// the new thread, skip searching the queue
bnez $at, 2f
nop
1:
// Search the queue for the position to insert the thread to maintain
// ordering by priority
move $t9, $t8
lw $t8, THREAD_NEXT($t8)
lw $t6, THREAD_PRI($t8)
slt $at, $t6, $t7
beqz $at, 1b
nop
2:
// Insert the thread into the queue
lw $t8, ($t9)
sw $t8, THREAD_NEXT($a1)
sw $a1, ($t9)
jr $ra
sw $a0, THREAD_QUEUE($a1)
END(__osEnqueueThread)
/**
* OSThread* __osPopThread(OSThread** threadQueue);
*
* Pops the highest priority thread from the top of the
* thread queue `threadQueue` and returns it
*/
LEAF(__osPopThread)
lw $v0, ($a0)
lw $t9, THREAD_NEXT($v0)
jr $ra
sw $t9, ($a0)
END(__osPopThread)
LEAF(__osNop)
jr $ra
nop
END(__osNop)
/**
* void __osDispatchThread(void);
*
* Dispatches the next thread to run after restoring the context
*/
LEAF(__osDispatchThread)
// Obtain highest priority thread from the active run queue
lui $a0, %hi(__osRunQueue)
jal __osPopThread
addiu $a0, $a0, %lo(__osRunQueue)
// Set thread as running
lui $at, %hi(__osRunningThread)
sw $v0, %lo(__osRunningThread)($at)
li $t0, OS_STATE_RUNNING
sh $t0, THREAD_STATE($v0)
// Restore SR, masking out any interrupts that are not also
// enabled in the global interrupt mask
move $k0, $v0
lui $t0, %hi(__OSGlobalIntMask)
lw $k1, THREAD_SR($k0)
addiu $t0, %lo(__OSGlobalIntMask)
lw $t0, ($t0)
lui $at, ((~SR_IMASK) >> 0x10) & 0xFFFF
andi $t1, $k1, SR_IMASK
ori $at, (~SR_IMASK) & 0xFFFF
andi $t0, $t0, SR_IMASK
and $t1, $t1, $t0
and $k1, $k1, $at
or $k1, $k1, $t1
mtc0 $k1, C0_SR
// Restore GPRs
ld $k1, THREAD_LO($k0)
ld $at, THREAD_AT($k0)
ld $v0, THREAD_V0($k0)
mtlo $k1
ld $k1, THREAD_HI($k0)
ld $v1, THREAD_V1($k0)
ld $a0, THREAD_A0($k0)
ld $a1, THREAD_A1($k0)
ld $a2, THREAD_A2($k0)
ld $a3, THREAD_A3($k0)
ld $t0, THREAD_T0($k0)
ld $t1, THREAD_T1($k0)
ld $t2, THREAD_T2($k0)
ld $t3, THREAD_T3($k0)
ld $t4, THREAD_T4($k0)
ld $t5, THREAD_T5($k0)
ld $t6, THREAD_T6($k0)
ld $t7, THREAD_T7($k0)
ld $s0, THREAD_S0($k0)
ld $s1, THREAD_S1($k0)
ld $s2, THREAD_S2($k0)
ld $s3, THREAD_S3($k0)
ld $s4, THREAD_S4($k0)
ld $s5, THREAD_S5($k0)
ld $s6, THREAD_S6($k0)
ld $s7, THREAD_S7($k0)
ld $t8, THREAD_T8($k0)
ld $t9, THREAD_T9($k0)
ld $gp, THREAD_GP($k0)
mthi $k1
ld $sp, THREAD_SP($k0)
ld $fp, THREAD_S8($k0)
ld $ra, THREAD_RA($k0)
// Move thread pc to EPC so that eret will return execution to where the thread left off
lw $k1, THREAD_PC($k0)
mtc0 $k1, C0_EPC
// Check if the FPU was used by this thread and if so also restore the FPU registers
lw $k1, THREAD_FP($k0)
beqz $k1, 1f
nop
lw $k1, THREAD_FPCSR($k0)
ctc1 $k1, C1_FPCSR
ldc1 $f0, THREAD_FP0($k0)
ldc1 $f2, THREAD_FP2($k0)
ldc1 $f4, THREAD_FP4($k0)
ldc1 $f6, THREAD_FP6($k0)
ldc1 $f8, THREAD_FP8($k0)
ldc1 $f10, THREAD_FP10($k0)
ldc1 $f12, THREAD_FP12($k0)
ldc1 $f14, THREAD_FP14($k0)
ldc1 $f16, THREAD_FP16($k0)
ldc1 $f18, THREAD_FP18($k0)
ldc1 $f20, THREAD_FP20($k0)
ldc1 $f22, THREAD_FP22($k0)
ldc1 $f24, THREAD_FP24($k0)
ldc1 $f26, THREAD_FP26($k0)
ldc1 $f28, THREAD_FP28($k0)
ldc1 $f30, THREAD_FP30($k0)
1:
// Restore RCP interrupt mask, masking out any RCP interrupts that
// are not also enabled in the global interrupt mask
lw $k1, THREAD_RCP($k0)
lui $k0, %hi(__OSGlobalIntMask)
addiu $k0, %lo(__OSGlobalIntMask)
lw $k0, ($k0)
srl $k0, $k0, RCP_IMASKSHIFT
and $k1, $k1, $k0
sll $k1, $k1, 1
lui $k0, %hi(__osRcpImTable)
addiu $k0, %lo(__osRcpImTable)
addu $k1, $k1, $k0
lhu $k1, ($k1)
lui $k0, %hi(PHYS_TO_K1(MI_INTR_MASK_REG))
addiu $k0, %lo(PHYS_TO_K1(MI_INTR_MASK_REG))
sw $k1, ($k0)
// Empty pipeline
nop
nop
nop
nop
// Resume thread execution
eret
END(__osDispatchThread)
/**
* void __osCleanupThread(void);
*
* When a thread entrypoint function returns, it returns to this function.
* This function is responsible for cleaning up the thread, signalling for the
* current thread to be destroyed.
*/
LEAF(__osCleanupThread)
jal osDestroyThread
move $a0, $zero
// Despite being a jal, this function does not return as the thread will have been destroyed
END(__osCleanupThread)

View file

@ -0,0 +1,14 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noreorder
.section .text
.balign 16
LEAF(__osGetCause)
mfc0 $v0, C0_CAUSE
jr $ra
nop
END(__osGetCause)

View file

@ -0,0 +1,14 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noreorder
.section .text
.balign 16
LEAF(osGetCount)
mfc0 $v0, C0_COUNT
jr $ra
nop
END(osGetCount)

View file

@ -0,0 +1,14 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noreorder
.section .text
.balign 16
LEAF(__osGetFpcCsr)
cfc1 $v0, C1_FPCSR
jr $ra
nop
END(__osGetFpcCsr)

14
src/libultra/os/getsr.s Normal file
View file

@ -0,0 +1,14 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noreorder
.section .text
.balign 16
LEAF(__osGetSR)
mfc0 $v0, C0_SR
jr $ra
nop
END(__osGetSR)

View file

@ -0,0 +1,53 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
#include "ultra64/thread.h"
.set noat
.set noreorder
.section .text
.balign 16
LEAF(__osDisableInt)
lui $t2, %hi(__OSGlobalIntMask)
addiu $t2, $t2, %lo(__OSGlobalIntMask)
lw $t3, ($t2)
andi $t3, $t3, SR_IMASK
mfc0 $t0, C0_SR
li $at, ~SR_IE
and $t1, $t0, $at
mtc0 $t1, C0_SR
andi $v0, $t0, SR_IE
lw $t0, ($t2)
andi $t0, $t0, SR_IMASK
beq $t0, $t3, No_Change_Global_Int
lui $t2, %hi(__osRunningThread)
//! @bug this addiu should be lw, it may never come up in practice as to reach this code
//! the CPU bits of __OSGlobalIntMask must have changed while this function is running.
addiu $t2, $t2, %lo(__osRunningThread)
lw $t1, THREAD_SR($t2)
andi $t2, $t1, SR_IMASK
and $t2, $t2, $t0
li $at, ~SR_IMASK
and $t1, $t1, $at
or $t1, $t1, $t2
li $at, ~SR_IE
and $t1, $t1, $at
mtc0 $t1, C0_SR
nop
nop
No_Change_Global_Int:
jr $ra
nop
END(__osDisableInt)
LEAF(__osRestoreInt)
mfc0 $t0, C0_SR
or $t0, $t0, $a0
mtc0 $t0, C0_SR
nop
nop
jr $ra
nop
END(__osRestoreInt)

View file

@ -0,0 +1,94 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noat
.set noreorder
.section .text
.balign 16
/**
* void osInvalDCache(void* vaddr, s32 nbytes);
*
* Invalidates the CPU Data Cache for `nbytes` at `vaddr`.
* The cache is not automatically synced with physical memory, so cache
* lines must be invalidated to ensure old data is not used in place of
* newly available data supplied by an external agent in a DMA operation.
*
* If `vaddr` is not aligned to a cache line boundary, or nbytes is not a
* multiple of the data cache line size (16 bytes) a larger region is
* invalidated.
*
* If the amount to invalidate is at least the data cache size (DCACHE_SIZE),
* the entire data cache is invalidated.
*/
LEAF(osInvalDCache)
// If the amount to invalidate is less than or equal to 0, return immediately
blez $a1, 3f
nop
// If the amount to invalidate is as large as or larger than
// the data cache size, invalidate all
li $t3, DCACHE_SIZE
sltu $at, $a1, $t3
beqz $at, 4f
nop
// Ensure end address doesn't wrap around and end up smaller
// than the start address
move $t0, $a0
addu $t1, $a0, $a1
sltu $at, $t0, $t1
beqz $at, 3f
nop
// Mask start with cache line
andi $t2, $t0, DCACHE_LINEMASK
// If mask is not zero, the start is not cache aligned
beqz $t2, 1f
addiu $t1, $t1, -DCACHE_LINESIZE
// Subtract mask result to align to cache line
subu $t0, $t0, $t2
// Hit-Writeback-Invalidate unaligned part
cache (CACH_PD | C_HWBINV), ($t0)
sltu $at, $t0, $t1
// If that's all there is to do, return early
beqz $at, 3f
nop
addiu $t0, $t0, DCACHE_LINESIZE
1:
// Mask end with cache line
andi $t2, $t1, DCACHE_LINEMASK
// If mask is not zero, the end is not cache aligned
beqz $t2, 1f
nop
// Subtract mask result to align to cache line
subu $t1, $t1, $t2
// Hit-Writeback-Invalidate unaligned part
cache (CACH_PD | C_HWBINV), DCACHE_LINESIZE($t1)
sltu $at, $t1, $t0
// If that's all there is to do, return early
bnez $at, 3f
nop
// Invalidate the rest
1:
// Hit-Invalidate
cache (CACH_PD | C_HINV), ($t0)
sltu $at, $t0, $t1
bnez $at, 1b
addiu $t0, $t0, DCACHE_LINESIZE
3:
jr $ra
nop
4:
li $t0, K0BASE
addu $t1, $t0, $t3
addiu $t1, $t1, -DCACHE_LINESIZE
5:
// Index-Writeback-Invalidate
cache (CACH_PD | C_IWBINV), ($t0)
sltu $at, $t0, $t1
bnez $at, 5b
addiu $t0, DCACHE_LINESIZE
jr $ra
nop
END(osInvalDCache)

View file

@ -0,0 +1,52 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noat
.set noreorder
.section .text
.balign 16
LEAF(osInvalICache)
// If the amount to invalidate is less than or equal to 0, return immediately
blez $a1, 2f
nop
// If the amount to invalidate is as large as or larger than
// the instruction cache size, invalidate all
li $t3, ICACHE_SIZE
sltu $at, $a1, $t3
beqz $at, 3f
nop
// ensure end address doesn't wrap around and end up smaller
// than the start address
move $t0, $a0
addu $t1, $a0, $a1
sltu $at, $t0, $t1
beqz $at, 2f
nop
// Mask and subtract to align to cache line
andi $t2, $t0, ICACHE_LINEMASK
addiu $t1, $t1, -ICACHE_LINESIZE
subu $t0, $t0, $t2
1:
cache (CACH_PI | C_HINV), ($t0)
sltu $at, $t0, $t1
bnez $at, 1b
addiu $t0, $t0, ICACHE_LINESIZE
2:
jr $ra
nop
3:
li $t0, K0BASE
addu $t1, $t0, $t3
addiu $t1, $t1, -ICACHE_LINESIZE
4:
cache (CACH_PI | C_IINV), ($t0)
sltu $at, $t0, $t1
bnez $at, 4b
addiu $t0, ICACHE_LINESIZE
jr $ra
nop
END(osInvalICache)

View file

@ -0,0 +1,36 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
#include "ultra64/rdb.h"
.set noreorder
.section .text
.balign 16
LEAF(osMapTLBRdb)
mfc0 $t0, C0_ENTRYHI
li $t1, NTLBENTRIES
mtc0 $t1, C0_INX
mtc0 $zero, C0_PAGEMASK
li $t2, (TLBLO_UNCACHED | TLBLO_D | TLBLO_V | TLBLO_G)
li $t1, (RDB_BASE_REG & TLBHI_VPN2MASK)
mtc0 $t1, C0_ENTRYHI
// Possible bug? Virtual address instead of physical address
// set as page frame number
li $t1, RDB_BASE_VIRTUAL_ADDR
srl $t3, $t1, TLBLO_PFNSHIFT
or $t3, $t3, $t2
mtc0 $t3, C0_ENTRYLO0
li $t1, TLBLO_G
mtc0 $t1, C0_ENTRYLO1
nop
tlbwi
nop
nop
nop
nop
mtc0 $t0, C0_ENTRYHI
jr $ra
nop
END(osMapTLBRdb)

View file

@ -0,0 +1,22 @@
#include "ultra64/asm.h"
.section .text
.macro IPL_SYMBOL name, address, size
.global \name
.set \name, \address
.type \name, @object
.size \name, \size
.endm
IPL_SYMBOL leoBootID, 0x800001A0, 4
IPL_SYMBOL osTvType, 0x80000300, 4
IPL_SYMBOL osRomType, 0x80000304, 4
IPL_SYMBOL osRomBase, 0x80000308, 4
IPL_SYMBOL osResetType, 0x8000030C, 4
IPL_SYMBOL osCicId, 0x80000310, 4
IPL_SYMBOL osVersion, 0x80000314, 4
IPL_SYMBOL osMemSize, 0x80000318, 4
IPL_SYMBOL osAppNMIBuffer, 0x8000031C, 0x40
.fill 0x60

View file

@ -0,0 +1,86 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noat
.set noreorder
.section .text
.balign 16
/**
* u32 __osProbeTLB(void* vaddr);
*
* Searches the TLB for the physical address associated with
* the virtual address `vaddr`.
*
* Returns the physical address if found, or -1 if not found.
*/
LEAF(__osProbeTLB)
// Set C0_ENTRYHI based on supplied vaddr
mfc0 $t0, C0_ENTRYHI
andi $t1, $t0, TLBHI_PIDMASK
li $at, TLBHI_VPN2MASK
and $t2, $a0, $at
or $t1, $t1, $t2
mtc0 $t1, C0_ENTRYHI
nop
nop
nop
// TLB probe, sets C0_INX to a value matching C0_ENTRYHI.
// If no match is found the TLBINX_PROBE bit is set to indicate this.
tlbp
nop
nop
// Read result
mfc0 $t3, C0_INX
li $at, TLBINX_PROBE
and $t3, $t3, $at
// Branch if no match was found
bnez $t3, 3f
nop
// Read TLB, sets C0_ENTRYHI, C0_ENTRYLO0, C0_ENTRYLO1 and C0_PAGEMASK for the TLB
// entry indicated by C0_INX
tlbr
nop
nop
nop
// Calculate page size = (page mask + 0x2000) >> 1
mfc0 $t3, C0_PAGEMASK
addi $t3, $t3, 0x2000
srl $t3, $t3, 1
// & with vaddr
and $t4, $t3, $a0
// Select C0_ENTRYLO0 or C0_ENTRYLO1
bnez $t4, 1f
addi $t3, $t3, -1 // make bitmask out of page size
mfc0 $v0, C0_ENTRYLO0
b 2f
nop
1:
mfc0 $v0, C0_ENTRYLO1
2:
// Check valid bit and branch if not valid
andi $t5, $v0, TLBLO_V
beqz $t5, 3f
nop
// Extract the Page Frame Number from the entry
li $at, TLBLO_PFNMASK
and $v0, $v0, $at
sll $v0, $v0, TLBLO_PFNSHIFT
// Mask vaddr with page size mask
and $t5, $a0, $t3
// Add masked vaddr to pfn to obtain the physical address
add $v0, $v0, $t5
b 4f
nop
3:
// No physical address for the supplied virtual address was found,
// return -1
li $v0, -1
4:
// Restore original C0_ENTRYHI value before returning
mtc0 $t0, C0_ENTRYHI
jr $ra
nop
END(__osProbeTLB)

View file

@ -0,0 +1,14 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noreorder
.section .text
.balign 16
LEAF(__osSetCompare)
mtc0 $a0, C0_COMPARE
jr $ra
nop
END(__osSetCompare)

View file

@ -0,0 +1,15 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noreorder
.section .text
.balign 16
LEAF(__osSetFpcCsr)
cfc1 $v0, C1_FPCSR
ctc1 $a0, C1_FPCSR
jr $ra
nop
END(__osSetFpcCsr)

View file

@ -0,0 +1,168 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
#include "ultra64/rcp.h"
#include "ultra64/exception.h"
.set noat
.set noreorder
.section .rodata
.balign 16
/**
* LUT to convert between an interrupt mask value and a value for MI_INTR_MASK_REG.
* The interrupt mask value is a single bit 0 = disabled, 1 = enabled, while writes to MI_INTR_MASK_REG has two distinct non-zero
* values for set and clear, hence the need for a conversion step.
*/
DATA(__osRcpImTable)
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
.half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP
ENDDATA(__osRcpImTable)
.section .text
.balign 16
/**
* OSIntMask osSetIntMask(OSIntMask);
*
* Sets the interrupt enable mask for the current thread. External interrupts
* originating either in the CPU or the RCP may be "masked out" so that they
* are not handled. This is sometimes important for critical code sections
* that must not be interrupted.
* Interrupts that are not enabled in the global interrupt mask __OSGlobalIntMask
* cannot be set here. The global interrupt mask is OS-internal and is not
* expected to change during runtime.
* The returned value is the previous interrupt enable mask so that it can be
* restored later.
*
* @bug Some usage of the global interrupt mask is broken both in here and in the
* exception handler routines.
* While a thread is running, the C0_SR interrupt enable bits contain the
* interrupt enable bits for the current thread masked by the global
* interrupt mask. There is an attempt to recover only the original interrupt
* enable bits belonging to the thread itself using the operation
* (SR | ~__OSGlobalIntMask).
* However, this does not work as intended and can cause interrupts to end
* up enabled when not intended to be. The same issue is present for the
* RCP interrupt enable bits in MI_INTR_MASK_REG.
* This does not cause issues in practice as __OSGlobalIntMask is almost always
* OS_IM_ALL, so the operation is usually simply (SR | 0).
*/
LEAF(osSetIntMask)
// Extract interrupt enable bits from current SR
mfc0 $t4, C0_SR
andi $v0, $t4, (SR_IMASK | SR_IE)
// Get value of __OSGlobalIntMask
lui $t0, %hi(__OSGlobalIntMask)
addiu $t0, %lo(__OSGlobalIntMask)
lw $t3, ($t0)
// Bitwise-OR in the disabled CPU bits of __OSGlobalIntMask
li $at, ~0
xor $t0, $t3, $at
andi $t0, $t0, SR_IMASK
or $v0, $v0, $t0
// Fetch MI_INTR_MASK_REG
lui $t2, %hi(PHYS_TO_K1(MI_INTR_MASK_REG))
lw $t2, %lo(PHYS_TO_K1(MI_INTR_MASK_REG))($t2)
// If there are RCP interrupts masked
beqz $t2, 1f
srl $t1, $t3, RCP_IMASKSHIFT
// Bitwise-OR in the disabled RCP bits of __OSGlobalIntMask
li $at, ~0
xor $t1, $t1, $at
andi $t1, $t1, (RCP_IMASK >> RCP_IMASKSHIFT)
or $t2, $t2, $t1
1:
// Shift the RCP bits to not conflict with the CPU bits
sll $t2, $t2, RCP_IMASKSHIFT
// OR the CPU and RCP bits together
or $v0, $v0, $t2
// Extract RCP interrupt enable bits from requested mask and mask with __OSGlobalIntMask
li $at, RCP_IMASK
and $t0, $a0, $at
and $t0, $t0, $t3
// Convert to a value for MI_INTR_MASK_REG and set it
srl $t0, $t0, (RCP_IMASKSHIFT-1)
lui $t2, %hi(__osRcpImTable)
addu $t2, $t2, $t0
lhu $t2, %lo(__osRcpImTable)($t2)
lui $at, %hi(PHYS_TO_K1(MI_INTR_MASK_REG))
sw $t2, %lo(PHYS_TO_K1(MI_INTR_MASK_REG))($at)
// Extract CPU interrupt enable bits from requested mask and mask with __OSGlobalIntMask
andi $t0, $a0, OS_IM_CPU
andi $t1, $t3, SR_IMASK
and $t0, $t0, $t1
li $at, ~SR_IMASK
and $t4, $t4, $at
// Bitwise OR in the remaining bits of SR and set new SR
or $t4, $t4, $t0
mtc0 $t4, C0_SR
nop
nop
jr $ra
nop
END(osSetIntMask)

15
src/libultra/os/setsr.s Normal file
View file

@ -0,0 +1,15 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noreorder
.section .text
.balign 16
LEAF(__osSetSR)
mtc0 $a0, C0_SR
nop
jr $ra
nop
END(__osSetSR)

View file

@ -0,0 +1,15 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noreorder
.section .text
.balign 16
LEAF(__osSetWatchLo)
mtc0 $a0, C0_WATCHLO
nop
jr $ra
nop
END(__osSetWatchLo)

View file

@ -0,0 +1,29 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noreorder
.section .text
.balign 16
LEAF(osUnmapTLBAll)
mfc0 $t0, C0_ENTRYHI
li $t1, (NTLBENTRIES - 1)
li $t2, (K0BASE & TLBHI_VPN2MASK)
mtc0 $t2, C0_ENTRYHI
mtc0 $zero, C0_ENTRYLO0
mtc0 $zero, C0_ENTRYLO1
1:
mtc0 $t1, C0_INX
nop
tlbwi
nop
nop
addi $t1, $t1, -1
bgez $t1, 1b
nop
mtc0 $t0, C0_ENTRYHI
jr $ra
nop
END(osUnmapTLBAll)

View file

@ -0,0 +1,60 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noat
.set noreorder
.section .text
.balign 16
/**
* void osWritebackDCache(void* vaddr, s32 nbytes);
*
* Writes back the contents of the data cache to main memory for `nbytes` at `vaddr`.
* If `nbytes` is as large as or larger than the data cache size, the entire cache is
* written back.
*/
LEAF(osWritebackDCache)
// If the amount to write back is less than or equal to 0, return immediately
blez $a1, .ret
nop
// If the amount to write back is as large as or larger than
// the data cache size, write back all
li $t3, DCACHE_SIZE
sltu $at, $a1, $t3
beqz $at, .all
nop
// ensure end address doesn't wrap around and end up smaller
// than the start address
move $t0, $a0
addu $t1, $a0, $a1
sltu $at, $t0, $t1
beqz $at, .ret
nop
// Mask and subtract to align to cache line
andi $t2, $t0, DCACHE_LINEMASK
addiu $t1, $t1, -DCACHE_LINESIZE
subu $t0, $t0, $t2
1:
cache (CACH_PD | C_HWB), ($t0)
sltu $at, $t0, $t1
bnez $at, 1b
addiu $t0, $t0, DCACHE_LINESIZE
.ret:
jr $ra
nop
// same as osWritebackDCacheAll in operation
.all:
li $t0, K0BASE
addu $t1, $t0, $t3
addiu $t1, $t1, -DCACHE_LINESIZE
1:
cache (CACH_PD | C_IWBINV), ($t0)
sltu $at, $t0, $t1
bnez $at, 1b
addiu $t0, DCACHE_LINESIZE
jr $ra
nop
END(osWritebackDCache)

View file

@ -0,0 +1,23 @@
#include "ultra64/asm.h"
#include "ultra64/r4300.h"
.set noat
.set noreorder
.section .text
.balign 16
LEAF(osWritebackDCacheAll)
li $t0, K0BASE
li $t2, DCACHE_SIZE
addu $t1, $t0, $t2
addiu $t1, $t1, -DCACHE_LINESIZE
1:
cache (CACH_PD | C_IWBINV), ($t0)
sltu $at, $t0, $t1
bnez $at, 1b
addiu $t0, DCACHE_LINESIZE
jr $ra
nop
END(osWritebackDCacheAll)