Created
January 9, 2022 03:37
-
-
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.
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
; 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