Vibe-Coding in Assembly

12 min read Original article ↗
; Redis-like Key-Value Store in x86-64 Assembly for macOS ; NASM syntax - Basic Redis server with SET, GET, DEL, PING commands extern _socket, _bind, _listen, _accept, _read, _write, _close, _exit, _printf, _htons extern _strcmp, _strlen, _strncmp, _sprintf section .data server_msg db "Redis-like server starting on port 6379...", 10 db "Connect with: redis-cli -p 6379", 10 db "Supported: PING, SET key value, GET key, DEL key, QUIT", 10, 0 ; RESP responses resp_ok db "+OK", 13, 10 resp_ok_len equ $ - resp_ok resp_pong db "+PONG", 13, 10 resp_pong_len equ $ - resp_pong resp_nil db "$-1", 13, 10 resp_nil_len equ $ - resp_nil resp_err_unknown db "-ERR unknown command", 13, 10 resp_err_unknown_len equ $ - resp_err_unknown ; Commands cmd_ping db "PING", 0 cmd_set db "SET", 0 cmd_get db "GET", 0 cmd_del db "DEL", 0 cmd_quit db "QUIT", 0 ; Socket address sockaddr: sin_family dw 2 sin_port dw 0 sin_addr dd 0 sin_zero dq 0 sockaddr_len equ $ - sockaddr ; Format strings (null-terminated!) fmt_bulk db "$%d", 13, 10, 0 fmt_int db ":%d", 13, 10, 0 crlf db 13, 10, 0 section .bss sock_fd resq 1 client_fd resq 1 buffer resb 8192 temp_key resb 256 temp_value resb 1024 response_buf resb 1024 saved_read_len resq 1 ; Simple key-value storage (100 entries) kv_keys resb 10000 ; Key storage (100 keys * 100 bytes) kv_values resb 100000 ; Value storage (100 values * 1000 bytes) kv_count resq 1 ; Number of entries section .text global _main %define AF_INET 2 %define SOCK_STREAM 1 %define IPPROTO_TCP 6 %define PORT 6379 %define MAX_KEYS 100 _main: push rbp mov rbp, rsp ; Initialize mov qword [rel kv_count], 0 ; Print message lea rdi, [rel server_msg] call _printf ; Setup port mov rdi, PORT call _htons mov [rel sockaddr + 2], ax ; Create socket mov rdi, AF_INET mov rsi, SOCK_STREAM mov rdx, IPPROTO_TCP call _socket cmp rax, 0 jl error_exit mov [rel sock_fd], rax ; Bind mov rdi, [rel sock_fd] lea rsi, [rel sockaddr] mov rdx, sockaddr_len call _bind cmp rax, 0 jl error_exit ; Listen mov rdi, [rel sock_fd] mov rsi, 5 call _listen cmp rax, 0 jl error_exit server_loop: ; Accept mov rdi, [rel sock_fd] mov rsi, 0 mov rdx, 0 call _accept cmp rax, 0 jl server_loop mov [rel client_fd], rax ; Handle client call handle_client ; Close mov rdi, [rel client_fd] call _close jmp server_loop handle_client: push rbp mov rbp, rsp .client_loop: ; Read mov rdi, [rel client_fd] lea rsi, [rel buffer] mov rdx, 8192 call _read cmp rax, 0 jle .client_done ; Save read length and null terminate mov [rel saved_read_len], rax lea rbx, [rel buffer] add rbx, rax mov byte [rbx], 0 ; Parse and execute call parse_resp jmp .client_loop .client_done: pop rbp ret parse_resp: push rbp mov rbp, rsp ; Check if RESP array (must start with '*') lea rbx, [rel buffer] cmp byte [rbx], '*' jne .parse_done ; Search for command strings in buffer (case-insensitive) ; Look for PING lea rdi, [rel buffer] mov rsi, [rel saved_read_len] lea rdx, [rel cmd_ping] call find_string_in_buffer cmp rax, 0 jne .found_ping ; Look for SET lea rdi, [rel buffer] mov rsi, [rel saved_read_len] lea rdx, [rel cmd_set] call find_string_in_buffer cmp rax, 0 jne .found_set ; Look for GET lea rdi, [rel buffer] mov rsi, [rel saved_read_len] lea rdx, [rel cmd_get] call find_string_in_buffer cmp rax, 0 jne .found_get ; Look for DEL lea rdi, [rel buffer] mov rsi, [rel saved_read_len] lea rdx, [rel cmd_del] call find_string_in_buffer cmp rax, 0 jne .found_del ; Look for QUIT lea rdi, [rel buffer] mov rsi, [rel saved_read_len] lea rdx, [rel cmd_quit] call find_string_in_buffer cmp rax, 0 jne .found_quit ; Unknown command jmp .unknown_cmd .found_ping: jmp .do_ping .found_set: jmp .do_set .found_get: jmp .do_get .found_del: jmp .do_del .found_quit: jmp .do_quit .unknown_cmd: mov rdi, [rel client_fd] lea rsi, [rel resp_err_unknown] mov rdx, resp_err_unknown_len call _write jmp .parse_done .do_ping: mov rdi, [rel client_fd] lea rsi, [rel resp_pong] mov rdx, resp_pong_len call _write jmp .parse_done .do_set: ; Extract key and value from RESP array call find_resp_command mov rbx, rax cmp rbx, 0 je .parse_done ; Find end of command string mov rcx, rbx .find_set_cmd_end: cmp byte [rcx], 13 je .found_set_cmd_end cmp byte [rcx], 0 je .parse_done inc rcx jmp .find_set_cmd_end .found_set_cmd_end: add rcx, 2 ; Skip \r\n ; Extract key (next bulk string) mov rdi, rcx call extract_resp_string_at cmp rax, 0 je .parse_done mov rsi, rax lea rdi, [rel temp_key] call copy_until_crlf ; Extract value (next bulk string after key) mov rdi, rsi call find_string_end mov rdi, rax call extract_resp_string_at cmp rax, 0 je .parse_done mov rsi, rax lea rdi, [rel temp_value] call copy_until_crlf ; Store key-value lea rdi, [rel temp_key] lea rsi, [rel temp_value] call kv_set mov rdi, [rel client_fd] lea rsi, [rel resp_ok] mov rdx, resp_ok_len call _write jmp .parse_done .do_get: call find_resp_command mov rbx, rax cmp rbx, 0 je .parse_done mov rcx, rbx .find_get_cmd_end: cmp byte [rcx], 13 je .found_get_cmd_end cmp byte [rcx], 0 je .parse_done inc rcx jmp .find_get_cmd_end .found_get_cmd_end: add rcx, 2 mov rdi, rcx call extract_resp_string_at cmp rax, 0 je .parse_done mov rsi, rax lea rdi, [rel temp_key] call copy_until_crlf lea rdi, [rel temp_key] call kv_get cmp rax, 0 je .get_not_found mov rdi, rax call send_bulk_string jmp .parse_done .get_not_found: mov rdi, [rel client_fd] lea rsi, [rel resp_nil] mov rdx, resp_nil_len call _write jmp .parse_done .do_del: call find_resp_command mov rbx, rax cmp rbx, 0 je .parse_done mov rcx, rbx .find_del_cmd_end: cmp byte [rcx], 13 je .found_del_cmd_end cmp byte [rcx], 0 je .parse_done inc rcx jmp .find_del_cmd_end .found_del_cmd_end: add rcx, 2 mov rdi, rcx call extract_resp_string_at cmp rax, 0 je .parse_done mov rsi, rax lea rdi, [rel temp_key] call copy_until_crlf lea rdi, [rel temp_key] call kv_del call send_integer jmp .parse_done .do_quit: mov rdi, [rel client_fd] lea rsi, [rel resp_ok] mov rdx, resp_ok_len call _write jmp .parse_done .parse_done: pop rbp ret ; ============================================================ ; Find string in buffer (case-insensitive) ; rdi = buffer, rsi = buffer_len, rdx = search_string ; Returns: pointer to match or 0 if not found ; ============================================================ find_string_in_buffer: push rbp mov rbp, rsp push rbx push r12 push r13 push r14 push r15 mov r12, rdi ; buffer pointer (callee-saved) mov r13, rdx ; search string (callee-saved) mov r14, rsi ; buffer length (callee-saved) mov r15, 0 ; position (callee-saved) ; Get search string length once mov rdi, r13 call _strlen mov rbx, rax ; search string length (callee-saved) .find_str_loop: cmp r15, r14 jge .find_str_not_found ; Check if we have enough bytes left mov rax, r14 sub rax, r15 cmp rax, rbx jl .find_str_not_found ; Compare strings (case-insensitive) lea rdi, [r12 + r15] mov rsi, r13 mov rdx, rbx call compare_strings_case_insensitive cmp rax, 0 je .find_str_found inc r15 jmp .find_str_loop .find_str_found: lea rax, [r12 + r15] jmp .find_str_done .find_str_not_found: mov rax, 0 .find_str_done: pop r15 pop r14 pop r13 pop r12 pop rbx pop rbp ret ; ============================================================ ; Compare strings case-insensitively ; rdi = str1, rsi = str2, rdx = length ; Returns: 0 if equal, 1 if not ; ============================================================ compare_strings_case_insensitive: push rbp mov rbp, rsp mov r8, rdx ; length mov r9, 0 ; index .cmp_loop: cmp r9, r8 jge .cmp_equal movzx eax, byte [rdi + r9] movzx ecx, byte [rsi + r9] ; Convert to uppercase cmp al, 'a' jl .al_ok cmp al, 'z' jg .al_ok sub al, 32 .al_ok: cmp cl, 'a' jl .cl_ok cmp cl, 'z' jg .cl_ok sub cl, 32 .cl_ok: cmp al, cl jne .cmp_not_equal inc r9 jmp .cmp_loop .cmp_equal: mov rax, 0 jmp .cmp_done .cmp_not_equal: mov rax, 1 .cmp_done: pop rbp ret ; ============================================================ ; Find command in RESP array ; Format: *N\r\n$len\r\ncommand\r\n... ; Returns: pointer to command string or 0 ; ============================================================ find_resp_command: push rbp mov rbp, rsp lea rbx, [rel buffer] cmp byte [rbx], '*' jne .not_found_cmd inc rbx ; Skip array count digits .skip_count: mov al, [rbx] cmp al, 13 je .check_crlf_count inc rbx jmp .skip_count .check_crlf_count: inc rbx ; skip \r inc rbx ; skip \n ; Now at first '$' cmp byte [rbx], '$' jne .not_found_cmd inc rbx ; skip '$' ; Skip length digits .skip_length: mov al, [rbx] cmp al, 13 je .check_crlf_len inc rbx jmp .skip_length .check_crlf_len: inc rbx ; skip \r inc rbx ; skip \n mov rax, rbx ; pointer to command string jmp .find_cmd_done .not_found_cmd: mov rax, 0 .find_cmd_done: pop rbp ret ; ============================================================ ; Extract RESP string at given position ; rdi = position in buffer (should point to '$') ; Returns: pointer to string data or 0 ; ============================================================ extract_resp_string_at: push rbp mov rbp, rsp mov rbx, rdi cmp byte [rbx], '$' jne .not_found_str inc rbx ; skip '$' ; Skip length digits .skip_len: mov al, [rbx] cmp al, 13 je .check_crlf_str inc rbx jmp .skip_len .check_crlf_str: inc rbx ; skip \r inc rbx ; skip \n mov rax, rbx ; pointer to string data jmp .extract_done .not_found_str: mov rax, 0 .extract_done: pop rbp ret ; ============================================================ ; Find end of string (skip past \r\n) ; rdi = pointer to start of string ; Returns: pointer after \r\n ; ============================================================ find_string_end: push rbp mov rbp, rsp mov rbx, rdi .find_end_loop: mov al, [rbx] cmp al, 13 je .found_end cmp al, 0 je .found_end_null inc rbx jmp .find_end_loop .found_end: add rbx, 2 ; skip \r\n mov rax, rbx jmp .find_end_done .found_end_null: mov rax, rbx .find_end_done: pop rbp ret ; ============================================================ ; Copy string until \r\n or null ; rdi = destination, rsi = source ; ============================================================ copy_until_crlf: push rbp mov rbp, rsp mov rbx, rsi mov rcx, rdi .copy_loop: mov al, [rbx] cmp al, 13 je .copy_done cmp al, 0 je .copy_done mov [rcx], al inc rbx inc rcx jmp .copy_loop .copy_done: mov byte [rcx], 0 pop rbp ret ; ============================================================ ; Key-value store operations ; ============================================================ kv_set: push rbp mov rbp, rsp push r12 push r13 mov r12, rdi ; key mov r13, rsi ; value ; Check if key already exists mov rdi, r12 call kv_find cmp rax, -1 jne .kv_update ; New key mov rbx, [rel kv_count] cmp rbx, MAX_KEYS jge .kv_set_done ; Copy key to storage lea r8, [rel kv_keys] mov rax, rbx mov r9, 100 ; KEY_SIZE mul r9 add r8, rax mov rdi, r8 mov rsi, r12 call copy_string_simple ; Copy value to storage lea r8, [rel kv_values] mov rax, rbx mov r9, 1000 ; VALUE_SIZE mul r9 add r8, rax mov rdi, r8 mov rsi, r13 call copy_string_simple ; Increment count inc qword [rel kv_count] jmp .kv_set_done .kv_update: ; Update existing value lea r8, [rel kv_values] mov r9, 1000 mul r9 add r8, rax mov rdi, r8 mov rsi, r13 call copy_string_simple .kv_set_done: pop r13 pop r12 pop rbp ret copy_string_simple: push rbp mov rbp, rsp mov rcx, rdi mov rdx, rsi .copy_simple_loop: mov al, [rdx] cmp al, 0 je .copy_simple_done mov [rcx], al inc rcx inc rdx jmp .copy_simple_loop .copy_simple_done: mov byte [rcx], 0 pop rbp ret kv_get: push rbp mov rbp, rsp call kv_find cmp rax, -1 je .kv_get_not_found ; Return pointer to value lea r8, [rel kv_values] mov rbx, rax mov r9, 1000 mov rax, rbx mul r9 add r8, rax mov rax, r8 jmp .kv_get_done .kv_get_not_found: mov rax, 0 .kv_get_done: pop rbp ret kv_del: push rbp mov rbp, rsp push r12 call kv_find cmp rax, -1 je .kv_del_not_found ; Delete by shifting entries down mov r12, rax ; index to delete mov rbx, [rel kv_count] dec rbx ; new count ; Clear the key (set first byte to 0) lea r8, [rel kv_keys] mov rax, r12 mov r9, 100 mul r9 add r8, rax mov byte [r8], 0 ; Clear the value lea r8, [rel kv_values] mov rax, r12 mov r9, 1000 mul r9 add r8, rax mov byte [r8], 0 mov rax, 1 ; 1 key deleted jmp .kv_del_done .kv_del_not_found: mov rax, 0 .kv_del_done: pop r12 pop rbp ret kv_find: push rbp mov rbp, rsp push r12 mov r12, rdi ; key to find mov rbx, [rel kv_count] mov rcx, 0 .find_loop: cmp rcx, rbx jge .find_not_found push rcx push rbx ; Get key pointer lea r8, [rel kv_keys] mov rax, rcx mov r9, 100 mul r9 add r8, rax ; Skip deleted entries (first byte = 0) cmp byte [r8], 0 je .find_skip ; Compare mov rdi, r8 mov rsi, r12 call _strcmp pop rbx pop rcx cmp rax, 0 je .find_found inc rcx jmp .find_loop .find_skip: pop rbx pop rcx inc rcx jmp .find_loop .find_found: mov rax, rcx jmp .find_done .find_not_found: mov rax, -1 .find_done: pop r12 pop rbp ret ; ============================================================ ; Send bulk string: $len\r\nstring\r\n ; rdi = pointer to null-terminated string ; ============================================================ send_bulk_string: push rbp mov rbp, rsp push r12 mov r12, rdi ; string pointer ; Get string length call _strlen mov rbx, rax ; Format header: $len\r\n lea rdi, [rel response_buf] lea rsi, [rel fmt_bulk] mov rdx, rbx call _sprintf ; Send header lea rdi, [rel response_buf] call _strlen mov rdx, rax mov rdi, [rel client_fd] lea rsi, [rel response_buf] call _write ; Send string content mov rdi, r12 call _strlen mov rdx, rax mov rdi, [rel client_fd] mov rsi, r12 call _write ; Send trailing \r\n mov rdi, [rel client_fd] lea rsi, [rel crlf] mov rdx, 2 call _write pop r12 pop rbp ret ; ============================================================ ; Send integer: :number\r\n ; rax = integer value ; ============================================================ send_integer: push rbp mov rbp, rsp ; Format: :number\r\n lea rdi, [rel response_buf] lea rsi, [rel fmt_int] mov rdx, rax call _sprintf ; Send lea rdi, [rel response_buf] call _strlen mov rdx, rax mov rdi, [rel client_fd] lea rsi, [rel response_buf] call _write pop rbp ret error_exit: mov rdi, 1 call _exit