diff options
| author | Miquel Sabaté Solà <mikisabate@gmail.com> | 2024-08-12 14:50:07 +0000 |
|---|---|---|
| committer | Miquel Sabaté Solà <mikisabate@gmail.com> | 2024-12-05 16:02:17 +0100 |
| commit | 8e968078e6aee470a02bed7728100b2df1877e90 (patch) | |
| tree | 8f64d8a0e8d8e7a9329a8239fd3c57ce217d5ff7 | |
| parent | c173df46a0a12fe52fdf13323f967e9117462096 (diff) | |
| download | farga-8e968078e6aee470a02bed7728100b2df1877e90.tar.gz farga-8e968078e6aee470a02bed7728100b2df1877e90.zip | |
basics: Add some examples on RISC-V assembly
Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | basics/Makefile | 20 | ||||
| -rw-r--r-- | basics/factorial.S | 28 | ||||
| -rw-r--r-- | basics/main.c | 70 | ||||
| -rw-r--r-- | basics/string.S | 93 |
5 files changed, 213 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3dec258 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +basics/basics diff --git a/basics/Makefile b/basics/Makefile new file mode 100644 index 0000000..90e88ef --- /dev/null +++ b/basics/Makefile @@ -0,0 +1,20 @@ +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +CCFLAGS = +EXE = basics + +DEBUG = +ifneq ($(strip $(DEBUG)),) + CCFLAGS += -g +endif + +.PHONY: all +all: clean $(EXE) + +.PHONY: $(EXE) +$(EXE): + gcc $(CCFLAGS) -o $(EXE) main.c factorial.S string.S + +.PHONY: clean +clean: + @rm -f $(EXE) diff --git a/basics/factorial.S b/basics/factorial.S new file mode 100644 index 0000000..7e3df12 --- /dev/null +++ b/basics/factorial.S @@ -0,0 +1,28 @@ +.text +.globl factorial +.type factorial, @function + +factorial: + // If input <= 1, just return the given thing. + addi t0, zero, 1 + bgt a0, t0, else + jr ra +else: + // Preserve the argument and the return address before the call. + addi sp, sp, -16 + sd a0, 0(sp) + sd ra, 8(sp) + + // Call the same function but with a value of argument-1. + addi a0, a0, -1 + call factorial + + // Restore back the original argument and the return address. Then multiply + // the original argument with the returned one from the previous call. + ld t1, 0(sp) + ld ra, 8(sp) + addi sp, sp, 16 + mul a0, t1, a0 + + // And return to the caller. + jr ra diff --git a/basics/main.c b/basics/main.c new file mode 100644 index 0000000..207fee2 --- /dev/null +++ b/basics/main.c @@ -0,0 +1,70 @@ +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +/* + * Returns the factorial for the given unsigned 64-bit integer. + * + * Implemented in factorial.S + */ +int factorial(uint64_t n); + +void test_factorial() +{ + assert(factorial(0) == 0); + assert(factorial(1) == 1); + assert(factorial(2) == 2); + assert(factorial(3) == 6); + assert(factorial(4) == 24); + + printf("factorial:\tOK\n"); +} + +/* + * Returns a pointer with the same given string but reversed. Note that the + * string cannot be in read-only space since the reversal is done in-place. + * + * Implemented in string.S + */ +char * reverse_string(char *str); + +void test_reverse_string() +{ + assert(reverse_string(NULL) == NULL); + assert(reverse_string("") == ""); + + char s1[] = "This is a string."; + assert(strcmp(reverse_string(s1), ".gnirts a si sihT") == 0); + + char s2[] = "."; + assert(strcmp(reverse_string(s2), ".") == 0); + + printf("reverse_string:\tOK\n"); +} + +/* + * Returns true if the given string is a palyndrome, false otherwise. + * + * Implemented in string.S + */ +bool is_palyndrome(char *str); + +void test_is_palyndrome() +{ + assert(is_palyndrome(NULL) == 0); + assert(is_palyndrome("") == 0); + assert(is_palyndrome("aba") == 1); + assert(is_palyndrome("aaa") == 1); + assert(is_palyndrome("This is a a si sihT") == 1); + + printf("is_palyndrome:\tOK\n"); +} + +int main() +{ + test_factorial(); + test_reverse_string(); + test_is_palyndrome(); +} diff --git a/basics/string.S b/basics/string.S new file mode 100644 index 0000000..e04e4de --- /dev/null +++ b/basics/string.S @@ -0,0 +1,93 @@ +.text + +.globl reverse_string +.type reverse_string, @function + +// char * reverse_string(char *str); +reverse_string: + // Return early on null pointer. + beq a0, zero, end + + // Preserve the original pointer. + addi sp, sp, -8 + sd a0, 0(sp) + + // Set `t0` to point to the end of the string. + add t0, a0, zero +set_end_ptr: + lbu t2, 0(t0) + beq t2, zero, end_ptr_done + addi t0, t0, 1 + j set_end_ptr + + // We are done iterating, if `a0` and `t0` are equal, then there's nothing + // to be done and we can return early. Otherwise decrement `t0` so it points + // to the byte right before the null termination. +end_ptr_done: + beq a0, t0, reverse_done + addi t0, t0, -1 + +reverse_loop: + // Swap values between the two pointers. + lb t1, 0(a0) + lb t2, 0(t0) + sb t1, 0(t0) + sb t2, 0(a0) + + // Move pointers and check whether the pointers have already crossed. If + // they have not crossed yet there is still looping to be done. Otherwise + // we are done. + addi a0, a0, 1 + addi t0, t0, -1 + bltu a0, t0, reverse_loop + +reverse_done: + // Restore things back and return to the caller. + ld a0, 0(sp) + addi sp, sp, 8 +end: + jr ra + +.globl is_palyndrome +.type is_palyndrome, @function + +// bool is_palyndrome(char *str); +is_palyndrome: + // Return early on null pointer. + beq a0, zero, palyndrome_no + + // Set `t0` to point to the end of the string. + add t0, a0, zero +pal_set_end_ptr: + lbu t2, 0(t0) + beq t2, zero, pal_end_ptr_done + addi t0, t0, 1 + j pal_set_end_ptr + + // We are done iterating, if `a0` and `t0` are equal, then there's nothing + // to be done and we can return early. Otherwise decrement `t0` so it points + // to the byte right before the null termination. +pal_end_ptr_done: + beq a0, t0, palyndrome_no + addi t0, t0, -1 + +pal_loop: + // Swap values between the two pointers. + lb t1, 0(a0) + lb t2, 0(t0) + bne t1, t2, palyndrome_no + + // Move pointers and check whether the pointers have already crossed. If + // they have not crossed yet there is still looping to be done. Otherwise + // we are done. + addi a0, a0, 1 + addi t0, t0, -1 + bleu a0, t0, pal_loop + +palyndrome_yes: + li a0, 1 + jr ra + +palyndrome_no: + li a0, 0 + jr ra |
