'Could anyone help me to read 64 bit from console in 32 bit RISC-V
I am new to assembly, but could anyone teach me how to read 64 bit from console in 32 bit RISC-V?
.eqv SYS_EXITO, 10
.eqv CON_PRTSTR, 4
.eqv CON_PRTINT, 1
.eqv CON_RDINT, 5
.eqv BUFSIZE, 100
.data
prompt:
.asciz "Read 64 bit integer:"
result:
.asciz "Output:"
buf:
.space BUFSIZE
.text
main:
la a0, prompt
li a7, CON_PRTSTR
ecall
la a0, buf
li a1, BUFSIZE
li a7, CON_RDINT
ecall
Then we I input 4294967295, the following error occured.
Error in /private/var/folders/bf/t4py6npj0v38grsvrgvq1dx00000gn/T/hsperfdata_sotarosuzuki/riscv1.asm line 24: Runtime exception at 0x00400020: invalid integer input (syscall 5)
So, should I read the integers as string and convert it to integer? I have searched for this solution, but I cannot find it.
Solution 1:[1]
Yeah, if you can't use the toy system calls, read a string and do total = total*10 + digit
on it, where digit = c-'0'
. You'll need to do extended-precision multiply, so it's probably easier to do extended-precision shifts like (total << 3) + (total << 1)
.
Check compiler output on Godbolt. For example, GCC using shifts, clang using mul
/mulhu
(high unsigned) for the lo * lo
32x32=>64-bit partial product, and a mul
for the high half cross product (hi * lo
). It's fewer instructions, but depends on a RISC-V CPU with a fast multiplier to be faster than shift/or.
(RISC-V extended-precision addition is inconvenient since it doesn't have a carry flag, you need to emulate carry-out as unsigned sum = a+b;
carry = sum<a;
)
#include <stdint.h>
uint64_t strtou64(unsigned char*p){
uint64_t total = 0;
unsigned digit = *p - '0'; // peeling the first iteration is usually good in asm
while (digit < 10) { // loop until any non-digit character
total = total*10 + digit;
p++; // *p was checked before the loop or last iteration
digit = *p - '0'; // get a digit ready for the loop branch
}
return total;
}
Clang's output is shorter, so I'll show it. It of course follows the standard calling convention, taking the pointer in a0
, and returning a 64-bit integer in a pair of registers, a1:a0
:
# rv32gc clang 14.0 -O3
strtou64:
mv a2, a0
lbu a0, 0(a0) # load the first char
addi a3, a0, -48 # *p - '0'
li a0, 9
bltu a0, a3, .LBB0_4 # return 0 if the first char is a non-digit
li a0, 0 # total in a1:a0 = 0 ; should have done these before the branch
li a1, 0 # so a separate ret wouldn't be needed
addi a2, a2, 1 # p++
li a6, 10 # multiplier constant
.LBB0_2: # do{
mulhu a5, a0, a6 # high half of (lo(total) * 10)
mul a1, a1, a6 # hi(total) * 10
add a1, a1, a5 # add the high-half partial products
mul a5, a0, a6 # low half of (lo(total) * 10)
lbu a4, 0(a2) # load *p
add a0, a5, a3 # lo(total) = lo(total*10) + digit
sltu a3, a0, a5 # carry-out from that
add a1, a1, a3 # propagate carry into hi(total)
addi a3, a4, -48 # digit = *p - '0'
addi a2, a2, 1 # p++ done after the load; clang peeled one pointer increment before the loop
bltu a3, a6, .LBB0_2 # }while(digit < 10)
ret
.LBB0_4:
li a0, 0 # return 0 special case
li a1, 0 # because clang was dumb and didn't load these regs before branching
ret
If you want to go with GCC's shift/or strategy, it should be straightforward to see how that slots in to the same logic clang is using. You can look at compiler output for a function like return u64 << 3
to see which instructions are part of that.
And BTW, I wrote the C with compiling to decent asm in mind, making it easy for the compiler to transform it into a do{}while
loop with the condition at the bottom. I based it on the x86 asm in my answer on NASM Assembly convert input to integer?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |