Skip to content

Instantly share code, notes, and snippets.

@ammarfaizi2
Created November 17, 2023 20:13
Show Gist options
  • Save ammarfaizi2/b18d66b85e45faadb69e232dd3a0a64f to your computer and use it in GitHub Desktop.
Save ammarfaizi2/b18d66b85e45faadb69e232dd3a0a64f to your computer and use it in GitHub Desktop.
// 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