# ECAD and Architecture Practical Classes

## Assembler programming guide

Some tips on assembly language programming for RISC-V...

### Registers

Rather than local variables, we have registers to store temporary values and pass data to and from functions. The RISC-V calling convention dictates whether each register must be preserved by the caller or the callee, and which have special purposes. In particular, it specifies which registers are used for function arguments and return values.

To preserve data we can use the stack. This just involves decrementing the stack pointer `sp` appropriately and then storing data to the location that `sp` points to. Since you will only be writing simple assembly functions this may not be needed.

### Instructions

The RISC-V Green Card has a full listing (note that our architecture is RV32I). Here are the instructions you will likely find most useful:

```    beq  rs1, rs2, label    # branch if rs1 == rs2
# similarly: bne (!=), blt (<), bge (>=)

add  rd, rs1, rs2       # rd := rs1 + rs2
sub  rd, rs1, rs2       # rd := rs1 - rs2
# similarly: xor, or, and

sll  rd, rs1, rs2       # rd := rs1 << rs2
srl  rd, rs1, rs2       # rd := rs1 >> rs2
sra  rd, rs1, rs2       # rd := rs1 >>> rs2 (arithmetic shift sign-extends the top bits of rs1)

slt  rd, rs1, rs2       # if rs1 < rs2 then rd := 1 else rd := 0

addi rd, rs, immediate  # rd := rs1 + immediate. The immediate must be at most 12-bits.
# similarly xori, ori, andi, slli, srli, srai, slti

sw   rs2, offset(rs1)   # *(rs1 + offset) := rs2. Store the value in rs2 to the address (rs1 + offset).
lw   rd, offset(rs1)    # rd := *(rs1 + offset). Load the value at address (rs1 + offset) into rd.
```

The assembler also provides a variety of pseudo-instructions, which correspond to one or two more complicated instructions but make life easier for the pogrammer. You may find the following useful:

```    li   rd, immediate      # rd := immediate
mv   rd, rs             # rd := rs

bgt  rs1, rs2, label    # branch to label if rs1 > rs2
ble  rs1, rs2, label    # branch to label if rs1 <= rs2
beqz rs, label          # branch to label if rs1 == 0
# similarly: bnez, blez, bgez, bltz, bgtz

```

We have also provided some special functionality for debugging:

```    ecall                   # stops the simulator when testing in ModelSim
csrw  0x7B2, rs         # output a debug message in the trace containing the value of rs
```

### Control flow

In high level languages `goto` is to be avoided, but in assembler that is the only control flow mechanism. This means we must translate for-loops and if-else statements into linear versions using branches instead.

For instance, a conditional statement in C such as:

```    if (i > 42) {
// true path
} else {
// false path
}
```

Might become in assembly:

```        li   t1, 42         # initialize state for conditiom
ble  t0, t1, else   # if condition not met, go to false path
...                 # true path
j    end
else:
...                 # false path
end:
...
```

And a simple for loop in C:

```    for (int i = 0; i < 10; i++) {
// something terribly clever
}
```

Becomes in assembly:

```        li   t0, 0          # initialize the loop variable
li   t1, 10         # initialize the condition variable
loop:
bge  t0, t1, end    # if the loop condition is not met, exit
...                 # something terribly clever
addi t1, t1, 1      # increment the loop variable
j    loop           # loop
end:
...
```

In many cases the number of branches required can be reduced by updating the loop variable at the start of the loop or checking the condition at the end. Extra care must be taken when there are `break` statements within the loop to ensure that the loop variable is always updated.

Do and while loops are implemented similarly.