From ab03d3be21f229be8c28dba292d5da237a929a73 Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Wed, 28 Aug 2024 22:25:56 +0200 Subject: Restructure the whole thing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be something more than just messing around with RISC-V stuff. Signed-off-by: Miquel Sabaté Solà --- arch/riscv/usr/Makefile | 20 ++++++++++ arch/riscv/usr/basics.c | 89 ++++++++++++++++++++++++++++++++++++++++++++ arch/riscv/usr/factorial.S | 28 ++++++++++++++ arch/riscv/usr/float.S | 40 ++++++++++++++++++++ arch/riscv/usr/string.S | 93 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 270 insertions(+) create mode 100644 arch/riscv/usr/Makefile create mode 100644 arch/riscv/usr/basics.c create mode 100644 arch/riscv/usr/factorial.S create mode 100644 arch/riscv/usr/float.S create mode 100644 arch/riscv/usr/string.S (limited to 'arch/riscv/usr') diff --git a/arch/riscv/usr/Makefile b/arch/riscv/usr/Makefile new file mode 100644 index 0000000..6e36dd6 --- /dev/null +++ b/arch/riscv/usr/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 float.S + +.PHONY: clean +clean: + @rm -f $(EXE) diff --git a/arch/riscv/usr/basics.c b/arch/riscv/usr/basics.c new file mode 100644 index 0000000..4b5f289 --- /dev/null +++ b/arch/riscv/usr/basics.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include + +/* + * Returns the factorial for the given unsigned 64-bit integer. + * + * Implemented in factorial.S + */ +int factorial(uint64_t n); + +void test_factorial(void) +{ + assert(factorial(0) == 0); + assert(factorial(1) == 1); + assert(factorial(2) == 2); + assert(factorial(3) == 6); + assert(factorial(4) == 24); + + printf("factorial:\t\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(void) +{ + 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:\t\tOK\n"); +} + +/* + * Returns true if the given string is a palindrome, false otherwise. + * + * Implemented in string.S + */ +bool is_palindrome(char *str); + +void test_is_palindrome(void) +{ + assert(is_palindrome(NULL) == 0); + assert(is_palindrome("") == 0); + assert(is_palindrome("aba") == 1); + assert(is_palindrome("aaa") == 1); + assert(is_palindrome("This is a a si sihT") == 1); + + printf("is_palindrome:\t\tOK\n"); +} + +/* + * This function computes the following: ((a + b) * b) + b. Don't try to make + * sense of it, that's the computation I ended up while messing with RISC-V + * floating point instructions. Anyways, with this in mind, it will return if + * the computed value is greater than 10. + * + * Implemented in float.S + */ +bool greater_than_ten(double a, uint64_t b); + +void test_greater_than_ten(void) +{ + assert(!greater_than_ten(2.3, 1)); // 4.3 + assert(greater_than_ten(2.3, 2)); // 10.6 + + printf("greater_than_ten:\tOK\n"); +} + +int main() +{ + test_factorial(); + test_reverse_string(); + test_is_palindrome(); + test_greater_than_ten(); +} diff --git a/arch/riscv/usr/factorial.S b/arch/riscv/usr/factorial.S new file mode 100644 index 0000000..7e3df12 --- /dev/null +++ b/arch/riscv/usr/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/arch/riscv/usr/float.S b/arch/riscv/usr/float.S new file mode 100644 index 0000000..f481f3e --- /dev/null +++ b/arch/riscv/usr/float.S @@ -0,0 +1,40 @@ +.text +.globl greater_than_ten +.type greater_than_ten, @function + +// bool greater_than_ten(double a, uint64_t b); +greater_than_ten: + // Upon entry a -> fa0; and b -> a0. That is, the ABI increments the + //(f)a register per argument type. + + // Change the rounding to "Rounds Towards Zero" (bits: 0b001). + li t0, 0b001 + fsrm t0 + + // Convert the second parameter to double. + fcvt.d.lu ft0, a0 + + // ret = a + b + fadd.d fa0, fa0, ft0 + + // ret = (ret * b) + b + fmv.d ft1, ft0 + fmadd.d fa0, fa0, ft0, ft1 + + // Now we need to compare the number with 10.0. There are multiple ways to + // load a floating point immediate: + // 1. Loading an integer immediate and then convert it into a double. This + // is discouraged on the RISC-V assembly manual. + // 2. Load a double with `fld` from a memory location on the data section. + // 3. Load a floating point immediate with `fli` (requires the `Zfa` + // extension). + // I don't have the `Zfa` extension on my board, so I'm opting for the + // second way. + fld ft1, .TEN, t0 + fle.d a0, ft1, fa0 + + ret + +.data +.TEN: + .double 10.0 diff --git a/arch/riscv/usr/string.S b/arch/riscv/usr/string.S new file mode 100644 index 0000000..0f3bcc1 --- /dev/null +++ b/arch/riscv/usr/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_palindrome +.type is_palindrome, @function + +// bool is_palindrome(char *str); +is_palindrome: + // Return early on null pointer. + beq a0, zero, palindrome_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, palindrome_no + addi t0, t0, -1 + +pal_loop: + // Swap values between the two pointers. + lb t1, 0(a0) + lb t2, 0(t0) + bne t1, t2, palindrome_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 + +palindrome_yes: + li a0, 1 + jr ra + +palindrome_no: + li a0, 0 + jr ra -- cgit v1.2.3