vak: (Default)
[personal profile] vak
Выдавать Hello World на ассемблере научились, а давайте теперь сбацаем что-нибудь простое арифметическое. Факториал посчитаем, к примеру. Дело нехитрое.
        .text
factorial:
mv t0, a0 # Устанавливаем начальное значение счётчика
1: addi t0, t0, -1 # Уменьшаем счётчик в регистре t0
beqz t0, 2f # Если получился ноль, идём на выход
mul a0, a0, t0 # Умножаем регистр результата на счётчик
j 1b # На следующую итерацию цикла
2: ret # Возвращаем результат в регистре a0
Попробуем вызвать эту функцию и показать результат... Однако натыкаемся на проблему! Как напечатать целое число из ассемблера? Из Си это сделать нетрудно, есть развитая библиотека libc, а в ней printf(). Ничего подобного не существует для ассемблера. Попытка вызвать Си-шный printf() из ассемблера плохо заканчивается. Потому что библиотека libc не проинициализирована толком. Для этого нужно линковаться с crt0.o и прочей ерундой. Выходит слишком громоздко.

Единственный выход - написать все нужные процедуры печати на ассемблере. Что мы и сделаем. Начнём с печати целого десятичного беззнакового числа.
#include <sys/syscall.h>
.text
.globl print_uns
print_uns:
mv a2, sp # end
addi sp, sp, -32 # allocate buf
mv a1, a2 # ptr
li a3, 10 # base
1:
remu a5, a0, a3 # value % base
addi a1, a1, -1 # --ptr
addi a5, a5, 48 # + '0'
sb a5, 0(a1) # *ptr = character
mv a5, a0 # old value
divu a0, a0, a3 # value /= base
bgeu a5, a3, 1b # if (old value >= base) continue

sub a2, a2, a1 # end - ptr
li a0, 1 # stdout
li a7, SYS_write # write() system call
ecall

addi sp, sp, 32
ret
Понадобится также процедура печати произвольной текстовой строки.
#include <sys/syscall.h>
.text
.globl print_string
print_string:
addi sp, sp, -16 # allocate space in stack
sd ra, 0(sp) # save return address
sd a0, 8(sp) # save string pointer

call strlen
mv a2, a0 # byte count
ld a1, 8(sp) # restore string pointer
li a0, 1 # stdout
li a7, SYS_write # write() system call
ecall

ld ra, 0(sp) # restore return address
addi sp, sp, 16 # free space in stack
ret
Заметьте, чтобы напечатать строку, требуется сначала посчитать её длину. Делаем функцию strlen().
        .text
.globl strlen
strlen: # a0 = const char *str
addi t1, a0, 1 # ptr + 1
1:
lb t0, 0(a0) # get byte from string
addi a0, a0, 1 # increment pointer
bnez t0, 1b # continue if not end

sub a0, a0, t1 # compute length - 1 for '\0' char
ret
Ну и отдельная процедура для выдачи конца строки.
#include <sys/syscall.h>
.text
.globl print_newline
print_newline:
li a7, SYS_write # write() system call
li a0, 1 # stdout
la a1, newline # string
li a2, 1 # one character
ecall
ret
newline:
.string "\n"
Теперь можем соорудить вызов факториала и показать результат.
#include <sys/syscall.h>
.globl _start
_start:
ld a0, input
call print_uns

la a0, text
call print_string

ld a0, input
call factorial
call print_uns

call print_newline

li a7, SYS_exit # exit the program
li a0, 0 # status code
ecall

.align 3
input: .dword 20
text: .string "! = "
Компилируем, запускаем.
$ cpp factorial.S | as -o factorial.o -

$ cpp print_uns.S | as -o print_uns.o -

$ cpp print_newline.S | as -o print_newline.o -

$ cpp print_string.S | as -o print_string.o -

$ as -o strlen.o strlen.s

$ ld -o factorial factorial.o print_uns.o print_newline.o print_string.o strlen.o

$ file factorial
factorial: ELF 64-bit LSB executable, UCB RISC-V, double-float ABI, version 1 (SYSV), statically linked, not stripped

$ size factorial
text data bss dec hex filename
268 0 0 268 10c factorial

$ ./factorial
20! = 2432902008176640000
Всё работает как положено. Размер программы 268 байт. Но становится понятно, почему народ перестал программировать на ассемблере. Всякую мелось приходится делать самому: никаких полезных библиотек. Напомню, что всё это происходит под Ubuntu на процессоре PIC64.

Файлы можно взять здесь: github.com/sergev/vak-opensource/tree/master/languages/assembler/riscv
This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org