Introduction
To convert a string to an integer in RISC-V assembly (a function
often called atoi), you must manually loop through each character
of the string, subtract the ASCII offset of '0', multiply your
current total by 10, and add the new digit.
Below is a complete, and optimized RISC-V assembly
implementation that handles positive integers, negative numbers,
and stops when encountering a null-terminator or non-digit character.
Code
Note: Convert this code to a function with a prologue, body,
and epilogue.
.data
.align 2
integer_string: .asciz "123"
.align 2
newline: .asciz "\n"
.text
.global main
# -------------------------------------------------------------------------
# Function: string_to_integer
# Arguments:
# a0 = pointer to the null-terminated string (char *)
# Return Value:
# a0 = parsed integer value
# -------------------------------------------------------------------------
string_to_integer:
# ---------------------------------------------------------------------
# function prologue?
# ---------------------------------------------------------------------
li t0, 0 # t0 = result accumulator (total = 0)
li t1, 0 # t1 = negative flag (0 = positive, 1 = negative)
li t4, 10 # t4 = constant 10 for decimal shifting
# ---------------------------------------------------------------------
# function body
# ---------------------------------------------------------------------
# 1. Check for optional sign characters ('+' or '-')
lb t2, 0(a0) # Load first character
li t3, 45 # ASCII code for '-' is 45
bne t2, t3, check_plus
li t1, 1 # Set negative flag to 1
addi a0, a0, 1 # Move string pointer to next character
j digit_loop
check_plus:
li t3, 43 # ASCII code for '+' is 43
bne t2, t3, digit_loop
addi a0, a0, 1 # Move string pointer past '+'
# 2. Main processing loop
digit_loop:
lb t2, 0(a0) # Load current character
beqz t2, finish # If character is '\0' (null-terminator),
# we are done
# Check if character is a valid digit ('0' to '9')
li t3, 48 # ASCII '0'
blt t2, t3, finish # If char < '0', exit loop
li t3, 57 # ASCII '9'
bgt t2, t3, finish # If char > '9', exit loop
# Convert ASCII character to raw integer digit
addi t2, t2, -48 # t2 = char - '0'
# Shift accumulator and add the new digit: total = (total * 10) + digit
mul t0, t0, t4 # total = total * 10
add t0, t0, t2 # total = total + digit
addi a0, a0, 1 # Advance pointer to next character
j digit_loop
# 3. Finalize and apply the sign
finish:
beqz t1, return_val # If negative flag is 0, skip negation
neg t0, t0 # Negate the accumulator (two's complement)
# ---------------------------------------------------------------------
# function epilogue?
# ---------------------------------------------------------------------
return_val:
mv a0, t0 # Place final result into return register a0
ret # Return to caller
# -------------------------------------------------------------------------
# main
# -------------------------------------------------------------------------
main:
la a0, integer_string
call string_to_integer
How the Algorithm Works
- Sign Handling: It checks the very first character. If it
detects a minus sign (-), it sets a tracking register (t1) to 1 so
it knows to negate the final integer at the end.
- ASCII to Integer Conversion: It loads one byte at a time
using lb (load byte). Because digits in ASCII are sequentially
arranged from 48 ('0') to 57 ('9'), subtracting 48 gives the true
numeric value of that specific character.
- Decimal Shifting: For each new digit encountered, the
existing total is multiplied by 10 before adding the new digit.
This progressively shifts the placeholder values to the left
(e.g., parsing "123" becomes 1, then 1*10 + 2 = 12, then
12*10 + 3 = 123).
- Safety Check: The code breaks out of the loop cleanly
if it hits the null terminator (0) or any invalid character
(like spaces or letters).