From a7a12c7c546205fd86786050370f05f16ccb559e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=CC=81o?= Date: Tue, 23 Jul 2024 18:41:14 +0200 Subject: [PATCH] feat: implement ft_strcpy and its explanation --- Makefile | 2 +- README.md | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ ft_strcpy.s | 15 ++++++++++++++ libasm.h | 4 +++- main.c | 43 ++++++++++++++++++++++++++++++++------ 5 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 ft_strcpy.s diff --git a/Makefile b/Makefile index 9bcd23e..f0254f8 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ NC = \033[0m NAME = libasm.a SOURCES = ft_strlen.s \ - # ft_strcpy.s \ + ft_strcpy.s \ # ft_strcmp.s \ # ft_write.s \ # ft_read.s \ diff --git a/README.md b/README.md index 9cd59a4..c149007 100644 --- a/README.md +++ b/README.md @@ -287,3 +287,63 @@ end: ``` And that's it! We have implemented the `ft_strlen` function in assembly. + +## ft_strcpy + +The `ft_strcpy` function is a function that copies a string into another string, and returns a pointer to the destination string. + +The logic is very similar to the `ft_strlen` function: + +1. Set a counter to 0. +2. Look at the first character of the source string. +3. Copy it to the destination string. +4. Increment the counter. +5. Look at the next character and copy it. +6. If it is not the null-terminator, increment the counter and go back to step 5. +7. If it is the null-terminator, exit the loop and return a pointer to the destination string. + +### Implementation + +A pointer to our `dst` string is passed in `rdi`, and a pointer to our `src` string is passed in `rsi`. + +We can start the same way we did with `ft_strlen` by setting the counter to 0. + +```nasm +ft_strcpy: + mov rcx, 0 +``` + +We can then start our loop. + +We need to copy every character in `rsi` to `rdi`. However, **in assembly, we can't copy data directly from one address to another** (`mov [rdi], [rsi]` would not work). + +We therefore need to copy the data from the source address to a register, and then copy it to the destination address. + +> We could use any register to store the character (like `r8` as seen before), but it is more appropriate to use `al` for this purpose, as it is a register that is meant to store a single byte. + +```nasm +loop: + mov al, [rsi + rcx] + mov [rdi + rcx], al + inc rcx + cmp al, 0 + jne loop +``` + +In this loop: +1. We copy the character in `rsi` to `al`. +2. We copy `al` to `rdi`. +3. We increment the counter. +4. We check if the character is the null-terminator. +5. If it is not, we go back to the beginning of the loop. + +Finally, we need to return the pointer to the destination string. + +Given that we received this pointer in `rdi` and that we did not move it, we can simply copy it to `rax` and return. + +```nasm +mov rax, rdi +ret +``` + +## ft_strcmp diff --git a/ft_strcpy.s b/ft_strcpy.s new file mode 100644 index 0000000..8dc36de --- /dev/null +++ b/ft_strcpy.s @@ -0,0 +1,15 @@ +global _ft_strcpy + +_ft_strcpy: + xor rcx, rcx + +while: + mov al, [rsi + rcx] + mov [rdi + rcx], al + inc rcx + cmp al, 0 + jne while + +break: + mov rax, rdi + ret diff --git a/libasm.h b/libasm.h index 017036b..e846301 100644 --- a/libasm.h +++ b/libasm.h @@ -4,10 +4,12 @@ #define NC "\033[0m" #define RED "\033[0;31m" #define GREEN "\033[0;32m" +#define BOLD "\033[1m" #include #include -int ft_strlen(char *str); +int ft_strlen(char *str); +char *ft_strcpy(char *dst, char *src); #endif diff --git a/main.c b/main.c index 0169cf7..1620af6 100644 --- a/main.c +++ b/main.c @@ -2,7 +2,7 @@ void test_strlen(void) { - printf("\nTesting ft_strlen\n"); + printf(BOLD "\nTesting ft_strlen\n" NC); char *strings[] = { "test12345", @@ -19,17 +19,48 @@ void test_strlen(void) int expected = strlen(strings[i]); int result = ft_strlen(strings[i]); - if (expected == result) { - printf(GREEN "OK" NC); - } else { - printf(RED "KO" NC); - } + printf(expected == result ? GREEN "OK" NC : RED "KO" NC); printf("\t Expected: %d\tResult: %d\n", expected, result); } } +void test_strcpy(void) +{ + printf(BOLD "\nTesting ft_strcpy\n" NC); + + char *strings[] = { + "test12345", + "1", + "test", + "", + "zzzzzzzz", + }; + + int numStrings = sizeof(strings) / sizeof(strings[0]); + + for (int i = 0; i < numStrings; i++) { + // Test return value + char dummy_dest[100]; + + void *ref_ptr = strcpy(dummy_dest, strings[i]); + void *test_ptr = ft_strcpy(dummy_dest, strings[i]); + printf(ref_ptr == test_ptr ? GREEN "POINTER OK" NC : RED "POINTER KO" NC); + printf("\tExpected: %p\tResult: %p\n", ref_ptr, test_ptr); + + // Test destination string + char ref_dest[100]; + char test_dest[100]; + + strcpy(ref_dest, strings[i]); + ft_strcpy(test_dest, strings[i]); + printf(strcmp(ref_dest, test_dest) == 0 ? GREEN "COPY OK" NC : RED "COPY KO" NC); + printf("\t\tExpected: %s\tResult: %s\n", ref_dest, test_dest); + } +} + int main(void) { test_strlen(); + test_strcpy(); return (0); }