This example has a .s (lowercase) extension which means that it does not use the C pre-processor (unlike the previous example: assembly1).
The example illustrates immediate loading of operands and addresses, addition and subtraction, and logic operations. No use is made of data memory; no load/store instructions between registers and data memory.
This example can be run on the PIC32 Simulator or the PIC32 Starter Kit, but there is no output. You will have to step through the program and watch registers change. However, the purpose for this example is to explore how the assembler converts assembly language instructions into machine language. The most important tool for this purpose is the disassembly listing, which shows the decoded instructions corresponding to each source line. In the case of C programs, a single statement can yield many machine instructions, and typically the C statements are followed by the corresponding machine instructions. In the case of assembly language, each line corresponds (most frequently) to one machine instruction, and the assembly source is listed side-by-side with the machine language generated.
There are two kinds of immediate loads, both implemented through pseudo-instructions (macros).
Instruction | Description | Function |
---|---|---|
LI | Load Immediate | Rt = immediate_value (data) |
LA | Load Address | Rt = immediate_value (label) |
Both pseudo-instructions do essentially the same thing - load a 32-bit constant - but the source of the constant differs. Data constants are known when the program is compiled, addresses may not be known until the program is linked.
Following are some explicit instances of li and la in this example project:
21: main: 9D000018 240807D8 addiu t0,zero,2008 22: li $t0, 2008 # "load immediate" 9D00001C 24090012 addiu t1,zero,18 23: li $t1, 18For operands that are 16-bits or smaller in size, li translates to a single instruction, in this case addition of the immediate operand with zero.
The following two pseudo-instructions involve 32-bit operands:
9D000030 3C08ABCD lui t0,0xabcd 30: li $t0,0xABCD8421 # 32-bit constant 9D000034 35088421 ori t0,t0,0x8421 9D000038 3C099D00 lui t1,0x9d00 31: la $t1,main # "load address" 9D00003C 25290018 addiu t1,t1,24Since these are 32-bit constants, separate instructions are used to load the upper and lower parts of the register. The address loaded is 0x9d000018, the start of main.
I am not sure why ori is used in one instance and addiu is used in the other (unless it has something to do with the fact that the "sign" bit is set in the first instance.
The example project has the following instances of addition and subtraction, including the pseudo-instruction neg (negate):
9D000020 01091020 add v0,t0,t1 25: add $v0,$t0,$t1 # add and subtract 9D000024 01091822 sub v1,t0,t1 26: sub $v1,$t0,$t1 9D000028 00095022 sub t2,zero,t1 27: neg $t2,$t1 # macro for "negate" 9D00002C 010A5821 addu t3,t0,t2 28: addu $t3,$t0,$t2 # should be the same as t0 - t1
Negation (-x) is the same as the subtraction 0 - x.
The example project has instances of the four logic operations directly supported by the instruction set: and, or, nor, and xor. There is also an instance of the not pseudo-instruction.
9D000040 24080101 addiu t0,zero,257 33: li $t0, 0x0101 #logic operations 9D000044 24090011 addiu t1,zero,17 34: li $t1, 0x0011 9D000048 01091024 and v0,t0,t1 35: and $v0,$t0,$t1 9D00004C 01091025 or v0,t0,t1 36: or $v0,$t0,$t1 9D000050 01001027 nor v0,t0,zero 37: not $v0,$t0 # pseudo-instruction (macro) 9D000054 01091026 xor v0,t0,t1 38: xor $v0,$t0,$t1 9D000058 01091027 nor v0,t0,t1 39: nor $v0,$t0,$t1
The not operation ~x (or bitwise complement) is implemented by nor(zero, x).
The move operation (simple register transfer) is a pseudo-instruction, shown in the instance below taken from the example project:
9D00005C 00001021 addu v0,zero,zero 41: move $v0,$zero # pseudo-instruction (macro)
Source: mips1.zip.
# mips1.s illustrates arithmetic (add/subtract) and logical operations .global main .text /* This directive tells the assembler don't optimize * the order of the instructions and don't insert * 'nop' instructions after jumps and branches. */ .set noreorder /********************************************************************* * main() * This is where the PIC32 start-up code will jump to after initial * set-up. ********************************************************************/ .ent main # directive that marks symbol 'main' as function in ELF output main: li $t0, 2008 # macro for "load immediate" li $t1, 18 add $v0,$t0,$t1 # add and subtract sub $v1,$t0,$t1 neg $t2,$t1 # macro for "negate" addu $t3,$t0,$t2 # should be the same as t0 - t1 li $t0,0xABCD8421 # 32-bit constant la $t1,main # macro for "load address" li $t0, 0x0101 #logic operations li $t1, 0x0011 and $v0,$t0,$t1 or $v0,$t0,$t1 not $v0,$t0 # pseudo-instruction (macro) xor $v0,$t0,$t1 nor $v0,$t0,$t1 move $v0,$zero # pseudo-instruction (macro) jr $ra nop .end main # directive that marks end of 'main' function # and registers size in ELF output
--- C:\pic32\test\mips1.s ---------------------------------------------------------------------- 1: # mips1.s illustrates arithmetic (add/subtract) and logical operations 2: 3: .global main 4: 5: .text 6: 7: /* This directive tells the assembler don't optimize 8: * the order of the instructions and don't insert 9: * 'nop' instructions after jumps and branches. 10: */ 11: .set noreorder 12: 13: /********************************************************************* 14: * main() 15: * This is where the PIC32 start-up code will jump to after initial 16: * set-up. 17: ********************************************************************/ 18: 19: .ent main # directive that marks symbol 'main' as function in ELF output 20: 21: main: 9D000018 240807D8 addiu t0,zero,2008 22: li $t0, 2008 # macro for "load immediate" 9D00001C 24090012 addiu t1,zero,18 23: li $t1, 18 24: 9D000020 01091020 add v0,t0,t1 25: add $v0,$t0,$t1 # add and subtract 9D000024 01091822 sub v1,t0,t1 26: sub $v1,$t0,$t1 9D000028 00095022 sub t2,zero,t1 27: neg $t2,$t1 # macro for "negate" 9D00002C 010A5821 addu t3,t0,t2 28: addu $t3,$t0,$t2 # should be the same as t0 - t1 29: 9D000030 3C08ABCD lui t0,0xabcd 30: li $t0,0xABCD8421 # 32-bit constant 9D000034 35088421 ori t0,t0,0x8421 9D000038 3C099D00 lui t1,0x9d00 31: la $t1,main # macro for "load address" 9D00003C 25290018 addiu t1,t1,24 32: 9D000040 24080101 addiu t0,zero,257 33: li $t0, 0x0101 #logic operations 9D000044 24090011 addiu t1,zero,17 34: li $t1, 0x0011 9D000048 01091024 and v0,t0,t1 35: and $v0,$t0,$t1 9D00004C 01091025 or v0,t0,t1 36: or $v0,$t0,$t1 9D000050 01001027 nor v0,t0,zero 37: not $v0,$t0 # pseudo-instruction (macro) 9D000054 01091026 xor v0,t0,t1 38: xor $v0,$t0,$t1 9D000058 01091027 nor v0,t0,t1 39: nor $v0,$t0,$t1 40: 9D00005C 00001021 addu v0,zero,zero 41: move $v0,$zero # pseudo-instruction (macro) 9D000060 03E00008 jr ra 42: jr $ra 9D000064 00000000 nop 43: nop
Maintained by John Loomis, last updated 17 August 2008