Memory, little endian and alignment
The four gigabytes a MIPS program can address and where this editor puts the code, the data and the stack in them. The byte order a word is written in, the five loads and three stores, and the addresses that end a run.
The overview of this topic is in Assembly basics. The same topic in M68K, Z80.
Thirty two registers hold thirty two words, and a program has more than that to keep. Everything else lives in memory, which MIPS reaches with a 32 bit address and with nothing but the load and store instructions.
The address space
An address is 32 bits, so it runs from 0x00000000 to 0xFFFFFFFF: four gigabytes. Nothing in the
hardware divides that up, and this simulator follows MARS in putting each thing at a fixed address:
| from | what is there |
|---|---|
0x00400000 | text, the instructions you wrote |
0x10000000 | global data |
0x10008000 | where $gp starts |
0x10010000 | static data, where .data puts your first label |
0x10040000 | the heap, which the memory syscall hands out |
0x7fffeffc | where $sp starts, and the stack grows downwards from it |
0x80000180 | kernel text, where an exception handler goes |
0xffff0000 | the memory-mapped devices, the screen and the keyboard |
A byte nobody has written reads 0 here. Open the memory panel of any program on this page, look
anywhere your program did not touch, and every byte is 00. That is this simulator's choice, and the
M68K's is the opposite, so a program that reads uninitialised memory behaves differently on the two.
The instructions are the one region you cannot read. lw $t0, 0x00400000($zero) ends the run with
Cannot read directly from text segment!0x00400000
The assembled program is there, at four bytes per instruction, and this simulator keeps it where a load cannot reach it. Everything else in the table is memory like any other.
Little endian
MIPS is little endian: the least significant byte of a word goes at the lowest address.
The word 0x12345678 written at 0x10010000 is 78 at 0x10010000, 56 at 0x10010001, 34 at
0x10010002 and 12 at 0x10010003, which is backwards from the way you wrote the number down.
Build this one with the memory panel open, type 10010000 in its address box, then Run.
| address | byte | in |
|---|---|---|
0x10010000 | 78 | $t1 |
0x10010001 | 56 | $t2 |
0x10010002 | 34 | $t3 |
0x10010003 | 12 | $t4 |
$t5 comes out at 00005678 and $t6 at 00001234, the two halves each read back the right way
round, and $t7 at 12345678, the whole word as you wrote it. A load of a word or a half puts the
bytes back in order; only reading them one at a time shows you which way they are stored.
The M68K is big endian and stores the same word as 12 34 56 78. Nothing about a program that only
reads and writes whole words changes between the two. What changes is a program that takes a word
apart a byte at a time, which is what the table above does.
The size is in the instruction's name
MIPS has no size suffix. Which instruction you use says how many bytes it touches, and the loads say what to do with the bits above them:
| instruction | bytes | what it does |
|---|---|---|
lb | 1 | loads a byte and sign extends it to 32 bits |
lbu | 1 | loads a byte and fills the rest with zeroes |
lh | 2 | loads a half and sign extends it |
lhu | 2 | loads a half and fills the rest with zeroes |
lw | 4 | loads a whole word |
sb | 1 | stores the lowest byte of the register |
sh | 2 | stores the lowest half |
sw | 4 | stores the whole word |
The stores have no signed and unsigned pair, because a store writes the bits it is given and there is nothing above them to fill in.
The eight bytes at 0x10010000 come out as DD CC BB AA 11 00 33 22, and $t4 reads 22330011.
The sb wrote one byte and left the one after it alone, the sh wrote two, and the lw picked up
all four as a little endian word, which is why the 11 your program stored first ends up at the
bottom of it.
0($t0), 4($t0) and 6($t0) are the only addressing mode there is: a register plus a constant
offset in bytes. "Loads, stores and immediates" is the lecture on it.
Alignment
A word has to start at an address that is a multiple of 4, and a half at a multiple of 2. A byte can go anywhere. Break the rule and the run ends with a message naming the address:
store address not aligned on word boundary 0x10010003
A load says fetch address instead of store address, and a half says halfword boundary.
The assembler keeps .word and .half aligned for you: put a .byte in front of a .word and it
leaves the gap, so .word data is always safe. What it does not align is .space and the
strings, because those are byte data and a byte needs no alignment. So a buffer you reserve after an
odd length string starts at an odd address, and the first sw into it ends the run.
.align n is the fix: it moves the next thing up to a multiple of 2 to the n, so .align 2 gives
you a multiple of 4.
$t0 is 10010000 and $t1 is 10010004, the three bytes of "Hi" rounded up to four, and
$t3 comes back at 42.
Delete the .align 2 line and press Build and Run. counts moves to 0x10010003, the sw stops
the program, and the message under the editor gives you that address. Then put it back.
Two more addresses end a run. One outside everything in the table, such as lw $t1, 4($zero), says
address out of range 0x00000004. And a loop that never stops is simply cut off when the Playground's
two million instructions run out, with no message at all, which is what an accidental infinite loop
looks like here.
Your turn
The word at value is 0x12345678. Leave its lowest byte in $t0, which is the 0x78, and its
highest byte in $t1, which is the 0x12, each as a number on its own with zeroes above it.
Show solution
The second one has a string of five bytes and a buffer after it, so the buffer starts at an odd
address. Make it word aligned and store 0x11223344 in its first word.