Skip to content

Instantly share code, notes, and snippets.

@aydinnyunus
Created February 13, 2026 18:02
Show Gist options
  • Select an option

  • Save aydinnyunus/7beef428ca91fb7eb8aa2965086998d8 to your computer and use it in GitHub Desktop.

Select an option

Save aydinnyunus/7beef428ca91fb7eb8aa2965086998d8 to your computer and use it in GitHub Desktop.
Vibe-Coding in Assembly
; 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment