'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