Created
November 17, 2023 20:13
-
-
Save ammarfaizi2/b18d66b85e45faadb69e232dd3a0a64f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-2.0-only | |
/* | |
* Simple CRUD with x86-64 Assembly (System V ABI). | |
*/ | |
#define MAX_INPUT_LEN 255 | |
#define SEEK_END 2 | |
/* | |
* The struct stored in the DB looks like this: | |
* struct data { | |
* uint8_t len; | |
* char str[]; | |
* }; | |
*/ | |
.section .rodata | |
str_menu: | |
.ascii "==================================================\n" | |
.ascii " 1. Add\n" | |
.ascii " 2. Delete\n" | |
.ascii " 3. Show\n" | |
.ascii " 4. Update\n" | |
.ascii " 5. Exit\n" | |
.ascii "==================================================\n" | |
.string "Enter the menu number: " | |
str_fmt_int: | |
.string "%d%*c" | |
str_file_name: | |
.string "db.bin" | |
str_file_name_tmp: | |
.string "db.bin.tmp" | |
str_mode_rbp: | |
.string "rb+" | |
str_mode_wbp: | |
.string "wb+" | |
str_open_file_err: | |
.string "Failed to open file %s!\n" | |
str_add_data: | |
.string "Enter the data: " | |
str_fmt_zu: | |
.string "%zu\n" | |
str_fmt_menu_show: | |
.string " %d. %s\n" | |
str_menu_delete_prompt: | |
.string "Enter the data number you want to delete: " | |
str_menu_update_prompt: | |
.string "Enter the data number you want to update: " | |
str_clear_screen: | |
.string "\033c" | |
str_menu_show_hold_screen: | |
.ascii "=============================================\n" | |
.ascii " Press enter to continue!\n" | |
.string "=============================================\n" | |
str_invalid_option: | |
.ascii "=============================================\n" | |
.ascii " Invalid option, please try again! (Press enter to continue)\n" | |
.string "=============================================\n" | |
str_menu_update_prompt_new: | |
.string "Enter the new date for update: " | |
.section .text | |
.global main | |
.extern printf | |
.extern scanf | |
.extern fopen | |
.extern fgets | |
.extern stdin | |
.extern strlen | |
.extern fwrite | |
.extern fseek | |
.extern fread | |
.extern remove | |
.extern rename | |
.extern getchar | |
# | |
# Return the menu number. | |
# | |
# int show_menu(void); | |
show_menu: | |
subq $8, %rsp | |
leaq str_clear_screen(%rip), %rdi | |
xorl %eax, %eax | |
callq printf | |
leaq str_menu(%rip), %rdi | |
xorl %eax, %eax | |
callq printf | |
leaq str_fmt_int(%rip), %rdi | |
movq %rsp, %rsi | |
xorl %eax, %eax | |
callq scanf | |
cmpl $1, %eax | |
jl .Lshow_menu_err | |
movl (%rsp), %eax | |
addq $8, %rsp | |
retq | |
.Lshow_menu_err: | |
movl $-1, %eax | |
addq $8, %rsp | |
retq | |
# FILE *__open_file(const char *file_name) | |
__open_file: | |
pushq %rbp | |
movq %rdi, %rbp | |
/* | |
* First, try to call fopen(rdi, "rb+"). | |
* If it doesn't fail, return directly. | |
*/ | |
leaq str_mode_rbp(%rip), %rsi | |
callq fopen | |
testq %rax, %rax | |
jnz .Lopen_file_out | |
/* | |
* fopen() with "rb+" has failed, try to | |
* open with "wb+", it will try to create | |
* the file if it doesn't yet exist. | |
*/ | |
.Lopen_file_wbp: | |
movq %rbp, %rdi | |
leaq str_mode_wbp(%rip), %rsi | |
callq fopen | |
testq %rax, %rax | |
jz .Lopen_file_err | |
.Lopen_file_out: | |
popq %rbp | |
retq | |
.Lopen_file_err: | |
leaq str_open_file_err(%rip), %rdi | |
leaq str_file_name(%rip), %rsi | |
xorl %eax, %eax | |
callq printf | |
xorl %eax, %eax | |
popq %rbp | |
retq | |
# FILE *open_file(void); | |
open_file: | |
leaq str_file_name(%rip), %rdi | |
jmp __open_file | |
# size_t get_input_data(char buf[MAX_INPUT_LEN]); | |
get_input_data: | |
pushq %rbp | |
movq %rdi, %rbp | |
xorl %eax, %eax | |
leaq str_add_data(%rip), %rdi | |
callq printf | |
movq %rbp, %rdi | |
movl $MAX_INPUT_LEN, %esi | |
movq stdin(%rip), %rdx | |
callq fgets | |
testq %rax, %rax | |
jz .Lget_input_data_out | |
movq %rax, %rdi | |
callq strlen | |
testq %rax, %rax | |
jz .Lget_input_data_out | |
/* | |
* If the last character is an LF, remove the LF | |
* and decrement the string length. Otherwise, | |
* save the result directly. | |
*/ | |
cmpb $'\n', -1(%rbp, %rax) | |
jne .Lget_input_data_out | |
movb $0, (%rbp, %rax) | |
decq %rax | |
.Lget_input_data_out: | |
popq %rbp | |
retq | |
# int menu_add(void); | |
menu_add: | |
pushq %rbp | |
subq $256, %rsp | |
xorl %ebp, %ebp | |
callq open_file | |
testq %rax, %rax | |
jz .Lmenu_add_err | |
movq %rax, %rbp | |
movq %rbp, %rdi | |
xorl %esi, %esi | |
movl $SEEK_END, %edx | |
callq fseek | |
leaq 1(%rsp), %rdi | |
callq get_input_data | |
testq %rax, %rax | |
jz .Lmenu_add_err | |
.Lmenu_add_save: | |
movb %al, (%rsp) | |
movq %rsp, %rdi | |
movl $1, %esi | |
leal 1(%eax), %edx | |
movq %rbp, %rcx | |
callq fwrite | |
movq %rbp, %rdi | |
callq fclose | |
addq $256, %rsp | |
popq %rbp | |
retq | |
.Lmenu_add_err: | |
testq %rbp, %rbp | |
jz .Lmenu_add_err_out | |
movq %rbp, %rdi | |
callq fclose | |
.Lmenu_add_err_out: | |
movl $-1, %eax | |
addq $256, %rsp | |
popq %rbp | |
retq | |
# size_t iterate_db(FILE *handle, struct data *d); | |
iterate_db: | |
pushq %rbp | |
pushq %rbx | |
subq $8, %rsp | |
movq %rdi, %rbp | |
movq %rsi, %rbx | |
/* | |
* Read the string length first. | |
*/ | |
movq %rsi, %rdi | |
movl $1, %esi | |
movl $1, %edx | |
movq %rbp, %rcx | |
callq fread | |
testq %rax, %rax | |
jz .Literate_db_out | |
/* | |
* Read the flexible array of chars. | |
*/ | |
leaq 1(%rbx), %rdi | |
movl $1, %esi | |
movzbl (%rbx), %edx | |
movq %rbp, %rcx | |
callq fread | |
movb $0, 1(%rbx, %rax) | |
.Literate_db_out: | |
addq $8, %rsp | |
popq %rbx | |
popq %rbp | |
retq | |
# int menu_delete(void); | |
menu_delete: | |
pushq %rbp | |
pushq %rbx | |
pushq %r12 | |
pushq %r13 | |
/* | |
* 0 - 256 (for struct data). | |
* 256 - 260 (for int (2nd arg to scanf)) | |
* 261 - 272 (reserved for alignment) | |
*/ | |
subq $(256 + 24), %rsp | |
movl $-1, %r12d | |
/* | |
* Open the temporary file. | |
*/ | |
leaq str_file_name_tmp(%rip), %rdi | |
callq __open_file | |
testq %rax, %rax | |
jz .Lmenu_delete_out | |
movq %rax, %rbp | |
/* | |
* Open the existing database file. | |
*/ | |
leaq str_file_name(%rip), %rdi | |
callq __open_file | |
testq %rax, %rax | |
jz .Lmenu_delete_close_tmp | |
movq %rax, %rbx | |
xorl %edi, %edi | |
callq menu_show | |
leaq str_menu_delete_prompt(%rip), %rdi | |
callq printf | |
leaq str_fmt_int(%rip), %rdi | |
leaq 256(%rsp), %rsi | |
callq scanf | |
cmpl $1, %eax | |
jl .Lmenu_delete_close_all | |
xorl %r13d, %r13d | |
xorl %r12d, %r12d | |
.Lmenu_delete_loop: | |
movq %rbx, %rdi | |
movq %rsp, %rsi | |
callq iterate_db | |
testq %rax, %rax | |
jz .Lmenu_delete_close_all | |
incl %eax | |
incl %r13d | |
cmpl %r13d, 256(%rsp) | |
je .Lmenu_delete_loop | |
/* | |
* Copy the data to a temporary file if | |
* it's not going to be deleted. | |
*/ | |
movq %rsp, %rdi | |
movl $1, %esi | |
movl %eax, %edx | |
movq %rbp, %rcx | |
callq fwrite | |
jmp .Lmenu_delete_loop | |
.Lmenu_delete_close_all: | |
movq %rbx, %rdi | |
callq fclose | |
.Lmenu_delete_close_tmp: | |
movq %rbp, %rdi | |
callq fclose | |
leaq str_file_name(%rip), %rdi | |
callq remove | |
testl %r12d, %r12d | |
jnz .Lmenu_delete_out | |
leaq str_file_name_tmp(%rip), %rdi | |
leaq str_file_name(%rip), %rsi | |
callq rename | |
.Lmenu_delete_out: | |
addq $(256 + 24), %rsp | |
movl %r12d, %eax | |
popq %r13 | |
popq %r12 | |
popq %rbx | |
popq %rbp | |
retq | |
# int menu_show(bool hold_screen); | |
menu_show: | |
pushq %rbp | |
pushq %rbx | |
pushq %r12 | |
subq $(256 + 16), %rsp | |
xorl %ebp, %ebp | |
xorl %ebx, %ebx | |
movl %edi, %r12d | |
callq open_file | |
testq %rax, %rax | |
jz .Lmenu_show_out | |
movq %rax, %rbp | |
.Lmenu_show_loop: | |
movq %rbp, %rdi | |
movq %rsp, %rsi | |
callq iterate_db | |
testq %rax, %rax | |
jz .Lmenu_show_out_close | |
incl %ebx | |
xorl %eax, %eax | |
leaq str_fmt_menu_show(%rip), %rdi | |
movl %ebx, %esi | |
leaq 1(%rsp), %rdx | |
callq printf | |
jmp .Lmenu_show_loop | |
.Lmenu_show_out_close: | |
movq %rbp, %rdi | |
callq fclose | |
testl %r12d, %r12d | |
jz .Lmenu_show_out | |
leaq str_menu_show_hold_screen(%rip), %rdi | |
xorl %eax, %eax | |
callq printf | |
callq getchar | |
xorl %eax, %eax | |
.Lmenu_show_out: | |
addq $(256 + 16), %rsp | |
popq %r12 | |
popq %rbx | |
popq %rbp | |
retq | |
# int menu_update(void); | |
menu_update: | |
pushq %rbp | |
pushq %rbx | |
pushq %r12 | |
pushq %r13 | |
/* | |
* 0 - 256 (for struct data). | |
* 256 - 260 (for int (2nd arg to scanf)) | |
* 261 - 272 (reserved for alignment) | |
*/ | |
subq $(256 + 24), %rsp | |
movl $-1, %r12d | |
/* | |
* Open the temporary file. | |
*/ | |
leaq str_file_name_tmp(%rip), %rdi | |
callq __open_file | |
testq %rax, %rax | |
jz .Lmenu_update_out | |
movq %rax, %rbp | |
/* | |
* Open the existing database file. | |
*/ | |
leaq str_file_name(%rip), %rdi | |
callq __open_file | |
testq %rax, %rax | |
jz .Lmenu_update_close_tmp | |
movq %rax, %rbx | |
xorl %edi, %edi | |
callq menu_show | |
leaq str_menu_update_prompt(%rip), %rdi | |
callq printf | |
leaq str_fmt_int(%rip), %rdi | |
leaq 256(%rsp), %rsi | |
callq scanf | |
cmpl $1, %eax | |
jl .Lmenu_update_close_all | |
xorl %r13d, %r13d | |
xorl %r12d, %r12d | |
.Lmenu_update_loop: | |
movq %rbx, %rdi | |
movq %rsp, %rsi | |
callq iterate_db | |
testq %rax, %rax | |
jz .Lmenu_update_close_all | |
incl %eax | |
incl %r13d | |
cmpl %r13d, 256(%rsp) | |
jne .Lmenu_update_do_fwrite | |
xorl %eax, %eax | |
leaq str_menu_update_prompt_new(%rip), %rdi | |
callq printf | |
leaq 1(%rsp), %rdi | |
movl $MAX_INPUT_LEN, %esi | |
movq stdin(%rip), %rdx | |
callq fgets | |
testq %rax, %rax | |
jz .Lmenu_update_loop | |
movq %rax, %rdi | |
callq strlen | |
testq %rax, %rax | |
jz .Lmenu_update_loop | |
/* | |
* If the last character is an LF, remove the LF | |
* and decrement the string length. Otherwise, | |
* save the result directly. | |
*/ | |
cmpb $'\n', (%rsp, %rax) | |
jne .Lmenu_update_do_fwrite | |
movb $0, (%rbp, %rax) | |
decq %rax | |
movb %al, (%rsp) | |
incq %rax | |
.Lmenu_update_do_fwrite: | |
/* | |
* Copy the data to a temporary file if | |
* it's not going to be updated. | |
*/ | |
movq %rsp, %rdi | |
movl $1, %esi | |
movl %eax, %edx | |
movq %rbp, %rcx | |
callq fwrite | |
jmp .Lmenu_update_loop | |
.Lmenu_update_close_all: | |
movq %rbx, %rdi | |
callq fclose | |
.Lmenu_update_close_tmp: | |
movq %rbp, %rdi | |
callq fclose | |
leaq str_file_name(%rip), %rdi | |
callq remove | |
testl %r12d, %r12d | |
jnz .Lmenu_update_out | |
leaq str_file_name_tmp(%rip), %rdi | |
leaq str_file_name(%rip), %rsi | |
callq rename | |
.Lmenu_update_out: | |
addq $(256 + 24), %rsp | |
movl %r12d, %eax | |
popq %r13 | |
popq %r12 | |
popq %rbx | |
popq %rbp | |
retq | |
# int handle_menu(int n); | |
handle_menu: | |
subq $8, %rsp | |
cmpl $1, %edi | |
je .Lhandle_menu_add | |
cmpl $2, %edi | |
je .Lhandle_menu_delete | |
cmpl $3, %edi | |
je .Lhandle_menu_show | |
cmpl $4, %edi | |
je .Lhandle_menu_update | |
cmpl $5, %edi | |
movl $-1, %eax | |
je .Lout | |
leaq str_invalid_option(%rip), %rdi | |
xorl %eax, %eax | |
callq printf | |
callq getchar | |
xorl %eax, %eax | |
jmp .Lout | |
.Lhandle_menu_add: | |
callq menu_add | |
jmp .Lout | |
.Lhandle_menu_delete: | |
callq menu_delete | |
jmp .Lout | |
.Lhandle_menu_show: | |
movl $1, %edi | |
callq menu_show | |
jmp .Lout | |
.Lhandle_menu_update: | |
callq menu_update | |
.Lout: | |
addq $8, %rsp | |
retq | |
main: | |
pushq %rbp | |
.Lmain_loop: | |
callq show_menu | |
movl %eax, %edi | |
callq handle_menu | |
testl %eax, %eax | |
jns .Lmain_loop | |
xorl %eax, %eax | |
popq %rbp | |
retq |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment