Chapter 10: Assembly
In this chapter, we introduce the assembly language which is the human-readable representation of the computer’s native language. We also introduce simple arithmetic instructions and show how these operations are written in Assembly language. We then define the MIPS instruction operands: registers, memory, and constants.
Objectives
By the end of this chapter you should be able to:
- Demonstrate knowledge of word-addressable and memory
- Differentiate word-addressable and byte-addressable memories
- Summarize features of the stored program
- Recall different types of machine code
- Demonstrate knowledge of logic operations
- Carry out conditional operations with arithmetic instructions
10.1 Assembly Languages
Read Word-Addressable Memory
An instruction operates on operands. In MIPS assembly architecture, there are only 32 registers which are not good enough to hold all the data. We can store more data in memory. The register file is small and fast, whereas memory is large and slow. The instructions are stored in memory. Only commonly used variables are kept in registers.
When we store the data in memory, each 32-bit data word has a unique address, as shown below:
Fig. ‑. Word-Addressable Memory
We can read the data in memory and load it to one register using the load word (lw) instruction, as indicated with the red arrow in the above figure.
The following instruction exemplifies the format of the lw instruction.
- lw $s0, 5(t1)
where $s0 is the register address that will hold the data after loading the data from memory. The memory address is calculated by adding the base address ($t1) to the offset value (5), i.e. $t1 + 5.
After executing the lw instruction, the register address $s0 holds the value at the memory address $t1 + 5. Any register can be used as the base address.
Let’s read a word of data at the memory address 1 into the register address $s3. If the $0 is used for the base address, the memory address is calculated by adding the zero value ($0) to the offset (1), as shown below:
- lw $s3, 1($0) # read memory word 1 into $s3
As a result of this instruction, the register address $s3 holds the data 0xF2F1AC07 as shown in the figure below:
Fig. ‑. Load data from Memory to Register with lw instruction
The above figure shows the register address $s3 holds the word of data at the memory address 1 after executing the instruction lw $s3, 1($0).
Write Word-Addressable Memory
We can store the data located at a register into memory with the store word (sw) instruction.
The following instruction exemplifies the format of the sw instruction.
- sw $t4, 0x7($0)
where $t4 is the register address. We can store the data located at the register address $t4 into the memory, where the memory address is calculated by adding the base address ($0) to the offset value (7), i.e. $0 + 7.
Let’s store the value of the register address $t4 into the memory address 7. If the $0 is used for the base address, the memory address is calculated by add the zero value ($0) to the offset (7), as shown below:
- sw $t4, 0x7($0) # write the value of $t4 into memory word 7
where the offset can be written in decimal (default) or hexadecimal. As a result of this instruction, the memory address 7 holds the value 0x6A049C04 of the register address $t4, as shown below:
Fig. ‑. Store the value of Register into Memory with sw instruction
Byte-Addressable Memory
Each data byte has unique address. We can load/store words or single bytes with load byte (lb) and store byte (sb). Since we are using 32-bit word that is 4 bytes, the word address is increased by 4. That means the address of a memory word must now be multiplied by 4. For example, the address of memory word 2 is 2 × 4 = 8 and the address of memory word 10 is 10 × 4 = 40 (0x28). Keep in mind that MIPS is byte-addressed, not word-addressed.
Power of the Stored Program
Both 32-bit instructions and data are stored in memory. The only difference between two applications is the sequence of instructions. We do not require large amounts of time and effort to reconfigure or rewire hardware to run a new program. We only require writing the new program to memory. When executing program, the processor fetches (reads) instructions from memory to instruction register in sequence and performs the specific operation. Even large and complex programs are simplified to a series of memory reads and instruction executions.
For example, the assembly code and the corresponding machine code are given as below:
Assembly Code | Machine Code |
---|---|
lw $t2, 32($0) add $s0, $s1, $s2 addi $t0, $s3, -12 sub $t0, $t3, $t5 | 0x8C0A0020 0x02328020 0x2268FFF4 0x016D4022 |
The whole machine codes are stored in memory, as shown in Fig. 10-4. In this example, the first instruction is stored at the memory address 0x00400000. The next instruction is stored at the memory address 0x00400004, etc. Note that the memory address is increased by 4 because it is a byte address (4 × 8 = 32). The program counter in the processor keeps track of current instruction. Each instruction is executed in sequence.
Fig. ‑. Stored Program
The processor starts to interpreting machine code. The first six bits (opcode) tell how parse the rest of them. If opcode is all 0’s, the function field tells the arithmetic/logic operation; otherwise it tells operation.
Exercises
- The data values in the memory address are drawn below. MIPS Assembly code is given as follows:
lw $s0, 0($0)
lw $s1, 8($0)
lw $s2, 0xC($0)
What are the register values in $s0, $s1, and $s2?
Fig. ‑. Data Values in Memory Address
Answer)
lw $s0, 0($0) # read data word 0(0xABCDEF78) into $s0
lw $s1, 8($0) # read data word 2(0x01EE2842) into $s1
lw $s2, 0xC($0) # read data word 3(0x40F30788) into $s2
- Translate the following machine language code into assembly language.
0x2237FFF1
Answer) the machine language code 0x2237FFF1 is expanded into as below:
2 2 3 7 F F F 1 (hexadecimal)
0010 0010 0011 0111 1111 1111 1111 0001 (binary, 32 bits)
0010 0010 0011 0111 1111 1111 1111 0001
8 17 23 -15
Opcode rs rt imm
The corresponding assembly code: addi $s7, $s1, -15.
- What is the assembly language statement corresponding to this machine instruction?
0x00AF8020
Answer)
Convert hexadecimal to binary
0 | 0 | A | F | 8 | 0 | 2 | 0 |
---|---|---|---|---|---|---|---|
0000 | 0000 | 1010 | 1111 | 1000 | 0000 | 0010 | 0000 |
Referring to the Table 9-2.
Op rs rt rd shamt funct
- 00101 01111 10000 00000 100000
→ add $s0, $a1, $t7
Logic Operations
MIPS instructions execute bitwise manipulation as shown below:
Table ‑. Instructions for Bitwise Manipulation
Logic operations | C operators | Java operators | MIPS instruction |
---|---|---|---|
Shift left | << | << | sll |
Shift right | >> | >>> | srl |
Bitwise AND | & | & | and, andi |
Bitwise OR | | | | | or, ori |
Bitwise NOT | ~ | ~ | nor |
The instructions in the above table are useful for extracting and inserting groups of bits in a word.
Shift Operations
The shift operation is a R-type instruction and the field value is shown below:
Fig. ‑. The field Value of Shift Operations
where the field shamt tells how many positions to shift.
There are two logic shifts, i.e., the shift left logic (sll) and the shift right logic (srl). The sll shifts the bits left and fills the empty bits with 0 bits. Sll by i bits is equivalent to multiply by 2i. The srl shifts the bits right and fills the empty bits with 0 bits. Srl by i bits is equivalent to divide by 2i (unsigned only).
Let’s look at how the shift operations work with some example. The MIPS assembly codes and field values are shown below:
Fig. ‑. Examples of Shift Operation
The source register address $s1 has the following field value:
After executing the above MIPS assembly code, the target register addresses $t0 and $s0 have the following field values:
AND Operations
AND operation is useful to mask bits in a word. When executing AND operation, some bits are selected if both bits are TRUE; otherwise, it clears others to 0.
For example, let’s execute AND operation of the values located in the register addresses $t1 and $t2, and store the result in the register address $t0:
- and $t0, $t1, $t2
Only the selected bits are set to TRUE (1’s), whereas the other bits are set to all FALSE (0’s), as shown below:
Fig. ‑. Examples of AND Operation
OR Operations
OR operation is useful to include bits in a word. When executing OR operation, it sets some bits to TRUE (1’s) and leaves others unchanged.
For example, let’s execute OR operation of the values located in the register addresses $t1 and $t2, and store the result in the register address $t0:
- or $t0, $t1, $t2
As shown below, some bits are set to TRUE highlighted in blue. The other bits are unchanged.
Fig. ‑. Examples of OR Operation
NOT Operations
NOT operation is useful to invert bits in a word. That means it change 0 bit to 1 bit, and 1 bit to 0 bit. MIPS has a NOR 3-operand instruction that has the same function as the NOT instruction.
- a NOR b == NOT (a OR b)
we can invert bits in a word using NOR 3-operand instruction, as shown below:
- nor $t0, $t1, $zero
Since the register 0 always holds zero value, the NOR 3-operand instruction can execute the above instruction and return the result of NOT operation as shown below:
Fig. ‑. Examples of NOT Operation
Exercises
- The source register addresses $s1 and $s2 are given below:
We would like to execute the following MIPS assembly code:
AND $s3, $s1, $s2
OR $s4, $s1, $s2
NOR $s5, $s1, $s2
XOR $s6, $s1, $s2
What field values do the target register addresses, i.e., $s3, $s4, $s5, and $s6, hold?
Conditional Operation
The conditional operations are used to branch to a labeled instruction if a condition is true. If the condition is false, the instructions are executed sequentially.
The following instructions show the conditional operations:
- beq rs, rt, L1
The branch on equal (beq) tests the equality of the condition. It branches to the instruction labeled L1 if the condition (rs == rt) is true.
- bne rs, rt, L1
The branch on not equal (bne) tests the inequality of the condition. It branches to the instruction labeled L1 if the condition (rs != rt) is true.
- j L1
In the jump (j), it jumps to the instruction labeled L1 unconditionally.
Let’s look at an example how the conditional branch instruction beq is used with the following MIPS assembly code:
addi $s0, $0, 4 # $s0 = 0 + 4 = 4 addi $s1, $0, 1 # $s1 = 0 + 1 = 1 sll $s1, $s1, 2 # $s1 = 1 << 2 = 4 beq $s0, $s1, target # $s0 == $s1, so branch is taken addi $s1, $s1, 1 # not executed sub $s1, $s1, $s0 # not executed Target: add $s1, $s1, $s0 # $s1 = 4 + 4 = 8 |
Fig. ‑. Examples of conditional instruction beq
The first two instructions set the values of the register addresses, $s0 to 4 and $s1 to 1. The value of the register address $s1 is multiplied by 4 (=22) using the instruction sll. Since the equality of the conditional instruction beq is true, the branch is taken. Two instructions, addi and sub, are not executed. The value of the source register $s1 is set to 8.
Let’s look at the following example how the conditional branch bne is used with MIPS assembly codes:
addi $s0, $0, 4 # $s0 = 0 + 4 = 4 addi $s1, $0, 1 # $s1 = 0 + 1 = 1 sll $s1, $s1, 2 # $s1 = 1 << 2 = 4 bne $s0, $s1, target # $s0 == $s1, so branch is not taken addi $s1, $s1, 1 # $s1 = 4 + 1 = 5 sub $s1, $s1, $s0 # $s1 = 5 – 4 = 1 target: add $s1, $s1, $s0 # $s1 = 1 + 4 = 5 |
Fig. ‑. Examples of conditional instruction bne
The first three instructions (two addi and an sll) set the values of the register addresses, $s0 to 4 and $s1 to 4. Since the inequality of the conditional instruction bne is false, the branch is not taken. Two instructions, addi and sub, are executed. The value of the source register address $s1 is set to 5 in this case.
Let’s look at an example how the unconditional branch j is used with the following MIPS assembly codes:
addi $s0, $0, 4 # $s0 = 4 addi $s1, $0, 1 # $s1 = 1 j target # jump to target addi $s1, $s1, 1 # not executed sub $s1, $s1, $s0 # not executed target: add $s1, $s1, $s0 # $s1 = 1 + 4 = 5 |
Fig. ‑. Examples of unconditional branch j
The first two instructions set the values of the register addresses, $s0 to 4 and $s1 to 1. The unconditional branch j jumps to the target of the instruction. Two instructions, addi and sub, are not executed in this case. The value of the source register address $s1 is set to 5 with the last instruction.
The unconditional branch, Jump register (jr) is used to jump to the address held in a register. The following MIPS assembly code includes the unique address:
0x00002000 addi $s0, $0, 0x2010 # $s0 = 0x2010 0x00002004 jr $s0 # jump to 0x00002010 0x00002008 addi $s1, $0,1 # not executed 0x0000200c sra $s1, $s1, 2 # not executed 0x00002010 lw $s3, 44($s1) # executed after jr |
Fig. ‑. Examples of unconditional branch jr
The fist instruction sets the value of the register address $s0 to 0x2010. The second instruction, Jump register (jr) jumps to the address 0x00002010 that was held in the register $s0.
Conditional Statements
There are conditional statements commonly used in high-level languages, as shown below:
- if statements
- if/else statements
- while loops
- for loops
Let’s look at how those conditional statements are translated into MIPS assembly code.
If statements
The high-level code with if statement is shown below:
if (i == j) f = g + h; f = f – i; |
If the condition is true, the code executes the add operation, followed by the subtract operation. If the condition is false, it won’t execute the add operation. It only executes the subtract operation. Since all the instruction is executed in sequence, the if conditional statement is translated into the MIPS assembly code with bne instruction.
# $s0 = f, $s1 = g, $s2 = h # $s3 = i, $s4 = j bne $s3, $s4, L1 # if i ≠ j add $$s0, $s1, $s2 # f = g + h L1: sub $s0, $s0, $s3 # f = f – i |
The conditional branch bne is taken if the register value $s3 is not equal to $s4, and then jumps to the target of the instruction sub. If the two register values are same, the conditional branch bne is not taken. Both add and sub instructions are executed.
If/else statements
The high-level code with if/else statement is shown below:
if (i == j) f = g + h; else f = f – i; |
If the condition is true, the code executes the add operation; otherwise it executes the subtract operation. That means it executes either add or sub operation. This conditional statement is translated into MIPS assembly code with both bne and j instructions.
# $s0 = f, $s1 = g, $s2 = h # $s3 = i, $s4 = j bne $s3, $s4, else # if i ≠ j add $$s0, $s1, $s2 # f = g + h j done # skip else else: sub $s0, $s0, $s3 # f = f – i done: |
The conditional branch bne is taken if the register value $s3 is not equal to $s4, and then jumps to the target of the instruction line else. After executing the instruction sub, the program is terminated.
If the two register values are same, the conditional branch bne is not taken. It executes the next instruction add in sequence, followed by the unconditional branch j that jumps to the end of this program.
While loops
The high-level code with while loop is shown below:
int pow = 1; int x = 0; while(pow != 128) { pow = pow * 2; x = x + 1; } |
If the condition of the while statement is true, the code executes all the instructions within the curly bracket {}; otherwise it terminate the program. This statement is translated into MIPS assembly code with both beq and j instructions.
# $s0 = pow, $s1 = x addi $s0, $0, 1 # pow = 1 addi $s1, $0, 0 # x = 0 addi $t0, $0, 128 # $t0 = 128 while: # comparison beq $s0, $t0, done # if pow=128 sll $s0, $s0, 1 # pow=pow*2 addi $s1, $s1, 1 # x = x + 1 j while done: |
The conditional branch beq is taken if the register value $s0 is equal to $t0, and then jumps to the target of the instruction line done. If the register value $s0 is not equal to $t0, the branch is not taken and it executes the next instructions in sequence, followed by the unconditional branch j that jumps to the target of the instruction while of this program.
For loops
The high-level code with for loop is shown below:
int sum = 0; for(i=0;i!=10;i=i+1) { sum = sum + i; } |
The integer variable sum is initialized with 0. In the for loop, there are three instructions, as shown below:
- index i initialized with 0; i = 0;
- the condition of for loop; i != 10;
- increment/decrement of the index i; i = i + 1
After initializing the index i, the statement checks the condition. If the condition is true, it executes all the instructions within the curly bracket { }. After increasing the value of the index i by 1, the statement checks the condition again. If the condition is false, it terminates the program. If the condition is true, it repeats all the previous steps until the condition becomes false. This statement is translated into MIPS assembly code with both beq and j instructions.
# $s0 = i, $s1 = sum addi $s1, $0, 0 # sum = 0 addi $s0, $0, 0 # i = 0 addi $t0, $0, 10 # $t0 = 10 for: beq $s0, $t0, done # if i == 10 add $s1, $s1, $s0 # sum=sum+i addi $s0, $s0, 1 # i = i + 1 j for done: |
The first three instructions initialize the register values, i.e., $s0, $s1 and $t0. If the register value $s0 is equal to $t0, the conditional branch beq is taken, and then jumps to the target of the instruction line done. If the register value $s0 is not equal to $t0, the branch is not taken and then it executes the next instructions, i.e., add and addi instructions in sequence. The add instruction updates the register value $s1 that is equivalent to the integer variable sum. The addi instruction increases the value of the index i by 1. The unconditional branch j jumps to the target of the instruction line for of this program.
Loops using slt
The high-level code with for loop is shown below:
int sum = 0; for(i=1;i<101;i=i*2) { sum = sum + i; } |
The integer variable sum is initialized with 0. In the for loop, there are three instructions, as shown below:
- index i initialized with 1; i = 1;
- the condition of for loop; i < 101;
- increment/decrement of the index i; i = i * 2
After initializing the index i, the statement checks the condition. If the condition is true, it executes all the instructions within the curly bracket { }. After multiplying the value of the index i with 2, the statement checks the condition again. If the condition is false, it terminates the program. If the condition is true (the value of the index i is less than 101), it repeats all the previous steps until the condition becomes false. Since there is the less than condition in the loop, this statement is translated into MIPS assembly code with slt, beq, and j instructions, where the set less than (slt) instruction sets the destination register value to 1 if the first register operand is less than the second register operand.
# $s0 = i, $s1 = sum addi $s1, $0, 0 # sum = 0 addi $s0, $0, 1 # i = 1 addi $t0, $0, 101 # $t0 = 101 loop: # if (i < 101) $t1=1, else $t1 = 0 slt $t1, $s0, $t0 beq $t1, $0, done # if $t1=0 add $s1, $s1, $s0 # sum = sum + i sll $s0, $s0, 1 # i = i * 2 j loop done: |
After initializing the register values, there comes the instruction line loop. In the slt instruction, the register value $s0 is compared with the register value $t0. If $s0 is less than $t0, it sets the value of the register $t1 to 1; otherwise sets to 0. The beq instruction compares the register value $t1 with $0. If the register value $t1 is 1 ($s0 < $t0), the branch is not taken and it executes the next instructions in sequence and updates the variables sum and i, followed by the unconditional branch j that jumps to the target of the instruction loop of this program. If the register value $t1 is 0 ($s0 => $t0), the branch is taken and it terminates the program.