[英]How to output an integer to screen risc-v assembly
The following is the assembly I have used in an attempt to print to console:以下是我用来尝试打印到控制台的程序集:
global _start
_start:
addi a0, x0, 1
addi a1, x0, 42
addi a7, x0, 63
ecall
addi a0, x0, 0
addi a7, x0, 93
ecall
.data
num:
.byte 6
I compiled with我编译了
riscv64-unknown-elf-as -o example.o example.S
riscv64-unknown-elf-ld -o example example.o
and run using spike and proxy kernel并使用尖峰和代理 kernel运行
spike pk example
No output is generated.没有生成 output。
This works on https://www.kvakil.me/venus/ with这适用于https://www.kvakil.me/venus/与
addi a0, x0, 1
addi a1, x0, 42
ecall
and prints 42.并打印 42。
Also, if I wanted to print the contents of num in the data segment, how would I go about it?另外,如果我想打印数据段中num的内容,我会怎么做go呢?
I managed a solution from Peter Cordes' answer.我从 Peter Cordes 的回答中管理了一个解决方案。 I am posting the implementation here in case someone needs it and for my own reference.
我在这里发布实现以防有人需要它并供我自己参考。
UPDATE:更新:
Steps:脚步:
System calls can be found here .系统调用可以在这里找到。
C code logically mirrors the assembly C 代码逻辑镜像装配
#include <unistd.h>
void num_print(long num){
unsigned int base = 10;
int sign_bit = 0;
char string[20];
char* end = string + 19;
char* p = end;
*p = '\n';
if (num < 0){
num = 0 - num;
sign_bit = 1;
}
do {
*(--p) = (num % base) + '0';
num /= base;
} while (num);
if (sign_bit)
*(--p) = '-';
size_t len = end - p;
write(1, p, len + 1);
}
int main(){
int arr[3] = {1234567, -1234567, 0};
for (int i=0; i < 3; i++){
num_print(arr[i]);
}
return 0;
}
Risc-v Assembly Risc-v 程序集
.global _start
.text
_start:
la s1, arr # s1: load arr address
addi s2, zero, 3 # s2: arr length
addi sp, sp, -8 # push 1 item to stack
sd ra, 0(sp) # save return address
mv s3, zero # s3: i loop counter
j compare_ipos
L1:
slli s4, s3, 3 # s4: i * 8
add s5, s1, s4 # s5: address of a[i]
ld a0, 0(s5) # a0: arr[i]
jal ra, num_print # call num_print
addi s3, s3, 1 # increment i
compare_ipos:
blt s3, s2, L1 # loop if i < 3
j exit
num_print:
addi sp, sp, -40 # create stack space
sd s0, 32(sp) # store frame pointer
addi s0, sp, 40 # new frame pointer
addi t0, zero, 0 # initialize sign_bit
addi t1, zero, 10 # divisor and new-line char
addi t2, s0, -16 # t2: string[n]
add a1, zero, t2 # a1: string[0] currently string[n]
addi t3, zero, '\n' # '\n' char
sb t3, 0(a1) # store '\n'
bge a0, zero, PVE # if num >= 0 go to L1 else get absolute
xori a0, a0, -1 # (num ^ -1)
addi a0, a0, 1 # num + 1
addi t0, zero, 1 # set sign-bit to 1
PVE:
remu t3, a0, t1 # num % 10
addi t3, t3, 48 # convert to ascii
addi a1, a1, -1 # decrement start pointer
sb t3, 0(a1) # store value
divu a0, a0, t1 # num /= 10
blt zero, a0, PVE # if num > 0 loop
beq t0, zero, print # if sign_bit = 0 go to print else, add '-' char
addi t3, zero, '-' # ascii '-'
addi a1, a1, -1 # decrement start pointer
sb t3, 0(a1) # store '-'
print:
sub t4, t2, a1 # t4: len -- string[n] - string[0]
addi a2, t4, 1 # len + 1
addi a0, zero, 1 # file descriptor to write to
addi a7, zero, 64 # pk SYS_write
ecall # transfer control to os
ld s0, 32(sp) # restore frame pointer
addi sp, sp, 40 # restore stack pointer
ret # return from function
exit:
ld ra, 0(sp) # restore ra
addi sp, sp, 8 # pop stack
addi a0, zero, 0 # return value
addi a7, zero, 93 # syscall exit code
ecall
.data
arr:
.dword 12345670, -12345670, 0
System calls depend on the environment.系统调用取决于环境。 "Toy" systems like Venus or RARS have their own set of toy system calls that do things like print an integer.
像 Venus 或RARS这样的“玩具”系统有自己的一套玩具系统调用,可以执行诸如打印 integer 之类的事情。
In a real-world system like GNU/Linux, true system calls that you can access with ecall
can only copy bytes to a file descriptor.在像 GNU/Linux 这样的真实系统中,您可以使用
ecall
访问的真正系统调用只能将字节复制到文件描述符中。 If you want to output text, you need to create text in memory in user-space and pass a pointer to a write system call.如果要 output 文本,则需要在用户空间的 memory 中创建文本,并将指针传递给 write 系统调用。
Spike + pk
is apparently more like Linux, with a POSIX write(2)
system call, not like those toy system-call environments where you could pass an integer directly to a print-int ecall
. Spike +
pk
显然更像 Linux,带有 POSIX write(2)
系统调用,不像那些可以将 integer 直接传递给 print-int ecall
的玩具系统调用环境。 https://www.reddit.com/r/RISCV/comments/dagvzr/where_do_i_find_the_list_of_stdio_system_etc/ has some examples and links. https://www.reddit.com/r/RISCV/comments/dagvzr/where_do_i_find_the_list_of_stdio_system_etc/有一些例子和链接。 Notably https://github.com/riscv/riscv-pk/blob/master/pk/syscall.h where we find
#define SYS_write 64
as the call number (goes in a7
) for a write
system call.值得注意的是https://github.com/riscv/riscv-pk/blob/master/pk/syscall.h我们发现
#define SYS_write 64
作为调用号(在a7
中)用于write
系统调用。
A write
system-call takes args: write(int fd, const void *buf, size_t count)
. write
系统调用需要 args: write(int fd, const void *buf, size_t count)
。
Formatting a binary integer into an ASCII string is something that library functions like printf
will do.将二进制 integer 格式化为 ASCII 字符串是
printf
类的库函数会做的事情。 Toy systems don't have a library, so they just put a few useful functions as system calls.玩具系统没有库,所以它们只是将一些有用的函数作为系统调用。 And if you want control over stuff like leading zeros or padding to a fixed width, you have to write it yourself.
如果你想控制诸如前导零或填充到固定宽度之类的东西,你必须自己写。 But on a system like Spike-pk, you only have simple Unix-like system calls and (perhaps?) no library at all, so you have to always do it yourself.
但是在像 Spike-pk 这样的系统上,你只有简单的类 Unix 系统调用和(也许?)根本没有库,所以你必须总是自己做。
With just Linux / Unix / Spike-pk system-calls, you'll want to do repeated division by 10 to get the decimal digits of a binary integer.仅使用 Linux / Unix / Spike-pk 系统调用,您需要重复除以 10 以获得二进制 integer 的十进制数字。 like in How do I print an integer in Assembly Level Programming without printf from the c library?
就像如何在没有 c 库中的 printf 的情况下在装配级编程中打印 integer 中的一样? which shows C and x86-64 assembly for Linux:
其中显示了 Linux 的 C 和 x86-64 组件:
char *itoa_end(unsigned long val, char *p_end) {
const unsigned base = 10;
char *p = p_end;
do {
*--p = (val % base) + '0';
val /= base;
} while(val); // runs at least once to print '0' for val=0.
// write(1, p, p_end-p);
return p; // let the caller know where the leading digit is
}
Translate to RISC-V assembly (or compile with gcc or clang, eg via https://godbolt.org/ ).转换为 RISC-V 程序集(或使用 gcc 或 clang 编译,例如通过https://godbolt.org/ )。 Reserving a small buffer on the stack is convenient.
在堆栈上保留一个小缓冲区很方便。
Also, if I wanted to print the contents of num in the data segment, how would I go about it?
另外,如果我想打印数据段中num的内容,我会怎么做go呢?
lw
the number into a register, then do the same thing as above. lw
将数字写入寄存器,然后执行与上述相同的操作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.