summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTharo <17233964+Thar0@users.noreply.github.com>2022-06-03 21:33:18 +0100
committerGitHub <noreply@github.com>2022-06-03 16:33:18 -0400
commit4775fd4a7ea8077f603900def093124f8aa7cec6 (patch)
tree23d5e36dbe6283a58760136602706bdf68893e1b /src
parente989cb7ace48435ef1de1ea10f8e81c61a8a46e2 (diff)
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
Diffstat (limited to 'src')
-rw-r--r--src/boot/mio0.s64
-rw-r--r--src/code/fp.s165
-rw-r--r--src/code/kanread.s216
-rw-r--r--src/libultra/gu/libm_vals.s9
-rw-r--r--src/libultra/libc/bcmp.s93
-rw-r--r--src/libultra/libc/bcopy.s233
-rw-r--r--src/libultra/libc/bzero.s65
-rw-r--r--src/libultra/mgu/mtxf2l.s40
-rw-r--r--src/libultra/mgu/mtxident.s29
-rw-r--r--src/libultra/mgu/mtxidentf.s28
-rw-r--r--src/libultra/mgu/mtxl2f.s41
-rw-r--r--src/libultra/mgu/normalize.s31
-rw-r--r--src/libultra/mgu/scale.s52
-rw-r--r--src/libultra/mgu/translate.s61
-rw-r--r--src/libultra/os/exceptasm.s982
-rw-r--r--src/libultra/os/getcause.s14
-rw-r--r--src/libultra/os/getcount.s14
-rw-r--r--src/libultra/os/getfpccsr.s14
-rw-r--r--src/libultra/os/getsr.s14
-rw-r--r--src/libultra/os/interrupt.s53
-rw-r--r--src/libultra/os/invaldcache.s94
-rw-r--r--src/libultra/os/invalicache.s52
-rw-r--r--src/libultra/os/maptlbrdb.s36
-rw-r--r--src/libultra/os/parameters.s22
-rw-r--r--src/libultra/os/probetlb.s86
-rw-r--r--src/libultra/os/setcompare.s14
-rw-r--r--src/libultra/os/setfpccsr.s15
-rw-r--r--src/libultra/os/setintmask.s168
-rw-r--r--src/libultra/os/setsr.s15
-rw-r--r--src/libultra/os/setwatchlo.s15
-rw-r--r--src/libultra/os/unmaptlball.s29
-rw-r--r--src/libultra/os/writebackdcache.s60
-rw-r--r--src/libultra/os/writebackdcacheall.s23
-rw-r--r--src/makerom/entry.s34
-rw-r--r--src/makerom/ipl3.s4
-rw-r--r--src/makerom/rom_header.s18
36 files changed, 2903 insertions, 0 deletions
diff --git a/src/boot/mio0.s b/src/boot/mio0.s
new file mode 100644
index 000000000..a3fb8eba2
--- /dev/null
+++ b/src/boot/mio0.s
@@ -0,0 +1,64 @@
+#include "ultra64/asm.h"
+
+.set noreorder
+
+.section .text
+
+.balign 16
+
+/**
+ * void Mio0_Decompress(void* src, void* dst);
+ *
+ * Decompress Mio0 chunk
+ */
+LEAF(Mio0_Decompress)
+ lw $a3, 8($a0) // compressed offset
+ lw $t9, 0xC($a0) // uncompressed offset
+ lw $t8, 4($a0) // decompressed length
+ add $a3, $a3, $a0 // compressed start
+ add $t9, $t9, $a0 // uncompressed start
+ move $a2, $zero // 0
+ addi $a0, $a0, 0x10 // move past header
+ add $t8, $t8, $a1 // dst + decompressed length = end
+mainloop:
+ bnez $a2, 1f
+ nop
+ lw $t0, ($a0)
+ li $a2, 32
+ addi $a0, $a0, 4
+1:
+ slt $t1, $t0, $zero
+ beqz $t1, read_comp
+ nop
+ lb $t2, ($t9) // read 1 byte from uncompressed data
+ addi $t9, $t9, 1 // advance uncompressed start
+ addi $a1, $a1, 1
+ b next_iter
+ sb $t2, -1($a1) // store uncompressed byte
+read_comp:
+ lhu $t2, ($a3) // read 2 bytes from compressed data
+ addi $a3, $a3, 2 // advance compressed start
+ srl $t3, $t2, 0xC
+ andi $t2, $t2, 0xFFF
+ beqz $t3, 3f
+ sub $t1, $a1, $t2
+ addi $t3, $t3, 2
+2:
+ lb $t2, -1($t1)
+ addi $t3, $t3, -1
+ addi $t1, $t1, 1
+ addi $a1, $a1, 1
+ bnez $t3, 2b
+ sb $t2, -1($a1)
+next_iter:
+ sll $t0, $t0, 1
+ bne $a1, $t8, mainloop // continue until decompressed length is reached
+ addi $a2, $a2, -1
+ jr $ra
+ nop
+3:
+ lbu $t3, ($t9)
+ addi $t9, $t9, 1
+ b 2b
+ addi $t3, $t3, 0x12
+END(Mio0_Decompress)
diff --git a/src/code/fp.s b/src/code/fp.s
new file mode 100644
index 000000000..431348d89
--- /dev/null
+++ b/src/code/fp.s
@@ -0,0 +1,165 @@
+#include "ultra64/asm.h"
+
+.set noreorder
+
+.section .data
+
+.balign 16
+
+DATA(qNaN0x3FFFFF)
+ .word 0x7FBFFFFF
+ENDDATA(qNaN0x3FFFFF)
+
+DATA(qNaN0x10000)
+ .word 0x7F810000
+ENDDATA(qNaN0x10000)
+
+DATA(sNaN0x3FFFFF)
+ .word 0x7FFFFFFF
+ENDDATA(sNaN0x3FFFFF)
+
+.section .text
+
+.balign 16
+
+LEAF(floorf)
+ floor.w.s $f12, $f12
+ jr $ra
+ cvt.s.w $f0, $f12
+END(floorf)
+
+LEAF(floor)
+ floor.w.d $f12, $f12
+ jr $ra
+ cvt.d.w $f0, $f12
+END(floor)
+
+LEAF(lfloorf)
+ floor.w.s $f4, $f12
+ mfc1 $v0, $f4
+ jr $ra
+ nop
+END(lfloorf)
+
+LEAF(lfloor)
+ floor.w.d $f4, $f12
+ mfc1 $v0, $f4
+ jr $ra
+ nop
+END(lfloor)
+
+LEAF(ceilf)
+ ceil.w.s $f12, $f12
+ jr $ra
+ cvt.s.w $f0, $f12
+END(ceilf)
+
+LEAF(ceil)
+ ceil.w.d $f12, $f12
+ jr $ra
+ cvt.d.w $f0, $f12
+END(ceil)
+
+LEAF(lceilf)
+ ceil.w.s $f4, $f12
+ mfc1 $v0, $f4
+ jr $ra
+ nop
+END(lceilf)
+
+LEAF(lceil)
+ ceil.w.d $f4, $f12
+ mfc1 $v0, $f4
+ jr $ra
+ nop
+END(lceil)
+
+LEAF(truncf)
+ trunc.w.s $f12, $f12
+ jr $ra
+ cvt.s.w $f0, $f12
+END(truncf)
+
+LEAF(trunc)
+ trunc.w.d $f12, $f12
+ jr $ra
+ cvt.d.w $f0, $f12
+END(trunc)
+
+LEAF(ltruncf)
+ trunc.w.s $f4, $f12
+ mfc1 $v0, $f4
+ jr $ra
+ nop
+END(ltruncf)
+
+LEAF(ltrunc)
+ trunc.w.d $f4, $f12
+ mfc1 $v0, $f4
+ jr $ra
+ nop
+END(ltrunc)
+
+LEAF(nearbyintf)
+ round.w.s $f12, $f12
+ jr $ra
+ cvt.s.w $f0, $f12
+END(nearbyintf)
+
+LEAF(nearbyint)
+ round.w.d $f12, $f12
+ jr $ra
+ cvt.d.w $f0, $f12
+END(nearbyint)
+
+LEAF(lnearbyintf)
+ round.w.s $f4, $f12
+ mfc1 $v0, $f4
+ jr $ra
+ nop
+END(lnearbyintf)
+
+LEAF(lnearbyint)
+ round.w.d $f4, $f12
+ mfc1 $v0, $f4
+ jr $ra
+ nop
+END(lnearbyint)
+
+LEAF(roundf)
+ li.s $f4, 0.5
+ nop
+ add.s $f0, $f12, $f4
+ floor.w.s $f0, $f0
+ jr $ra
+ cvt.s.w $f0, $f0
+END(roundf)
+
+LEAF(round)
+ li.d $f4, 0.5
+ nop
+ add.d $f0, $f12, $f4
+ floor.w.d $f0, $f0
+ jr $ra
+ cvt.d.w $f0, $f0
+END(round)
+
+LEAF(lroundf)
+ li.s $f4, 0.5
+ nop
+ add.s $f0, $f12, $f4
+ floor.w.s $f0, $f0
+ mfc1 $v0, $f0
+ jr $ra
+ nop
+END(lroundf)
+
+LEAF(lround)
+ li.d $f4, 0.5
+ nop
+ add.d $f0, $f12, $f4
+ floor.w.d $f0, $f0
+ mfc1 $v0, $f0
+ jr $ra
+ nop
+END(lround)
diff --git a/src/code/kanread.s b/src/code/kanread.s
new file mode 100644
index 000000000..70a884870
--- /dev/null
+++ b/src/code/kanread.s
@@ -0,0 +1,216 @@
+#include "ultra64/asm.h"
+
+.set noat
+.set noreorder
+
+.section .text
+
+.balign 16
+
+/**
+ * s32 Kanji_OffsetFromShiftJIS(s32 sjis);
+ *
+ * Returns the offset of the glyph texture data in the file `kanji` corresponding
+ * to a given 2-byte Shift-JIS codepoint. No range validity check is carried out.
+ *
+ * A nice Shift-JIS codepoint table: https://uic.io/en/charset/show/shift_jis/
+ * The file `kanji` contains the 'Level 1' kanji (0x889F-0x9872), and a reworked
+ * version of the non-kanji section that includes extra English and Hylian glyphs.
+ *
+ * @note This function assumes that its argument is a valid Shift-JIS codepoint;
+ * there is no range protection at all.
+ *
+ * @param sjis Shift-JIS Codepoint of glyph.
+ * @return s32 offset into `kanji` file.
+ *
+ * @remark Original name: "LeoGetKadr"
+ */
+LEAF(Kanji_OffsetFromShiftJIS)
+ // Characters with codepoints >= 0x8800 are kanji. Arrangement is regular,
+ // so convert index directly.
+ li $at, 0x8800
+ slt $at, $a0, $at
+ bnez $at, .nonkanji
+ // 0xBC is number of glyphs in one block in the `kanji` file:
+ // 0x100 possible codepoints with the same byte1
+ // - 0x40 unused at beginning
+ // - 1 unused at 0x7F
+ // - 3 unused at 0xFD, 0xFE, 0xFF
+ li $a2, 0xBC
+ // Get byte1 and adjust so starts at 0
+ srl $a1, $a0, 8
+ addi $a1, $a1, -0x88
+ multu $a2, $a1
+ // Get byte2 and adjust so starts at 0
+ andi $a3, $a0, 0xFF
+ addi $a3, $a3, -0x40
+ slti $at, $a3, (0x80 - 0x40)
+ mflo $a2
+ // 0x__7F is always empty and elided in the file, so decrement if larger
+ bnezl $at, .kanji_lower_halfblock
+ mflo $a2
+ addi $a3, $a3, -1
+ mflo $a2
+.kanji_lower_halfblock:
+ addi $a3, $a3, 0x30A
+ add $a3, $a3, $a2
+ jr $ra
+ sll $v0, $a3, 7
+ // returns (0x30A + (adjusted byte2) + (adjusted byte1) * 0xBC) * FONT_CHAR_TEX_SIZE
+
+// Non-kanji are arranged with irregular gaps, use the lookup table.
+.nonkanji:
+ // Get byte1 and adjust so starts at 0
+ srl $a1, $a0, 8
+ addi $a1, $a1, -0x81
+ multu $a2, $a1
+ // Get byte2 and adjust so starts at 0
+ andi $a3, $a0, 0xFF
+ addi $a3, $a3, -0x40
+ slti $at, $a3, (0x80 - 0x40)
+ mflo $a2
+ // 0x__7F is always empty and elided in the file, so decrement if larger
+ bnezl $at, .nonkanji_lower_halfblock
+ mflo $a2
+ addi $a3, $a3, -1
+ mflo $a2
+.nonkanji_lower_halfblock:
+ add $a3, $a3, $a2
+ lui $a2, %hi(sNonKanjiIndices)
+ sll $a3, $a3, 1
+ addiu $a2, %lo(sNonKanjiIndices)
+ add $a3, $a3, $a2
+ lh $a2, ($a3)
+ jr $ra
+ sll $v0, $a2, 7
+ // returns sNonKanjiIndices[(adjusted byte2) + (adjusted byte1) * 0xBC] * FONT_CHAR_TEX_SIZE
+END(Kanji_OffsetFromShiftJIS)
+
+/**
+ * Nearly equivalent C code (Equivalent for all valid input, will behave differently on overflow due to the use of `add` in the original):
+ *
+ * extern u16 sNonKanjiIndices[];
+ *
+ * s32 Kanji_OffsetFromShiftJIS(s32 sjis) {
+ * u32 byte1 = (u32)sjis >> 8;
+ * u32 byte2 = sjis & 0xFF;
+ *
+ * byte2 -= 0x40;
+ * if (byte2 >= 0x40) {
+ * byte2--;
+ * }
+ *
+ * if (sjis >= 0x8800) {
+ * byte1 -= 0x88;
+ * return (0x30A + byte2 + byte1 * 0xBC) * FONT_CHAR_TEX_SIZE;
+ * } else {
+ * byte1 -= 0x81;
+ * return sNonKanjiIndices[byte2 + byte1 * 0xBC] * FONT_CHAR_TEX_SIZE;
+ * }
+ * }
+ */
+
+/**
+ * Index table, formatting follows conventional Shift-JIS character table layout,
+ * with space for the missing 0x__7F,0x__FD,0x__FE,0x__FF entries, and split
+ * into blocks by high byte.
+ */
+DATA(sNonKanjiIndices)
+// 0x___0 0x___1 0x___2 0x___3 0x___4 0x___5 0x___6 0x___7 0x___8 0x___9 0x___A 0x___B 0x___C 0x___D 0x___E 0x___F
+/* 0x814_ */ .half 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F
+/* 0x815_ */ .half 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F
+/* 0x816_ */ .half 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F
+/* 0x817_ */ .half 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E
+/* 0x818_ */ .half 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E
+/* 0x819_ */ .half 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E
+/* 0x81A_ */ .half 0x005F, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x0000, 0x0000, 0x0000
+/* 0x81B_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073
+/* 0x81C_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x0000
+/* 0x81D_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F
+/* 0x81E_ */ .half 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x81F_ */ .half 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0000, 0x0000, 0x0000, 0x0000, 0x0091
+
+// 0x___0 0x___1 0x___2 0x___3 0x___4 0x___5 0x___6 0x___7 0x___8 0x___9 0x___A 0x___B 0x___C 0x___D 0x___E 0x___F
+/* 0x824_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0092
+/* 0x825_ */ .half 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x826_ */ .half 0x009C, 0x009D, 0x009E, 0x009F, 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB
+/* 0x827_ */ .half 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x828_ */ .half 0x0000, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4
+/* 0x829_ */ .half 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x0000, 0x0000, 0x0000, 0x0000, 0x00D0
+/* 0x82A_ */ .half 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0
+/* 0x82B_ */ .half 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0
+/* 0x82C_ */ .half 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, 0x0100
+/* 0x82D_ */ .half 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F, 0x0110
+/* 0x82E_ */ .half 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F, 0x0120
+/* 0x82F_ */ .half 0x0121, 0x0122, 0x0123, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+
+// 0x___0 0x___1 0x___2 0x___3 0x___4 0x___5 0x___6 0x___7 0x___8 0x___9 0x___A 0x___B 0x___C 0x___D 0x___E 0x___F
+/* 0x834_ */ .half 0x0124, 0x0125, 0x0126, 0x0127, 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, 0x0130, 0x0131, 0x0132, 0x0133
+/* 0x835_ */ .half 0x0134, 0x0135, 0x0136, 0x0137, 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x013F, 0x0140, 0x0141, 0x0142, 0x0143
+/* 0x836_ */ .half 0x0144, 0x0145, 0x0146, 0x0147, 0x0148, 0x0149, 0x014A, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F, 0x0150, 0x0151, 0x0152, 0x0153
+/* 0x837_ */ .half 0x0154, 0x0155, 0x0156, 0x0157, 0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F, 0x0160, 0x0161, 0x0162
+/* 0x838_ */ .half 0x0163, 0x0164, 0x0165, 0x0166, 0x0167, 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F, 0x0170, 0x0171, 0x0172
+/* 0x839_ */ .half 0x0173, 0x0174, 0x0175, 0x0176, 0x0177, 0x0178, 0x0179, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x017A
+/* 0x83A_ */ .half 0x017B, 0x017C, 0x017D, 0x017E, 0x017F, 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187, 0x0188, 0x0189, 0x018A
+/* 0x83B_ */ .half 0x018B, 0x018C, 0x018D, 0x018E, 0x018F, 0x0190, 0x0191, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192
+/* 0x83C_ */ .half 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, 0x0198, 0x0199, 0x019A, 0x019B, 0x019C, 0x019D, 0x019E, 0x019F, 0x01A0, 0x01A1, 0x01A2
+/* 0x83D_ */ .half 0x01A3, 0x01A4, 0x01A5, 0x01A6, 0x01A7, 0x01A8, 0x01A9, 0x01AA, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x83E_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x83F_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+
+// 0x___0 0x___1 0x___2 0x___3 0x___4 0x___5 0x___6 0x___7 0x___8 0x___9 0x___A 0x___B 0x___C 0x___D 0x___E 0x___F
+/* 0x844_ */ .half 0x01AB, 0x01AC, 0x01AD, 0x01AE, 0x01AF, 0x01B0, 0x01B1, 0x01B2, 0x01B3, 0x01B4, 0x01B5, 0x01B6, 0x01B7, 0x01B8, 0x01B9, 0x01BA
+/* 0x845_ */ .half 0x01BB, 0x01BC, 0x01BD, 0x01BE, 0x01BF, 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C4, 0x01C5, 0x01C6, 0x01C7, 0x01C8, 0x01C9, 0x01CA
+/* 0x846_ */ .half 0x01CB, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x847_ */ .half 0x01CC, 0x01CD, 0x01CE, 0x01CF, 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7, 0x01D8, 0x01D9, 0x01DA
+/* 0x848_ */ .half 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF, 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E4, 0x01E5, 0x01E6, 0x01E7, 0x01E8, 0x01E9, 0x01EA
+/* 0x849_ */ .half 0x01EB, 0x01EC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01ED
+/* 0x84A_ */ .half 0x01EE, 0x01EF, 0x01F0, 0x01F1, 0x01F2, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7, 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD
+/* 0x84B_ */ .half 0x01FE, 0x01FF, 0x0200, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x0206, 0x0207, 0x0208, 0x0209, 0x020A, 0x020B, 0x020C, 0x020D
+/* 0x84C_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x84D_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x84E_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x84F_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+
+// 0x___0 0x___1 0x___2 0x___3 0x___4 0x___5 0x___6 0x___7 0x___8 0x___9 0x___A 0x___B 0x___C 0x___D 0x___E 0x___F
+/* 0x854_ */ .half 0x020E, 0x020F, 0x0210, 0x0211, 0x0212, 0x0213, 0x0214, 0x0215, 0x0216, 0x0217, 0x0218, 0x0219, 0x021A, 0x021B, 0x021C, 0x021D
+/* 0x855_ */ .half 0x021E, 0x021F, 0x0220, 0x0221, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0222, 0x0223
+/* 0x856_ */ .half 0x0224, 0x0225, 0x0226, 0x0227, 0x0228, 0x0229, 0x022A, 0x022B, 0x022C, 0x022D, 0x022E, 0x022F, 0x0230, 0x0231, 0x0232, 0x0233
+/* 0x857_ */ .half 0x0234, 0x0235, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0236, 0x0237, 0x0238
+/* 0x858_ */ .half 0x0239, 0x023A, 0x023B, 0x023C, 0x023D, 0x023E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x859_ */ .half 0x0000, 0x023F, 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, 0x0248, 0x0000, 0x0000, 0x0000, 0x0000, 0x0249
+/* 0x85A_ */ .half 0x024A, 0x024B, 0x024C, 0x024D, 0x024E, 0x024F, 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, 0x0000, 0x0000
+/* 0x85B_ */ .half 0x0000, 0x0000, 0x0000, 0x0258, 0x0259, 0x025A, 0x025B, 0x025C, 0x025D, 0x025E, 0x025F, 0x0260, 0x0261, 0x0262, 0x0263, 0x0264
+/* 0x85C_ */ .half 0x0265, 0x0266, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x85D_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0267, 0x0268, 0x0269, 0x026A, 0x026B
+/* 0x85E_ */ .half 0x026C, 0x026D, 0x026E, 0x026F, 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, 0x0278, 0x0279, 0x027A, 0x027B
+/* 0x85F_ */ .half 0x027C, 0x027D, 0x027E, 0x027F, 0x0280, 0x0281, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+
+// 0x___0 0x___1 0x___2 0x___3 0x___4 0x___5 0x___6 0x___7 0x___8 0x___9 0x___A 0x___B 0x___C 0x___D 0x___E 0x___F
+/* 0x864_ */ .half 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, 0x0288, 0x0289, 0x028A, 0x028B, 0x028C, 0x028D, 0x028E, 0x028F, 0x0290, 0x0291
+/* 0x865_ */ .half 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, 0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029D, 0x029E, 0x029F, 0x0000, 0x0000
+/* 0x866_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x867_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x868_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x869_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x02A0, 0x02A1, 0x02A2, 0x02A3, 0x02A4
+/* 0x86A_ */ .half 0x02A5, 0x02A6, 0x02A7, 0x02A8, 0x02A9, 0x02AA, 0x02AB, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x86B_ */ .half 0x0000, 0x0000, 0x0000, 0x02AC, 0x02AD, 0x02AE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x86C_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x02AF, 0x02B0, 0x02B1, 0x02B2, 0x02B3, 0x02B4, 0x02B5, 0x02B6, 0x02B7
+/* 0x86D_ */ .half 0x02B8, 0x02B9, 0x02BA, 0x02BB, 0x02BC, 0x02BD, 0x02BE, 0x02BF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x86E_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x86F_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+
+// 0x___0 0x___1 0x___2 0x___3 0x___4 0x___5 0x___6 0x___7 0x___8 0x___9 0x___A 0x___B 0x___C 0x___D 0x___E 0x___F
+/* 0x874_ */ .half 0x02C0, 0x02C1, 0x02C2, 0x02C3, 0x02C4, 0x02C5, 0x02C6, 0x02C7, 0x02C8, 0x02C9, 0x02CA, 0x02CB, 0x02CC, 0x02CD, 0x02CE, 0x02CF
+/* 0x875_ */ .half 0x02D0, 0x02D1, 0x02D2, 0x02D3, 0x02D4, 0x02D5, 0x02D6, 0x02D7, 0x02D8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x876_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x877_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x878_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x879_ */ .half 0x0000, 0x02D9, 0x02DA, 0x02DB, 0x02DC, 0x02DD, 0x02DE, 0x02DF, 0x02E0, 0x02E1, 0x02E2, 0x02E3, 0x02E4, 0x02E5, 0x02E6, 0x02E7
+/* 0x87A_ */ .half 0x02E8, 0x02E9, 0x02EA, 0x02EB, 0x02EC, 0x02ED, 0x02EE, 0x02EF, 0x02F0, 0x02F1, 0x02F2, 0x02F3, 0x02F4, 0x02F5, 0x02F6, 0x02F7
+/* 0x87B_ */ .half 0x02F8, 0x02F9, 0x02FA, 0x02FB, 0x02FC, 0x02FD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x02FE, 0x02FF, 0x0300
+/* 0x87C_ */ .half 0x0301, 0x0302, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x87D_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x87E_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0303, 0x0304, 0x0305, 0x0306, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+/* 0x87F_ */ .half 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0307, 0x0308, 0x0309
+ENDDATA(sNonKanjiIndices)
diff --git a/src/libultra/gu/libm_vals.s b/src/libultra/gu/libm_vals.s
new file mode 100644
index 000000000..18de1ae4f
--- /dev/null
+++ b/src/libultra/gu/libm_vals.s
@@ -0,0 +1,9 @@
+#include "ultra64/asm.h"
+
+.section .rodata
+
+.balign 16
+
+DATA(__libm_qnan_f)
+ .word 0x7F810000
+ENDDATA(__libm_qnan_f)
diff --git a/src/libultra/libc/bcmp.s b/src/libultra/libc/bcmp.s
new file mode 100644
index 000000000..df6692feb
--- /dev/null
+++ b/src/libultra/libc/bcmp.s
@@ -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)
diff --git a/src/libultra/libc/bcopy.s b/src/libultra/libc/bcopy.s
new file mode 100644
index 000000000..e1240f113
--- /dev/null
+++ b/src/libultra/libc/bcopy.s
@@ -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)
diff --git a/src/libultra/libc/bzero.s b/src/libultra/libc/bzero.s
new file mode 100644
index 000000000..5323a92d7
--- /dev/null
+++ b/src/libultra/libc/bzero.s
@@ -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)
diff --git a/src/libultra/mgu/mtxf2l.s b/src/libultra/mgu/mtxf2l.s
new file mode 100644
index 000000000..edbb675a8
--- /dev/null
+++ b/src/libultra/mgu/mtxf2l.s
@@ -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)
diff --git a/src/libultra/mgu/mtxident.s b/src/libultra/mgu/mtxident.s
new file mode 100644
index 000000000..fb0e4c44a
--- /dev/null
+++ b/src/libultra/mgu/mtxident.s
@@ -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)
diff --git a/src/libultra/mgu/mtxidentf.s b/src/libultra/mgu/mtxidentf.s
new file mode 100644
index 000000000..583fc2218
--- /dev/null
+++ b/src/libultra/mgu/mtxidentf.s
@@ -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)
diff --git a/src/libultra/mgu/mtxl2f.s b/src/libultra/mgu/mtxl2f.s
new file mode 100644
index 000000000..9298da37c
--- /dev/null
+++ b/src/libultra/mgu/mtxl2f.s
@@ -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)
diff --git a/src/libultra/mgu/normalize.s b/src/libultra/mgu/normalize.s
new file mode 100644
index 000000000..7686317ea
--- /dev/null
+++ b/src/libultra/mgu/normalize.s
@@ -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)
diff --git a/src/libultra/mgu/scale.s b/src/libultra/mgu/scale.s
new file mode 100644
index 000000000..80199c062
--- /dev/null
+++ b/src/libultra/mgu/scale.s
@@ -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)
diff --git a/src/libultra/mgu/translate.s b/src/libultra/mgu/translate.s
new file mode 100644
index 000000000..d059ad569
--- /dev/null
+++ b/src/libultra/mgu/translate.s
@@ -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)
diff --git a/src/libultra/os/exceptasm.s b/src/libultra/os/exceptasm.s
new file mode 100644
index 000000000..79a6ed1a2
--- /dev/null
+++ b/src/libultra/os/exceptasm.s
@@ -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)
diff --git a/src/libultra/os/getcause.s b/src/libultra/os/getcause.s
new file mode 100644
index 000000000..48fbf3236
--- /dev/null
+++ b/src/libultra/os/getcause.s
@@ -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)
diff --git a/src/libultra/os/getcount.s b/src/libultra/os/getcount.s
new file mode 100644
index 000000000..bad47d0bc
--- /dev/null
+++ b/src/libultra/os/getcount.s
@@ -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)
diff --git a/src/libultra/os/getfpccsr.s b/src/libultra/os/getfpccsr.s
new file mode 100644
index 000000000..d86734ad0
--- /dev/null
+++ b/src/libultra/os/getfpccsr.s
@@ -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)
diff --git a/src/libultra/os/getsr.s b/src/libultra/os/getsr.s
new file mode 100644
index 000000000..aed66d40a
--- /dev/null
+++ b/src/libultra/os/getsr.s
@@ -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)
diff --git a/src/libultra/os/interrupt.s b/src/libultra/os/interrupt.s
new file mode 100644
index 000000000..82090742b
--- /dev/null
+++ b/src/libultra/os/interrupt.s
@@ -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)
diff --git a/src/libultra/os/invaldcache.s b/src/libultra/os/invaldcache.s
new file mode 100644
index 000000000..41799d318
--- /dev/null
+++ b/src/libultra/os/invaldcache.s
@@ -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)
diff --git a/src/libultra/os/invalicache.s b/src/libultra/os/invalicache.s
new file mode 100644
index 000000000..79d576fba
--- /dev/null
+++ b/src/libultra/os/invalicache.s
@@ -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)
diff --git a/src/libultra/os/maptlbrdb.s b/src/libultra/os/maptlbrdb.s
new file mode 100644
index 000000000..ea8fe4aa4
--- /dev/null
+++ b/src/libultra/os/maptlbrdb.s
@@ -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)
diff --git a/src/libultra/os/parameters.s b/src/libultra/os/parameters.s
new file mode 100644
index 000000000..c0020ddbe
--- /dev/null
+++ b/src/libultra/os/parameters.s
@@ -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
diff --git a/src/libultra/os/probetlb.s b/src/libultra/os/probetlb.s
new file mode 100644
index 000000000..ce655faca
--- /dev/null
+++ b/src/libultra/os/probetlb.s
@@ -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)
diff --git a/src/libultra/os/setcompare.s b/src/libultra/os/setcompare.s
new file mode 100644
index 000000000..4b69b2ea9
--- /dev/null
+++ b/src/libultra/os/setcompare.s
@@ -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)
diff --git a/src/libultra/os/setfpccsr.s b/src/libultra/os/setfpccsr.s
new file mode 100644
index 000000000..9b6b368f3
--- /dev/null
+++ b/src/libultra/os/setfpccsr.s
@@ -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)
diff --git a/src/libultra/os/setintmask.s b/src/libultra/os/setintmask.s
new file mode 100644
index 000000000..3afadb73a
--- /dev/null
+++ b/src/libultra/os/setintmask.s
@@ -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)
diff --git a/src/libultra/os/setsr.s b/src/libultra/os/setsr.s
new file mode 100644
index 000000000..382f2eada
--- /dev/null
+++ b/src/libultra/os/setsr.s
@@ -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)
diff --git a/src/libultra/os/setwatchlo.s b/src/libultra/os/setwatchlo.s
new file mode 100644
index 000000000..749b1dbd8
--- /dev/null
+++ b/src/libultra/os/setwatchlo.s
@@ -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)
diff --git a/src/libultra/os/unmaptlball.s b/src/libultra/os/unmaptlball.s
new file mode 100644
index 000000000..d7fe7cf53
--- /dev/null
+++ b/src/libultra/os/unmaptlball.s
@@ -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)
diff --git a/src/libultra/os/writebackdcache.s b/src/libultra/os/writebackdcache.s
new file mode 100644
index 000000000..4eea0d83a
--- /dev/null
+++ b/src/libultra/os/writebackdcache.s
@@ -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)
diff --git a/src/libultra/os/writebackdcacheall.s b/src/libultra/os/writebackdcacheall.s
new file mode 100644
index 000000000..837495c76
--- /dev/null
+++ b/src/libultra/os/writebackdcacheall.s
@@ -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)
diff --git a/src/makerom/entry.s b/src/makerom/entry.s
new file mode 100644
index 000000000..6be493e6b
--- /dev/null
+++ b/src/makerom/entry.s
@@ -0,0 +1,34 @@
+#include "ultra64/asm.h"
+#include "boot.h"
+
+.set noreorder
+
+.section .text
+
+.balign 16
+
+LEAF(entrypoint)
+ // Clear boot segment .bss
+ la $t0, _bootSegmentBssStart
+#ifndef AVOID_UB
+ // UB: li only loads the lower 16 bits of _bootSegmentBssSize when it may be larger than this,
+ // so not all of bss may be cleared if it is too large
+ li $t1, _bootSegmentBssSize
+#else
+ la $t1, _bootSegmentBssSize
+#endif
+.clear_bss:
+ addi $t1, $t1, -8
+ sw $zero, ($t0)
+ sw $zero, 4($t0)
+ bnez $t1, .clear_bss
+ addi $t0, $t0, 8
+ // Set up stack and enter program code
+ lui $t2, %hi(bootproc)
+ lui $sp, %hi(sBootThreadStack + BOOT_STACK_SIZE)
+ addiu $t2, %lo(bootproc)
+ jr $t2
+ addiu $sp, %lo(sBootThreadStack + BOOT_STACK_SIZE)
+END(entrypoint)
+
+.fill 0x60 - (. - entrypoint)
diff --git a/src/makerom/ipl3.s b/src/makerom/ipl3.s
new file mode 100644
index 000000000..2efa7b53d
--- /dev/null
+++ b/src/makerom/ipl3.s
@@ -0,0 +1,4 @@
+
+.section .text
+
+.incbin "baserom.z64", 0x40, 0xFC0
diff --git a/src/makerom/rom_header.s b/src/makerom/rom_header.s
new file mode 100644
index 000000000..883b8b0ab
--- /dev/null
+++ b/src/makerom/rom_header.s
@@ -0,0 +1,18 @@
+/*
+ * The Legend of Zelda: Ocarina of Time ROM header
+ */
+
+.byte 0x80, 0x37, 0x12, 0x40 /* PI BSD Domain 1 register */
+.word 0x0000000F /* Clockrate setting */
+.word 0x80000400 /* Entrypoint function (`entrypoint`) */
+.word 0x0000144C /* Revision */
+.word 0x917D18F6 /* Checksum 1 */
+.word 0x69BC5453 /* Checksum 2 */
+.word 0x00000000 /* Unknown */
+.word 0x00000000 /* Unknown */
+.ascii "THE LEGEND OF ZELDA " /* Internal ROM name */
+.word 0x00000000 /* Unknown */
+.word 0x0000004E /* Cartridge */
+.ascii "ZL" /* Cartridge ID */
+.ascii "P" /* Region */
+.byte 0x0F /* Version */