# Move a square with the keyboard

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

Web page: https://asm-editor.specy.app/learn/courses/mips/examples/keyboard-control

> The receiver registers polled once a frame for w, a, s and d, a direction kept in registers, and a square that keeps going until you steer it somewhere else.

The same program in: [M68K](https://asm-editor.specy.app/learn/courses/m68k/examples/keyboard-control.md), [RISC-V](https://asm-editor.specy.app/learn/courses/risc-v/examples/keyboard-control.md), [Z80](https://asm-editor.specy.app/learn/courses/z80/examples/keyboard-control.md), [x86-64](https://asm-editor.specy.app/learn/courses/x86/examples/keyboard-control.md)

Select **Open in editor**, then **Build** and **Run**. Click the **Screen** before pressing keys;
click it again if you return to the editor. Type lowercase `w`, `a`, `s` or `d` to steer the square.
It keeps moving in the last direction you chose and reappears at the opposite edge when it crosses
the grid. Press **Stop** when you are done.

The Screen has a 32 by 32 grid of coloured cells. The square covers 3 by 3 cells, and its upper-left
corner starts at `(14, 14)`. `fill_rect` takes `x` in `$a0`, `y` in `$a1`, width in `$a2`, and
height in `$a3`. It uses the colour in `$s0` and the address of `display` in `$s1`. The program calls
it first to paint the background, then to erase and redraw the moving square.

The keyboard receiver is a device-address block beginning at `0xffff0000`. Its control register is
at that address, and its data register is four bytes later, at `0xffff0004`. Loads from these
addresses ask the device for input. Stores to `display` paint Screen cells; loads from `display`
read their current colour words.

```mips
# @screen unit=8 width=256 height=256 base=display
.eqv MMIO 0xffff0000
.eqv SIDE 32                # words across and down
.eqv BOX 3                  # the square, in words
.eqv LAST 29                # SIDE - BOX, the largest x or y
.eqv FRAME 80               # milliseconds per frame
.eqv BACKGROUND 0x00101820
.eqv BOXCOLOUR 0x0000C060

.data
display: .space 4096        # SIDE * SIDE words, four bytes each

.text
.globl main

# fill_rect(x, y, w, h): $a0-$a3 hold the rectangle;
# $s0 holds the colour and $s1 holds the grid base
fill_rect:
    move $t0, $a1           # row = y
    add $t1, $a1, $a3       # one past the last row
rect_rows:
    sll $t2, $t0, 5         # row * SIDE
    add $t2, $t2, $a0       # + x
    sll $t2, $t2, 2         # four bytes per word
    add $t2, $t2, $s1
    move $t3, $a2
rect_cols:
    sw $s0, 0($t2)
    addi $t2, $t2, 4
    addi $t3, $t3, -1
    bnez $t3, rect_cols
    addi $t0, $t0, 1
    blt $t0, $t1, rect_rows
    jr $ra

main:
    la $s1, display
    li $s7, MMIO
    li $s0, BACKGROUND      # paint the grid once
    li $a0, 0
    li $a1, 0
    li $a2, SIDE
    li $a3, SIDE
    jal fill_rect

    li $s2, 14              # x
    li $s3, 14              # y
    li $s4, 1               # dx
    li $s5, 0               # dy

frame:
    li $s0, BACKGROUND      # erase the square where it was
    move $a0, $s2
    move $a1, $s3
    li $a2, BOX
    li $a3, BOX
    jal fill_rect

# --- one key sets the direction, it does not move the square -----------------
    lw $t4, 0($s7)          # the receiver control register
    andi $t4, $t4, 1        # the Ready bit
    beqz $t4, no_key
    lw $t5, 4($s7)          # read the waiting character
    andi $t5, $t5, 0xFF
    bne $t5, 'a', not_a
    li $s4, -1
    li $s5, 0
not_a:
    bne $t5, 'd', not_d
    li $s4, 1
    li $s5, 0
not_d:
    bne $t5, 'w', not_w
    li $s4, 0
    li $s5, -1
not_w:
    bne $t5, 's', no_key
    li $s4, 0
    li $s5, 1
no_key:

# --- and the square moves on its own, coming back in at the far edge ---------
    add $s2, $s2, $s4
    ble $s2, LAST, x_low
    li $s2, 0               # off the right edge, back at the left
x_low:
    bgez $s2, x_done
    li $s2, LAST            # off the left edge, back at the right
x_done:
    add $s3, $s3, $s5
    ble $s3, LAST, y_low
    li $s3, 0
y_low:
    bgez $s3, y_done
    li $s3, LAST
y_done:

    li $s0, BOXCOLOUR       # and draw it where it is now
    move $a0, $s2
    move $a1, $s3
    li $a2, BOX
    li $a3, BOX
    jal fill_rect

    li $v0, 32              # a frame of program time
    li $a0, FRAME
    syscall
    j frame
```

*Testcase for the program above (JSON):*

```json
{ "runFor": 100000 }
```

Each pass through `frame` erases the old square, polls the keyboard, moves its upper-left corner,
wraps that corner if it crosses an edge, draws the square at its new position, and waits before the
next pass. The background is painted only once. `$s2` and `$s3` hold the corner's `x` and `y`;
`$s4` and `$s5` hold the horizontal and vertical steps. The initial steps `(1, 0)` move it right.

`$s7` holds `0xffff0000`. The load at `0($s7)` reads receiver control; `andi` keeps Ready bit 0.
When that bit is 1, a character is waiting, so the load at `4($s7)` reads receiver data. The
character is in its low byte, and reading the data consumes it. If a character is waiting, it
remains available until the program reads it. If Ready is 0, the program skips the character checks
and keeps the previous steps.

Each `bne` compares the received character with one of the lowercase direction letters. The
assembler accepts a character literal such as `'d'` as shorthand for its numeric value. Pressing
`d` sets `($s4, $s5)` to `(1, 0)`: one cell right per frame and no vertical movement. Pressing `w`
sets it to `(0, -1)`. Setting the unused step to zero prevents diagonal movement when you change
direction. Uppercase `W` does not match lowercase `'w'` in this program.

The receiver supplies typed characters rather than a continuous report of held keys. A key changes
the stored direction; releasing it does not change the steps, so the square keeps moving. This
program reads the four letter characters. Arrow keys do not produce characters, so the receiver
does not report them. To read arrow keys or check whether a key is held, use the
[Input controller](https://asm-editor.specy.app/documentation/mips/input-controller.md), which also reports key presses and releases.
The fixed testcase runs the program without typing into the receiver; use the Screen to check the
steering yourself.

`LAST` is 29 because a 3-cell square whose left edge is at column 29 occupies columns 29–31. If a
rightward step makes `x` greater than 29, the code sets `x` to 0. A step past the left edge sets
`x` to 29. The same checks wrap `y` at the top and bottom.

Try this first: while it moves right, press `w`, then `d`. Predict the steps after each key. They
should be `(0, -1)` and then `(1, 0)`, so the square turns up and then right without moving
diagonally. To change the edge behavior, replace the `li $s2, 0` immediately after
`ble $s2, LAST, x_low` with `li $s2, LAST`. Build and Run again, then press `d`. At the right edge
the square now stays in the last valid column instead of appearing on the left; the other three
edges still wrap.

## Instructions in this Lecture

- MIPS: [`move`](https://asm-editor.specy.app/documentation/mips/instruction/move.md), [`add`](https://asm-editor.specy.app/documentation/mips/instruction/add.md), [`sll`](https://asm-editor.specy.app/documentation/mips/instruction/sll.md), [`sw`](https://asm-editor.specy.app/documentation/mips/instruction/sw.md), [`addi`](https://asm-editor.specy.app/documentation/mips/instruction/addi.md), [`bnez`](https://asm-editor.specy.app/documentation/mips/instruction/bnez.md), [`blt`](https://asm-editor.specy.app/documentation/mips/instruction/blt.md), [`jr`](https://asm-editor.specy.app/documentation/mips/instruction/jr.md), [`la`](https://asm-editor.specy.app/documentation/mips/instruction/la.md), [`li`](https://asm-editor.specy.app/documentation/mips/instruction/li.md), [`jal`](https://asm-editor.specy.app/documentation/mips/instruction/jal.md), [`lw`](https://asm-editor.specy.app/documentation/mips/instruction/lw.md), [`andi`](https://asm-editor.specy.app/documentation/mips/instruction/andi.md), [`beqz`](https://asm-editor.specy.app/documentation/mips/instruction/beqz.md), [`bne`](https://asm-editor.specy.app/documentation/mips/instruction/bne.md), [`ble`](https://asm-editor.specy.app/documentation/mips/instruction/ble.md), [`bgez`](https://asm-editor.specy.app/documentation/mips/instruction/bgez.md), [`syscall`](https://asm-editor.specy.app/documentation/mips/instruction/syscall.md), [`j`](https://asm-editor.specy.app/documentation/mips/instruction/j.md)

Previous: [A bouncing ball](https://asm-editor.specy.app/learn/courses/mips/examples/bouncing-ball.md)

Next: [The snake game](https://asm-editor.specy.app/learn/courses/mips/examples/snake.md)
