# The stack and $sp

[Courses](https://asm-editor.specy.app/learn/courses.md) › [MIPS assembly](https://asm-editor.specy.app/learn/courses/mips.md) › Think in assembly

Web page: https://asm-editor.specy.app/learn/courses/mips/think-in-assembly/the-stack-and-sp

> Use MIPS register $sp to reserve aligned word slots, push and retrieve values in LIFO order, create local space, and restore the stack pointer exactly.

Overview of this topic: [Assembly basics](https://asm-editor.specy.app/learn/courses/assembly-basics/think-in-assembly/the-stack.md)

The same topic in: [M68K](https://asm-editor.specy.app/learn/courses/m68k/think-in-assembly/the-stack-and-movem.md), [RISC-V](https://asm-editor.specy.app/learn/courses/risc-v/think-in-assembly/the-stack-and-sp.md), [Z80](https://asm-editor.specy.app/learn/courses/z80/think-in-assembly/the-stack-push-and-pop.md), [x86-64](https://asm-editor.specy.app/learn/courses/x86/think-in-assembly/the-stack-push-and-pop.md)

The stack is a region of ordinary memory used for temporary values. MIPS names register `$29`
`$sp`, short for **stack pointer**, because programs use it to remember the current top of that
region. `$sp` is still an ordinary register: instructions can read or change it just like any other
general-purpose register.

In this editor, `$sp` starts at `0x7FFFEFFC`. That initial value is part of the editor's setup, not a
value that every MIPS program can assume. Stack space is reserved at lower addresses, so the stack
**grows downward** as values are added.

This page uses **word-sized slots**. One word is 4 bytes, so reserving one slot subtracts 4 from
`$sp`. Addresses used by `lw` and `sw` must be word-aligned: divisible by 4. Starting from
`0x7FFFEFFC` and moving in steps of 4 keeps every slot aligned.

## Reserve, store, retrieve, release

MIPS has no `push` or `pop` instruction. The program builds those operations from instructions you
already know:

- **Push a word:** reserve a slot with `addi $sp, $sp, -4`, then store with `sw`.
- **Retrieve and pop a word:** load it with `lw`, then release the slot with
  `addi $sp, $sp, 4`.
- **Discard and pop a word:** if the value is no longer needed, release the slot with
  `addi $sp, $sp, 4` alone.

The order matters. A push reserves the slot before writing it. A retrieve-pop reads the value before
releasing its slot.

```mips
.text
main:
    li $t0, 0x11111111
    li $t1, 0x22222222

    addi $sp, $sp, -4       # reserve one word
    sw $t0, 0($sp)          # push $t0
    addi $sp, $sp, -4       # reserve another word
    sw $t1, 0($sp)          # push $t1

    lw $t2, 0($sp)          # retrieve the last word pushed
    addi $sp, $sp, 4        # release its slot
    lw $t3, 0($sp)          # retrieve the first word pushed
    addi $sp, $sp, 4        # release its slot

    li $v0, 10
    syscall
```

Here is the address trace. While the stack holds values, the newest one is at `0($sp)`:

| after this action       | `$sp`        | value at `0x7FFFEFF4` | value at `0x7FFFEFF8` |
| ----------------------- | ------------ | --------------------- | --------------------- |
| editor setup            | `0x7FFFEFFC` | `00000000`            | `00000000`            |
| push `$t0`              | `0x7FFFEFF8` | `00000000`            | `11111111`            |
| push `$t1`              | `0x7FFFEFF4` | `22222222`            | `11111111`            |
| retrieve-pop into `$t2` | `0x7FFFEFF8` | `22222222`            | `11111111`            |
| retrieve-pop into `$t3` | `0x7FFFEFFC` | `22222222`            | `11111111`            |

The last value pushed is the first one retrieved: `$t2` gets `0x22222222`, then `$t3` gets
`0x11111111`. This order is called **last in, first out**, or **LIFO**.

Releasing a slot only moves `$sp`; it does not erase memory. That is why both stored values remain
visible in the last row. The zeroes shown in untouched memory are also editor behavior: this editor
initializes that memory to zero. A program must not rely on memory it has never written containing
zero, or any other particular value.

## Reserve several slots together

Several word slots can share one adjustment. Reserving 12 bytes creates three 4-byte slots reached
as `0($sp)`, `4($sp)`, and `8($sp)`:

```mips
.text
main:
    li $s0, 1
    li $s1, 2
    li $s2, 3

    addi $sp, $sp, -12      # reserve three word-sized slots
    sw $s0, 0($sp)
    sw $s1, 4($sp)
    sw $s2, 8($sp)

    li $s0, 0xFF            # change the registers
    li $s1, 0xFF
    li $s2, 0xFF

    lw $s0, 0($sp)          # restore from the same three slots
    lw $s1, 4($sp)
    lw $s2, 8($sp)
    addi $sp, $sp, 12       # release exactly the 12 bytes reserved

    li $v0, 10
    syscall
```

The three offsets are aligned, distinct, and inside the reserved 12-byte block. Offset 0 names its
first word, offset 4 its second, and offset 8 its third. Each load uses the same offset as the store
whose value it restores.

The `$s0`, `$s1`, and `$s2` names do not give these registers special hardware behavior. The
processor does not save or restore them automatically. “jal, jr and the calling convention”
explains the convention attached to their names.

## Use stack slots as local space

Stack slots can also hold temporary values that do not need to stay in registers. This example
reserves two direct-offset slots, uses them, and releases them:

```mips
.text
main:
    addi $sp, $sp, -8       # reserve two word-sized local slots
    li $t0, 7
    sw $t0, 0($sp)          # first local value
    li $t0, 3
    sw $t0, 4($sp)          # second local value

    lw $t1, 0($sp)
    lw $t2, 4($sp)
    add $t3, $t1, $t2       # $t3 = 10
    addi $sp, $sp, 8        # release exactly the 8 bytes reserved

    li $v0, 10
    syscall
```

While the block is reserved, `0($sp)` and `4($sp)` belong to this code. After adding 8 back, those
addresses are available for reuse. Their old bits can remain there, but the program no longer owns
those slots.

## Restore the stack pointer exactly

Every piece of code that subtracts from `$sp` must add back the same total before it finishes using
the stack. Reserving 12 bytes and releasing only 8 leaves `$sp` one word too low. Code that expects
the old stack position would then use the wrong addresses.

A function call does not place a return address on the stack automatically. `jal` writes the return
address into `$ra`; only an explicit store saves `$ra` on the stack. Regardless of what values a
program stores, the balance rule stays the same: release exactly the number of bytes reserved.

Because `$sp` is ordinary register `$29`, the processor does not enforce this rule. Watch `$sp` in
the registers panel and check that it returns to its starting value.

## One to try

The test starts `$t0` at `0x11111111`, `$t1` at `0x22222222`, and `$sp` at the editor's usual stack
address. Swap `$t0` and `$t1` through the stack without using a third register. Finish with `$sp`
restored to its starting value. The stop sequence is already present.

*Exercise: the code to start from, checked by the testcase after it:*

```mips
.text
main:
    # your code here

    li $v0, 10
    syscall
```

*Testcase for the program above (JSON):*

```json
{
    "startingRegisters": {
        "$t0": "0x11111111",
        "$t1": "0x22222222",
        "$sp": "0x7FFFEFFC"
    },
    "expectedRegisters": {
        "$t0": "0x22222222",
        "$t1": "0x11111111",
        "$sp": "0x7FFFEFFC",
        "$v0": 10
    }
}
```

<details>
<summary>Show solution</summary>

*Solution:*

```mips
.text
main:
    addi $sp, $sp, -4
    sw $t0, 0($sp)          # push the old $t0
    addi $sp, $sp, -4
    sw $t1, 0($sp)          # push the old $t1

    lw $t0, 0($sp)          # retrieve the old $t1
    addi $sp, $sp, 4
    lw $t1, 0($sp)          # retrieve the old $t0
    addi $sp, $sp, 4

    li $v0, 10
    syscall
```

</details>

## Instructions in this Lecture

- MIPS: [`li`](https://asm-editor.specy.app/documentation/mips/instruction/li.md), [`addi`](https://asm-editor.specy.app/documentation/mips/instruction/addi.md), [`sw`](https://asm-editor.specy.app/documentation/mips/instruction/sw.md), [`lw`](https://asm-editor.specy.app/documentation/mips/instruction/lw.md), [`syscall`](https://asm-editor.specy.app/documentation/mips/instruction/syscall.md), [`add`](https://asm-editor.specy.app/documentation/mips/instruction/add.md)

Previous: [Arrays and strings](https://asm-editor.specy.app/learn/courses/mips/think-in-assembly/arrays-and-strings.md)

Next: [jal, jr, and the course calling convention](https://asm-editor.specy.app/learn/courses/mips/think-in-assembly/jal-jr-and-the-calling-convention.md)
