Load Immediate, Add/Subtract, Logic Operations

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.

Immediate Loads

There are two kinds of immediate loads, both implemented through pseudo-instructions (macros).

Instruction Description Function
LILoad Immediate Rt = immediate_value (data)
LALoad 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, 18
For 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,24
Since 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.

Addition/Subtraction

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.

Logic Operations

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).

Move operation

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)

Assembly Source

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

Disassembly Listing

---  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