This
instruction will take the pointer in $s0, add 12 bytes to it, and then load the
value from the memory pointed to by this calculated sum into register $t0

°Notes:

•$s0 is
called the base register

•12 is
called the offset

•if s0
contains 0x1000 0100 as above, this load accesses address 0x1000 010c.Note that 12 decimal is c in hex. Numbers
default to decimal in assembler. Need to write 0x in front of hex numbers, just
as in C.

But how do we get
0x1000 0100 into s0??Using lui and la

We saw how to use
immediate operands to construct constants, but they only can handle 16 bits.

Another instruction:
lui, load upper immediate, loads 16 bits into the upper half of the target
register, and clears the lower half.

So to get 0x1000
0100 into s0:

lui $s0, 0x1000

addi $s0, 0x0100

Note that the assembler supports a pseudo-instruction la
(load address) that knows how to do this, so we can write:

la $s0,
0x10000100

and the assembler will compose the two needed instructions
(perhaps using ori instead of addi) for us.

Store: Also want to store value from a register into
memory

°Store
instruction syntax is identical to Load instruction syntax

°MIPS
Instruction Name:

°sw (meaning
Store Word, so 32 bits or one word are stored at a time)

°Example:sw $t0,12($s0)

°This instruction will take the
pointer in $s0, add 12 bytes to it, and then store the value from register $t0
into the memory address pointed to by the calculated sum

First full program:
hello.s, the hello world program linked to the class web page.

The assembler reads the program, in order, and while .text is
in effect, it puts the indicated instructions into a growing text segment (i.e.
code), and when .data is in effect, it puts the indicated data areas into the
data segment.These are loaded into SPIM
along with some startup code, and we end up with what you see in the screenshot
handout.

Startup Code

Note that the assembled code starts at 0x40 0024, and there
is code before that. This code is the user-level startup code. It sets some
registers up and then calls main. When main returns, it does the exit syscall,
returning execution to the OS. We see the same kind of thing on full-sized UNIX
and Linux systems.

Symbols, and the “symbol
table”

The assembler is able to handle name-value associations
called symbols. The most important kind of symbol is a named memory location
like symbol (or label) main, with value 0x40 0024, in the code area (text
segment) and symbol msg, with value 0x1001 0000, in the data segment.

At runtime, symbols
have fixed values, arrived at by the build process done by SPIM.When we write the program, we don’t yet know
these values, but we can trust the build process to allocate proper addresses.
We can just use the symbols like named constants.

To see the “global symbols”, the ones with .globl either by
you or the startup module, use Simulator>Display symbol table. This had been
done in the run shown on the screenshot.See the Console window near the bottom.If you want to see msg in this report, add “.globl msg” at the top to
make it global.

Now that we know that msg = 0x1001 0000, we can see how the
la is expanded into two instructions by the assembler:

ori
$4, $1, 0 [msg]# this is or-ing in the
lower half, which is 0 in this case, and putting the addr in $a0

syscall: special trap instruction to get OS to print the
string

Calling syscall 4, print string:

·Put address of string to print in $a0

·Put 4 in $v0

·Do syscall instruction

See pg. B-44 for list of system calls you can use.The string needs to be null-terminated, as
done in C. The .asciiz directive does this for us.

Understanding the
bytes in the string at 0x1001 0000 (msg): Little-endian representation!

We see [0x10010000]0x6c6c6548 ...

We can do “man ascii” on Linux/UNIX to get a decent ASCII
table that shows hex values. It shows that 0x6c = l, 0x65 = e, and 0x48 = H, so
we are seeing the first 4 bytes of Hello world! in opposite order.

Actually, the byte 48 is at 0x1001 0000, the byte 65 at
0x1001 0001, and so on, as expected for a string starting at 0x1001 0000. But
SPIM is displaying the contents of the word
at 0x1001 0000 (4 bytes) as an integer number.

Little-endian Integer Layout:

A: lowest byte of number, loads (lw) to lowest byte of
register, prints at right end

A+1: next byte

A+2: next byte

A+3: high byte, loads to highest byte of register, prints
at left end

So you see that to find the byte at A, the first byte of
the string, you need to look on the right-hand end of the printed number.

Working with
strings (for problem 1 of hw1)

Let’s count the characters in a string set up like Hello,
world!.

Idea: set up a pointer in a register, clear the count reg

Loop:

load a byte (lb)

if 0, branch to done

count it

branch back to load

Registers we can use without “preserving”: see pg. 118: t’s,
a’s, v’s: plenty

Ex: use t0 for ptr, t1 for count, t2 for byte

Need branches: Sec 2.7, pg. 105

beg reg1, reg2, label1

Go to statement label label1 if contents of reg1 and reg2
are equal.Similarly bne.

Unconditional branch: “jump” j label1

OK, we’re ready to code!

From Handout: String Processing Example 1/31/11

countstring.s

#
countstring.s: count chars in a string in data

#
registers:

#
t0 - pointer to string

#
t1 - count of chars

#
t2 - current char

.globl
main

.globl
str2count

main:la $t0, str2count# point to string

li $t1, 0# count = 0

loop:
lb $t2, 0($t0)# load byte of
string

beq $t2, $0, done# check if byte = 0

addi $t1,$t1,1# inc count

addi $t0, $t0, 1# inc pointer

j loop# loop back

done:
move $a0, $t1 # count to print

li $v0, 1# print string

syscall

jr $ra# return

.data

str2count:
.asciiz "abcdefg"

In C, written to avoid #include <stdio.h>, so we
can use mips-gcc on it, and also putting the string in the data section.