Skip to content

Introduction

Assembly

Register ABI Name (alias) Description
pc pc Program counter (where the next instruction is)
x0 zero Hardwired zero (always reads as zero)
x1 ra Return address
x2 sp Stack pointer
x5 - x7 t0 - t2 Temporary registers
x8 fp Stack frame pointer
x10 - x11 a0 - a1 Function arguments/return values
x12 - x17 a2 - a7 Function arguments
x18 - x27 s0 - s11 Temporary registers saved across calls
x28 - x31 t3 - t6 Temporary registers
ra - When you call a function using an instruction like jal, the address of the next instruction is stored is stored here so the function knows where to return when it returns.

fp - It is used to keep track of the start of the current function’s frame stack.

t3-t6 - Caller-Saved

s2-s11 - Callee-Saved

Memory access

lw a0, (a1)  // Read a word (32-bits) from address in a1
             // and store it in a0. In C, this would be: a0 = *a1;
sw a0, (a1)  // Store a word in a0 to the address in a1.
             // In C, this would be: *a1 = a0;

Branch Instructions

    bnez    a0, <label>   // Go to <label> if a0 is not zero
    // If a0 is zero, continue here

<label>:
    // If a0 is not zero, continue here

Other branching instructions - bnez, bne, beq, blt

Functions Calls

    li  a0, 123      // Load 123 to a0 register (function argument)
    jal ra, <label>  // Jump to <label> and store the return address
                     // in the ra register.

    // After the function call, continue here...

// int func(int a) {
//   a += 1;
//   return a;
// }
<label>:
    addi a0, a0, 1    // Increment a0 (first argument) by 1

    ret               // Return to the address stored in ra.
                      // a0 register has the return value.

jal (jump and link) is used to to go to a specific label and store the return value

Note - a0-a7 are used for function arguments and a0 is used for return value

Stack

LIFO memory space, grows downwards.

To save a value into stack, decrement the stack pointer and store the value (push operation)

    addi sp, sp, -4  // Move the stack pointer down by 4 bytes
                     // (i.e. stack allocation).

    sw   a0, (sp)    // Store a0 to the stack

To load a value from the stack, load the value and increment the stack pointer (pop operation)

lw a0, (sp)
addi sp, sp, 4

CPU Modes

The CPU has three modes

M-mode Mode in which OpenSBI (i.e. BIOS) operates.
S-mode Mode in which the kernel operates, aka. "kernel mode".
U-mode Mode in which applications operate, aka. "user mode".

Privileged Instructions

Opcode and operands Overview Pseudocode
csrr rd, csr Read from CSR rd = csr;
csrw csr, rs Write to CSR csr = rs;
csrrw rd, csr, rs Read from and write to CSR at once tmp = csr; csr = rs; rd = tmp;
sret Return from trap handler (restoring program counter, operation mode, etc.)
sfence.vma Clear Translation Lookaside Buffer (TLB)
CSR (Control and Status Register) is a register that stores CPU settings

What doe SRET do?

1) Set the program counter to SEPC 2) Restoring the mode using SPP (Supervisor Previous Privilege) 3) Restoring interrupts (SIE and SPIE), if the interrupts were on before the trap, they are no turned back on 4) Clearing memory protection (MPRV - Modify Privilege) bit allows kernel to access memory as if it were a lower privilege

Inline assembly

uint32_t value;
__asm__ __volatile__ ("csrr %0, sepc": "=r"(value));
__asm__ __volatile__("assembly" : output operands : input operands : clobbered registers);

=r (register) for output operands, and r for input operands.

Output and input operands can be accessed in the assembly code using %0, %1, etc. in order of starting from the output operands.

__asm__ __volatile__("csrw sscratch, %0":"r"(123));