Please disable your adblock and script blockers to view this page

Lecture notes on RISC-V assembly


MIPS
Assembly
t1
s3
t3
a1
a3
XOR
ft1
t2

t2 (>=
s0
bne
a2
RA
a7
aX
fa0



fcvt.s.d
Branching
10;i++
sd ra
sd a0
sd s1
ld s1
ABI

No matching tags

No matching tags

No matching tags

No matching tags

No matching tags

Positivity     52.00%   
   Negativity   48.00%
The New York Times
SOURCE: https://web.eecs.utk.edu/~smarz1/courses/ece356/notes/assembly/
Write a review: Hacker News
Summary

Understand the load and store instructions and data sizes. All registers that start with s (except sp) for saved are registers that are preserved across function calls. RISC-V is a load/store architecture, so integer instruction operands must be registers. sb t0, 8(sp)Stores (dereferences) from register t0 into memory address (sp + 8). The floating point instructions are prefixed with an f, such as fld, fsw, for floating-point load doubleword and floating point store word, respectively. Notice in the code above, we used the fadd.s instruction to tell the RISC-V processor to add two single-precision values (ft0 and ft1) and store it as a single precision value into ft2. Branching instructions are how function calls and conditionals are implemented in assembly. The branching instructions take three parameters: the two operands (registers) to compare, and then if that comparison holds true, a memory label of the instruction you want to execute. The assembly code above implements the following C++ loop. Whenever we use the saved registers or if we want to preserve a temporary register across a function call, we must save it on the stack. The code above saves the return address on the stack, calls printf, and then when printf returns, we load the old value of the return address back off the stack, and then deallocate by adding 8. However, all functions have a preamble, which is essentially setting up a stack frame for local storage, and an epilogue, which usually entails loading saved registers and return address and moving the stack pointer before returning. This code shows that we first allocate 32 bytes from the stack, which is the size of 4 registers. You can see that I subtract all of the necessary space off of the stack first, store the values, run my code, and then execute the epilogue. This function requires that int a be in the register a0, int *b have the memory address that b points to in a1, and the value of float c in fa0. For float versus double, we choose instruction.s versus instruction.d. For example, fadd.s fa0, ft0, ft1 adds single-precision values and fadd.d fa0, ft0, ft1 adds double-precision values.

As said here by