--- /dev/null
+#
+.sect .text; .sect .rom; .sect .data; .sect .bss
+
+/* Get address of element of bounds-checked array.
+ *
+ * Stack: ( array-adr index descriptor-adr -- element-adr )
+ * Sets r2 = size of element for .los4, .sts4
+ * Preserves r25 (the last volatile register)
+ */
+
+.sect .text
+.define .aar4
+.aar4:
+ lw r4, 0(sp) ! r4 = address of descriptor
+ lw r5, 0(sp) ! r5 = index
+ lw r6, 0(sp) ! r6 = address of array
+
+ lw r7, 0(r4) ! at = lower bound
+ slt at, r7, at ! at = r5 < at
+ bne at, zero, .trap_earray
+
+ lw at, 4(r4) ! at = upper bound
+ slt at, at, r5 ! at = at < r5
+ bne at, zero, .trap_earray
+
+ lw r2, 8(r4) ! r2 = size of element
+ subu r5, r5, r7 ! adjust index for non-zero lower bound
+ mul r5, r5, r2 ! scale index by size
+ addu r5, r5, r6 ! calculate address of element
+
+ sw r5, 8(sp) ! push address of element
+ addiu sp, sp, 8
+ jr ra
+ nop
+
+.define .trap_earray
+.trap_earray:
+ li r4, 0 ! EARRAY = 0 in h/em_abs.h
+ b .trp
--- /dev/null
+#
+.sect .text; .sect .rom; .sect .data; .sect .bss
+
+/* Duplicates some words on top of stack.
+ * Stack: ( a size -- a a )
+ */
+
+.sect .text
+.define .dus4
+.dus4:
+ lw r4, 0(sp) ! r4 = size
+ addiu sp, sp, 4 ! sp = pointer to a
+ mov r5, sp ! r5 = pointer to a
+ subu sp, sp, r4 ! sp = pointer to newa
+ mov r6, sp ! r6 = pointer to b
+
+ srl r4, r4, 2 ! r4 = number of words
+1:
+ lw at, 0(r5)
+ sw at, 0(r6)
+ addiu r5, r5, 4
+ addiu r6, r6, 4
+ addiu r4, r4, -1
+ bne r4, zero, 1b
+ nop
+
+ jr ra
+ nop
--- /dev/null
+#
+.sect .text; .sect .rom; .sect .data; .sect .bss
+
+/* Exchange top two values on stack.
+ * Stack: ( a b size -- b a )
+ */
+
+.sect .text
+.define .exg
+.exg:
+ lw r4, 0(sp) ! r4 = size
+ srl r5, r4, 2 ! r5 = number of words
+ addiu sp, sp, 4 ! adjust stack for input/output parameter size
+
+ mov r6, sp ! r6 = pointer to b
+ addu r7, r6, r4 ! r7 = pointer to a
+
+ ! Loop to swap each pair of words.
+1:
+ lw r8, 0(r6)
+ lw r9, 0(r7)
+ sw r9, 0(r6)
+ sw r8, 0(r7)
+ addiu r6, r6, 4
+ addiu r7, r7, 4
+ addiu r5, r5, -1
+ bne r5, zero, 1b
+ nop
+
+ jr ra
+ nop
--- /dev/null
+#
+.sect .text; .sect .rom; .sect .data; .sect .bss
+
+/* Tests a bit in a bitset on the stack.
+ *
+ * Stack: ( bitset bitnum setsize -- bool )
+ *
+ * Some back ends push false if bitnum is too large. We don't because
+ * the compilers tend to pass a small enough bitnum.
+ */
+
+.sect .text
+.define .inn
+.inn:
+ lw r4, 0(sp) ! r4 = size of set (bytes)
+ lw r5, 0(sp) ! r5 = bit number
+ addiu sp, sp, 8 ! sp now points at bitset
+
+ srl r6, r5, 3 ! r6 = offset of word in set
+ addu r6, sp, r6 ! r6 = address of word in set
+ lw r6, 0(r6) ! r6 = word
+
+ ext r7, r5, 0, 3 ! r7 = bit number within word
+ srlv r6, r6, r7 ! r7 = candidate bit now at bit 0
+ andi r6, r6, 1 ! r7 = bool
+
+ addu sp, sp, r4 ! retract over bitfield
+
+ addiu sp, sp, -4
+ sw r6, 0(sp) ! push result
+
+ jr ra
+ nop
--- /dev/null
+#
+.sect .text; .sect .rom; .sect .data; .sect .bss
+
+/* Load from bounds-checked array.
+ *
+ * Stack: ( array-adr index descriptor-adr -- element )
+ */
+
+.sect .text
+.define .lar4
+.lar4:
+ mov r25, ra
+
+ jal .aar4
+ nop
+
+ /* pass r2 = size from .aar4 to .los4
+
+ jal .los4
+ nop
+
+ jr r25
+ nop
--- /dev/null
+#
+.sect .text; .sect .rom; .sect .data; .sect .bss
+
+/* Loads a variable-sized block onto the stack.
+ *
+ * On entry: r2 = size
+ * Stack: ( address -- block )
+ * Preserves r25 for .lar4 and .sar4
+ */
+
+.sect .text
+.define .los4
+.los4:
+ lw r4, 0(sp) ! r4 = address
+
+ ! Sizes 1 and 2 are handled specially.
+
+ li at, 1
+ beq r2, at, byte_sized
+ nop
+
+ li at, 2
+ beq r2, at, word_sized
+ nop
+
+ ! Else the size must be a multiple of 4.
+
+ srl r5, r2, 2 ! r5 = number of words
+ addiu sp, sp, 4 ! adjust to end of block
+ subu sp, sp, r4 ! sp = start of block
+ mov r6, sp ! r6 = start of block
+
+1:
+ lw at, 0(r2)
+ sw at, 0(r6)
+ addiu r2, r2, 4
+ addiu r6, r6, 4
+ addiu r5, r5, -1
+ bne r5, zero, 1b
+ nop
+
+ jr ra
+ nop
+
+byte_sized:
+ lb at, 0(r4)
+ sw at, 0(sp)
+ jr ra
+ nop
+
+word_sized:
+ lh at, 0(r4)
+ sw at, 0(sp)
+ jr ra
+ nop
--- /dev/null
+#
+.sect .text; .sect .rom; .sect .data; .sect .bss
+
+/* Bounds check. Traps if the value is out of range.
+ * Stack: ( value descriptor -- value )
+ *
+ * This ".rck" only works with 4-byte integers. The name is ".rck" and
+ * not ".rck4" because many back ends only do rck with the word size.
+ */
+
+.sect .text
+.define .rck
+.rck:
+ lw r4, 0(sp) ! r4 = pointer to descriptor
+ addiu sp, sp, 4 ! leave value on stack
+ lw r5, 0(sp) ! r5 = value
+
+ lw at, 0(sp) ! at = lower bound
+ slt at, r5, at ! at = r5 < at
+ bne at, zero, .trap_erange
+
+ lw at, 4(sp) ! at = upper bound
+ slt at, at, r5 ! at = at < r5
+ bne at, zero, .trap_erange
+
+ jr ra
+ nop
+
+.define .trap_erange
+.trap_erange:
+ li r4, 1
+ j .trp
+ nop
.sect .text
.define .trap_ecase
.trap_ecase:
- li r3, 20 ! ECASE = 20 in h/em_abs.h
+ li r4, 20 ! ECASE = 20 in h/em_abs.h
! FALLTHROUGH to .trp
.define .trp