Skip to content
Contents

Memory is a long row of bytes

Section 4.1 7 min read

  • address
  • byte, halfword, word
  • little-endian
  • alignment and the address error
  • the memory map

Checked against: MIPS32 Vol. I §2.2 (byte ordering, alignment) carolyne_ref/carolyne_io.h (the memory map) carolyne_ref/dumps/hello_o0.dump (bfc00084: lui sp / addiu sp)

Before you start

  • §2.1: registers are inside the CPU, memory is outside
  • §1.1: hex; a word is 32 bits

By the end, you can

  • Say what an address is and how many bytes a word takes
  • Write the four bytes of a word in memory in little-endian order
  • Decide whether an address is legal for lw, lh or lb
  • Place the program, the data, the stack and the doors on the memory map

A row of bytes, each with a number

Memory is a row of . Each byte has a number, its . The first byte is address 0, the next is 1, and so on up to 4294967295. An address is a 32-bit number, so it fits in a register. This book writes addresses as eight hex digits, 0x10000100.

Sixteen bytes of memory, starting at 0x10000100

What does each cell hold, and what does the number under it mean?

480100
691
0a2
003
780104
565
346
127
ff0108
ff9
ffa
ffb
00010c
00d
00e
00f

What to notice

Each cell is one byte, two hex digits. The label under a cell is the last digits of its address. Addresses go up by one per cell, left to right. The first three bytes happen to be the characters H, i and a new line.

Words, halfwords, and which byte comes first

A word is four bytes in a row. It is named by its lowest address: “the word at 0x10000104” is bytes 0x104, 0x105, 0x106 and 0x107. A is two bytes.

Which of the four bytes holds the top of the number? On your toolchain and your core, the lowest address holds the least significant byte. That is order. Watch sw put 0x12345678 into memory:

sw writes 0x12345678 at 0x10000100

Which byte of the register goes to which address?

000100
001
002
003
000104
005
006
007

sw writes register value 0x12345678 as bytes:

78→ 0x1000010056→ 0x1000010134→ 0x1000010212→ 0x10000103the low byte goes to the lowest address (little-endian)

What to notice

The register’s low byte 78 goes to the lowest address, 0x100. The high byte 12 goes to 0x103. Read the cells left to right and the number looks backwards: 78 56 34 12. That is not a mistake; it is the rule.

Now read it back. Drag the slider to move the window, or change the operation to see how a halfword or a byte is assembled.

lw reads the four bytes back into a register

Move the window one byte to the right. What happens?

780100
561
342
123
fe0104
ff5
006
007

bytes 0x10000100 … 0x10000103, lowest address first: [78 56 34 12]

assembled, most significant on the left:12byte 334byte 256byte 178byte 0= 0x12345678

lw puts 0x12345678 = 305419896 in the register.

What to notice

At 0x100 the four bytes come back as 0x12345678: the strip reverses them for you. At 0x101 the window turns red and dashed. A word may not start there. Switch to lh and 0x102 becomes legal again; switch to lb and any address is fine.

Alignment: where a word may start

A word must start at a multiple of 4. A halfword must start at a multiple of 2. A byte may start anywhere. This rule is .

accesslegal addressesexample
lw, swmultiples of 40x10000100, 0x10000104, never 0x10000102
lh, lhu, shmultiples of 20x10000102, never 0x10000103
lb, lbu, sbany0x10000103

On real MIPS a misaligned lw does not read a wrong word. It stops the program with an exception. GCC knows the rule and never emits a misaligned access, so a compiled program never meets it. Your own core does not check alignment yet. It would read the wrong word, and that gap is a recorded open item.

Before reading on, decide:

`sw` stored 0xDEADBEEF at 0x200. What does `lb $t0,0($t1)` with $t1 = 0x200 load?

The memory map

Where do things live? Your test machine uses two separate memories and a fixed plan, its .

Where things live: the test machine's memory map (16 KB of data)

Higher addresses are at the top. The stack starts just under the doors and grows downward.

0xFFFFFFFF 0x00000000 the program (instruction memory) crt0, main, the doors' helpers · 8 KB 0xBFC00000 ← the reset vector: the PC starts here (§9.2) … nothing is mapped between the regions … 0x10004000 exit door 0x10003FF8 putint door 0x10003FF4 putchar door 0x10003FF0 ← three words the harness watches (§1.2) 0x10003FF0 ← $sp starts here (crt0) the stack grows toward lower addresses (§4.4) .data and .rodata: globals, string constants reached only by lw / sw / lb / sb … 0x10000000 ← the data memory starts here

What to notice

The program sits far up, at 0xBFC00000, the address every MIPS CPU starts from. The data sits at 0x10000000. The three doors are the top 16 bytes of the data memory, and the stack starts right under them and grows downward. Everything the compiler places, it places into these two regions.

The two memories are separate hardware, which is why the code and the data have different bases. A load reads the data memory; the program memory is only ever fetched. The smoke scenarios of chapters 1 to 3 used a 4 KB data memory, so their doors were at 0x10000FF0. Compiled programs use 16 KB.

Go deeper: why alignment exists, and what your core does today

Memory hardware hands out one aligned word per access. A misaligned word straddles two of them, so it needs two accesses and a shift-and-merge. The 1985 MIPS designers refused to build that path: one access per load, always. So the ISA forbids misaligned words and traps on them, and the special instructions lwl/lwr (not covered here) exist for the rare cases that need the two halves.

Carolyne’s load/store unit takes the same one-access shortcut: it fetches the containing word and picks bytes out by the low two address bits. But it does not raise the address error, because the description has no trap policy yet. A misaligned lw therefore returns the aligned word that contains the address, silently. It is listed in docs/open_items.md, and GCC’s alignment discipline is what keeps every compiled program clear of it.

Can you now explain it?

Memory is a numbered row of bytes. A word is four of them, named by the lowest address, with the least significant byte first. Words start at multiples of 4, halfwords at multiples of 2, and the machine's regions sit at fixed addresses.

If you remember only three things

  1. An address names one byte. Thirty-two bits, written as eight hex digits. A register holds one, which is how pointers work.
  2. Little-endian: small end first. 78 56 34 12 in memory is 0x12345678 in a register. A dump looks reversed; the rule is not.
  3. Aligned, or an address error. lw at a multiple of 4, lh at a multiple of 2, lb anywhere. GCC obeys; real MIPS traps; your core does not check yet.

Now try to explain this one yourself: bytes FE FF sit at 0x10000104 and 0x10000105. What does lhu from 0x10000104 load, and what does lh load? Use the byte order and §3.2’s two extensions.

Go deeper: a model answer, once you have tried

Little-endian: the byte at the lower address is the low byte. So the halfword is 0xFFFE. lhu zero-extends it: 0x0000FFFE, which is 65534. lh sign-extends it: bit 15 is 1, so the top fills with ones, 0xFFFFFFFE, which is −2. Same two bytes, two register values, and only the instruction decides. Section 4.3 makes this the main subject.

Check your understanding

0/5 answered
  1. 1.`sw` stores 0xDEADBEEF at address 0x200. Which byte is at 0x200, and which at 0x203?

  2. 2.Which of these addresses may `lh` use?

  3. 3.An array of words starts at 0x10000010. What is the address of its third word, `a[2]`?

  4. 4.A program executes `lw $t0,2($t1)` with $t1 = 0x10000100. What happens on real MIPS?

  5. 5.What does 'little-endian' mean?


Next: §4.2 — load and store: lw, sw and base + offset.

Where this comes back

3 later sections build on this one.