format ELF64 executable 3

struc s_stat
{
    .st_dev dq 0
    .st_ino dq 0
    .st_nlink dq 0
    .st_mode dd 0
    .st_uid dd 0
    .st_gid dq 0
    .st_rdev dq 0
    .st_size dq 0
    .st_blksize dq 0
    .st_blocks dq 0
    .st_atim dq 0
    .st_atim_nano dq 0
    .st_mtim dq 0
    .st_mtim_nano dq 0
    .st_ctim dq 0
    .st_ctim_nano dq 0
    .st_atim_idk dq 0
    .st_mtim_idk dq 0
    .st_ctim_idk dq 0
}

macro syscall1 nr, arg1
{
   mov rax, nr
   mov rdi, arg1
   syscall
}

macro syscall2 nr, arg1, arg2
{
   mov rax, nr
   mov rdi, arg1
   mov rsi, arg2
   syscall
}

macro syscall3 nr, arg1, arg2, arg3
{
   mov rax, nr
   mov rdi, arg1
   mov rsi, arg2
   mov rdx, arg3
   syscall
}

macro syscall6 nr, arg1, arg2, arg3, arg4, arg5, arg6
{
   mov rax, nr
   mov rdi, arg1
   mov rsi, arg2
   mov rdx, arg3
   mov r10, arg4
   mov r8, arg5
   mov r9, arg6
   syscall
}

STDOUT equ 1
STDERR equ 2

SYS_read equ 0
;; ssize_t read(int fd, void buf[.count], size_t count);
macro read fd,buf,count
{
   syscall3 SYS_read, fd, buf, count
}

SYS_write equ 1
;; ssize_t write(int fd, const void buf[.count], size_t count);
macro write fd,buf,count
{
   syscall3 SYS_write, fd, buf, count
}

SYS_open equ 2
;; int open(const char *pathname, int flags);
macro open pathname, flags
{
   syscall2 SYS_open, pathname, flags
}

SYS_close equ 3
;; int close(int fd);
macro close fd
{
   syscall1 SYS_close, fd
}

SYS_stat equ 4
;; int stat(const char *restrict pathname
;;          struct stat *restrict statbuf);
macro stat pathname, statbuf
{
   syscall2 SYS_stat, pathname, statbuf
}


PROT_READ equ 1
MAP_PRIVATE = 2
SYS_mmap equ 9
;; void *mmap(void addr[.length], size_t length, int prot, int flags,
;;            int fd, off_t offset);
macro mmap addr, length, prot, flags, fd, offset
{
   syscall6 SYS_mmap, addr, length, prot, flags, fd, offset
}

SYS_munmap equ 11
;; int munmap(void addr[.length], size_t length);
macro munmap addr, length
{
   syscall2 SYS_munmap, addr, length
}

SYS_exit equ 60
;; [[noreturn]] void _exit(int status);
macro exit status
{
   syscall1 SYS_exit, status
}

segment readable executable
entry main

main:
	;; Determine the file size of the input file
	stat input_filename, file_stat

	;; Open a file descriptor for the input file
	open input_filename, 0
	mov [input_fd], rax

        ;; Map the input file into memory
	mmap 0, [file_stat.st_size], PROT_READ, MAP_PRIVATE, [input_fd], 0
	mov [input], rax

	;; Close the input file
	close [input_fd]

	;; Setup the loop variables for counting 
	;; rbx starts at the first byte of input, and is incremented to read it all
	mov rbx, [input]
	;; rcx tracks the current floor
	mov rcx, 0
	;; rdx counts down from the file's size to 0
	mov rdx, [file_stat.st_size]

lp:
	;; Is the current character the "go up" character?
	cmp byte [rbx], '('
	;; If so, jump to "up"
	je up
	;; If not, it's the "go down" character, so decrement the current floor
	dec rcx
	;; Skip over the "increment"
	jmp moved
up:
	;; We should "go up", so increment the current floor
	inc rcx
moved:
	;; Check the next character in the file
	inc rbx
	;; Count down the file's length
	dec rdx
	;; If we haven't reached the end of the file, loop
	jnz lp

	;; Put part 1's answer in rax
	mov rax, rcx
	mov rbx, part_1
	call itoa

	;; Write part 1's verbiage to STDOUT
	write STDOUT, part_1_verbiage, 8
	write STDOUT, part_1, 6
	write STDOUT, newline, 1

	;; Setup the loop variables for counting 
	;; rbx starts at the first byte of input, and is incremented to read it all
	mov rbx, [input]
	;; rcx tracks the current floor
	mov rcx, 0
	;; rdx tracks how many steps we've made so far
	mov rdx, 0
lp2:
	cmp byte [rbx], '('
	je up2
	dec rcx
	jmp moved2
up2:
	inc rcx
moved2:
	inc rdx
	cmp rcx, 0
	jl done
	inc rbx
	jnz lp2
done:

	;; Put part 2's answer in rax
	mov rax, rdx
	mov rbx, part_2
	call itoa

	;; Write part 2's verbiage to STDOUT
	write STDOUT, part_2_verbiage, 8
	write STDOUT, part_2, 6
	write STDOUT, newline, 1

	;; Unmap the mapped file
	munmap [input], [file_stat.st_size]

	;; Done!
	exit 0

;; rax contains the integer
;; rbx contains a pointer to the string buffer
itoa:
   mov byte [rbx], 48
   mov byte [rbx + 1], 48
   mov byte [rbx + 2], 48
   mov byte [rbx + 3], 48
   mov byte [rbx + 4], 48
   mov byte [rbx + 5], 48
   sub rax, 100000
   inc byte [rbx]
   cmp rax, 0
   jge itoa
   add rax, 100000
   dec byte [rbx]
.ten_thousand:
   sub rax, 10000
   inc byte [rbx + 1]
   cmp rax, 0
   jge .ten_thousand
   add rax, 10000
   dec byte [rbx + 1]
.one_thousand:
   sub rax, 1000
   inc byte [rbx + 2]
   cmp rax, 0
   jge .one_thousand
   add rax, 1000
   dec byte [rbx + 2]
.one_hundred:
   sub rax, 100
   inc byte [rbx + 3]
   cmp rax, 0
   jge .one_hundred
   add rax, 100
   dec byte [rbx + 3]
.ten:
   sub rax, 10
   inc byte [rbx + 4]
   cmp rax, 0
   jge .ten
   add rax, 10
   dec byte [rbx + 4]
   add [rbx + 5], rax
   ret

segment readable writable
input_filename db '../data/2015/1/input.txt', 0
input_fd dq -1
input dq -1
part_1_verbiage db 'Part 1: ', 0
part_1 db '     ', 0
part_2_verbiage db 'Part 2: ', 0
part_2 db '     ', 0
newline db 10
file_stat s_stat