What do these Opcodes do?

Viewed 160

I am working through the Western Digital tutorial for RISC-V assembly. I am relatively new to assembly programming. So I have a few clarifying questions, and I can't seem to find a real dummy friendly tutorial.

Here is the code in question on GITHub.

# SPDX-License-Identifier: Unlicense
# Copyright (c) 2018 Western Digital Corporation or its affiliates.

.section .text
.align 2
.globl setupGPIO

.include "memory_map.inc"
.include "gpio.inc"

setupGPIO:
    addi sp, sp, -16            # Allocate Stack Frame

    #First Question
    sw ra, 12(sp)               # Save return address onto the stack

    li t0, GPIO_CTRL_ADDR       # Load the base GPIO address
    li t1, GPIO_RGB_PINS        # Get the RGP Pins offset
    sw t1, GPIO_OUTPUT_EN(t0)   # Enable RGB pins as output pins
    sw t1, GPIO_OUTPUT_XOR(t0)  # Set the XOR to that the pins are Active High

    #Second Question
    sw x0, GPIO_OUTPUT_VAL(t0)  # Set all writable GPIO pins to zero

    #Third Question
    lw ra, 12(sp)               # Restore the return address
    addi sp, sp, 16             # Deallocate stack frame
    ret

So, my questions are:

  1. What is the 12 doing? I thought the stack pointer has already been shifted 16 bits in the previous instruction. I'm generally confused about why move down 16 bits in memory, but then add 12 back.
    Also, is this syntax saying the same thing in x86 as [eax+12]?

  2. The destination operand has always been on the left, is this backwards only for the x0 register?

  3. I've noticed that we have not used the stack pointer at all, do we really need to do all this? Also, what is the difference between sw and lw? They have both been used and I'm not sure what they accomplish or why one would be used over the other.

1 Answers

1) subtracting 16 from the stack pointer reserves space on the stack for the current frame. In order to store things into that reserved space, you add an offset (in this case 12) to the decremented stack pointer.

2) sw stores a word (in this case the value 0, which x0 always contains) from a register to memory, and you're right (IMO) that the order of arguments looks backwards compared to the rest of the syntax.

3) a) we've used the stack pointer to save and restore the return address, which in this small function that doesn't itself make a call isn't needed, but as a standard boilerplate for function entry and exit, it's a helpful pattern. The stack is used to store local variables that don't fit in registers (or need to be on the stack to support recursion), and learning how to work with it is part of what they're showing you here.

3 b) sw stores a value from a register into memory, and lw loads from memory and stores into a register. In a device driver, memory locations are how you communicate with the device - storing to memory lets you give information (either data or control information) to the device, loading from memory is how you read information from the device.

Related