aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2024-08-12 14:50:07 +0000
committerMiquel Sabaté Solà <mikisabate@gmail.com>2024-12-05 16:02:17 +0100
commit8e968078e6aee470a02bed7728100b2df1877e90 (patch)
tree8f64d8a0e8d8e7a9329a8239fd3c57ce217d5ff7
parentc173df46a0a12fe52fdf13323f967e9117462096 (diff)
downloadfarga-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--.gitignore2
-rw-r--r--basics/Makefile20
-rw-r--r--basics/factorial.S28
-rw-r--r--basics/main.c70
-rw-r--r--basics/string.S93
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