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.

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:

fromwhat is there
0x00400000text, the instructions you wrote
0x10000000global data
0x10008000where $gp starts
0x10010000static data, where .data puts your first label
0x10040000the heap, which the memory syscall hands out
0x7fffeffcwhere $sp starts, and the stack grows downwards from it
0x80000180kernel text, where an exception handler goes
0xffff0000the 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.

addressbytein
0x1001000078$t1
0x1001000156$t2
0x1001000234$t3
0x1001000312$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:

instructionbyteswhat it does
lb1loads a byte and sign extends it to 32 bits
lbu1loads a byte and fills the rest with zeroes
lh2loads a half and sign extends it
lhu2loads a half and fills the rest with zeroes
lw4loads a whole word
sb1stores the lowest byte of the register
sh2stores the lowest half
sw4stores 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.

Show solution