Skip to content

Instantly share code, notes, and snippets.

@mguaypaq
Created January 9, 2022 03:37
Show Gist options
  • Save mguaypaq/ac8c7fc6d69d1ab27374954a42fd68d0 to your computer and use it in GitHub Desktop.
Save mguaypaq/ac8c7fc6d69d1ab27374954a42fd68d0 to your computer and use it in GitHub Desktop.
Stub for a solution to Advent of Code 2021 days 1 and 2, in x86-64 assembly, as a static linux elf binary.
; Replace the line marked 'TODO' with your solution, then compile by running:
;
; nasm aoc2021-day01a.nasm && chmod +x aoc2021-day01a
;
; to generate a static elf binary which should work on x86-64 linux.
; You can see what happens instruction-by-instruction with gdb, by running:
;
; gdb aoc2021-day01a
;
; and then using the gdb commands:
;
; tui enable
; layout asm
; layout regs
; break *0x4000b0
; run < my-test-input-file.txt
; stepi
; <enter>
; <enter>
; <enter>
; ...
; The elf headers ask the operating system to set up two memory mappings,
; before starting execution at memory address 0x0040_00b0:
;
; 1. Some read+execute memory for the code (including the elf header)
; starting at memory address 0x0040_0000.
;
; 2. One megabyte of zero-filled, read+write memory for the heap
; from memory address 0x0050_0000 to 0x0060_0000.
; Some input/output helper functions are provided after the 'TODO' marker,
; which you can call in your solution by writing:
;
; 1. 'jmp exit'
; 2. 'jmp panic'
; 3. 'call read_stdin'
; 4. 'call parse_num'
; 5. 'call format_num'
; 6. 'call write_stdout'
;----------------------------------------------------------------
[bits 64]
org 0x0040_0000
;----------------------------------------------------------------
ehdr: ; elf header
db 0x7f, 'ELF' ; elf magic number
db 2 ; 64-bit architecture
db 1 ; little-endian
db 1 ; elf version
db 0 ; no elf extensions
dq 0 ; reserved
dw 2 ; executable file
dw 62 ; x86-64 architecture
dd 1 ; elf version (again)
dq start ; execution start memory address
dq (phdr0 - ehdr) ; program header table file offset
dq 0 ; section header table file offset (absent)
dd 0 ; architecture-specific flags (none)
dw 64 ; size of this elf header
dw 56 ; size of a program header
dw 2 ; count of program headers
dw 64 ; size of a section header
dw 0 ; count of section headers
dw 0 ; index of the section name section header (absent)
phdr0: ; program header for the code
dd 1 ; loadable segment
dd 0b101 ; permission flags (read, no write, exec)
dq (ehdr - ehdr) ; file offset
dq ehdr ; memory address
dq 0 ; reserved
dq (end - ehdr) ; size in file
dq (end - ehdr) ; size in memory
dq (1 << 12) ; 4KB alignment
phdr1: ; program header for the heap
dd 1 ; loadable segment
dd 0b110 ; permission flags (read, write, no exec)
dq 0 ; file offset
dq heap ; memory address
dq 0 ; reserved
dq 0 ; size in file
dq 0x0010_0000 ; size in memory
dq (1 << 12) ; 4KB alignment
start: ; start of code
;----------------------------------------------------------------
jmp panic ; TODO
;----------------------------------------------------------------
exit:
; exit program with return code 0 (doesn't return)
mov edi, 0 ; return code
mov eax, 60 ; exit syscall
syscall ; no return
panic:
; exit program with return code 1 (doesn't return)
mov edi, 1 ; return code
mov eax, 60 ; exit syscall
syscall ; no return
read_stdin:
; read stdin until eof
; input:
; - esi = buffer start
; - edx = buffer size
; output:
; - esi = remaining buffer start
; - edx = remaining buffer size
; panic if:
; - read error (other than eintr)
; - out of buffer space
push rax
push rcx
push rdi
push r11
mov edi, 0 ; stdin
mov eax, 0
.loop:
add esi, eax
sub edx, eax
jz near panic ; out of buffer space
.eintr:
mov eax, 0 ; read syscall
syscall ; read count in rax (less than 1<<31), garbage in rcx and r11
cmp eax, -4 ; eintr
je short .eintr
cmp eax, 0 ; eof
jl near panic ; negative result = read error
jg short .loop ; positive result = some bytes read
pop r11
pop rdi
pop rcx
pop rax
ret
parse_num:
; parse a newline-terminated 32-bit number from the start of a buffer
; input:
; - esi = buffer start
; - edx = buffer size
; output:
; - eax = parsed number
; - esi = remaining buffer start
; - edx = remaining buffer size
; panic if:
; - missing number
; - missing newline
; - leading zero
; - overflow
push rcx
push rbx
mov eax, 0
mov ecx, edx
mov ebx, 0
; first digit is special
sub ecx, 1
jc near panic ; buffer exhausted
lodsb
sub al, 0x30 ; ascii '0'
jb near panic ; invalid char
je short .leading_zero
cmp al, 10
jae near panic ; invalid char
; remaining digits
.loop:
sub ecx, 1
jc near panic ; buffer exhausted
mov bl, byte [rsi]
inc esi
cmp bl, 0x0a ; ascii newline
je short .newline
sub bl, 0x30 ; ascii '0'
jb near panic ; invalid char
cmp bl, 10
jae near panic ; invalid char
mov edx, 10
mul edx ; shift result by one digit
jc near panic ; overflow
add eax, ebx ; add new digit to result
jc near panic ; overflow
jmp short .loop
.leading_zero:
sub ecx, 1
jc near panic ; buffer exhausted
cmp byte [rsi], 0x0a ; ascii newline
jne near panic
inc esi
.newline:
mov edx, ecx
pop rbx
pop rcx
ret
format_num:
; format a 32-bit number as a decimal
; (right-aligned inside the supplied buffer)
; input:
; - eax = number to format
; - esi = buffer start
; - edx = buffer size
; output:
; - esi = used buffer start
; - edx = used buffer size
; panic if:
; - buffer too small
cmp eax, 0
je short .zero
push rax
push rcx
push rdx
push rbx
mov ecx, edx
mov ebx, 10
.loop:
mov edx, 0
div ebx
add dl, 0x30 ; ascii '0'
sub ecx, 1
jc near panic ; buffer exhausted
mov byte [rsi+rcx], dl
cmp eax, 0
jne .loop
pop rbx
pop rdx
add esi, ecx
sub edx, ecx
pop rcx
pop rax
ret
.zero:
sub edx, 1
jc near panic ; buffer exhausted
mov byte [rsi+rdx], 0x30 ; ascii '0'
add esi, edx
mov edx, 1
ret
write_stdout:
; write to stdout
; input:
; - esi = buffer start
; - edx = buffer size
; panic if:
; - write error (other than eintr)
push rax
push rcx
push rdx
push rsi
push rdi
push r11
mov edi, 1 ; stdout
.loop:
mov eax, 1 ; write syscall
syscall ; write count in rax (less than 1<<31), garbage in rcx and r11
cmp eax, -4 ; eintr
je short .loop
cmp eax, 0
jl near panic ; negative result = write error
add esi, eax
sub edx, eax
jnz short .loop
pop r11
pop rdi
pop rsi
pop rdx
pop rcx
pop rax
ret
;----------------------------------------------------------------
end: ; end of code
absolute 0x0050_0000
heap: ; start of heap
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment