; Advent of Code 2025 - Day 1 bits 64 default rel global _start %include "utils/io.asm" section .data input_path db "input/day1.txt", 0 label1 db "Password 1: ", 0 label2 db "Password 2: ", 0 angle1 dd 50 angle2 dd 50 password1 dd 0 password2 dd 0 section .bss buffer resb 4096 line_buffer resb 256 line_len resd 1 section .text _start: ; open file mov rax, SYS_OPEN mov rdi, input_path mov rsi, O_RDONLY xor rdx, rdx syscall mov r12, rax ; fd in R12 mov dword [line_len], 0 ; no chars in current line yet read_loop: ; read up to 4096 bytes mov rax, SYS_READ mov rdi, r12 mov rsi, buffer mov rdx, 4096 syscall cmp rax, 0 jle eof ; <=0 -> EOF or error mov rdi, buffer ; scan pointer mov rbp, rax ; bytes remaining in this chunk scan_chunk: cmp rbp, 0 je read_loop ; chunk done, read next mov al, [rdi] ; windows can be a fucking bitch ; handle (CR) -> just skip cmp al, 13 je skip_cr ; handle LF (10) -> end of line cmp al, 10 je end_of_line ; normal character -> append to line_buffer if space left mov edx, [line_len] cmp edx, 255 ; leave 1 byte for NULL jae skip_store ; ignore extra characters if too long mov [line_buffer+rdx], al inc edx mov [line_len], edx skip_store: inc rdi dec rbp jmp scan_chunk skip_cr: inc rdi dec rbp jmp scan_chunk end_of_line: ; terminate current line mov edx, [line_len] mov byte [line_buffer+rdx], 0 ; if line isn't empty, process it cmp edx, 0 je line_done push rdi mov rsi, line_buffer call handle_line pop rdi line_done: mov dword [line_len], 0 ; reset line length inc rdi dec rbp jmp scan_chunk ; process last line if not newline-terminated, then print result eof: ; if there is a partial line at EOF, process it mov edx, [line_len] cmp edx, 0 jle print_results mov byte [line_buffer+rdx], 0 mov rsi, line_buffer call handle_line print_results: mov rcx, label1 call print_string mov eax, [password1] call print_int64 call print_newline mov rcx, label2 call print_string mov eax, [password2] call print_int64 call print_newline mov rax, SYS_CLOSE mov rdi, r12 syscall mov rax, SYS_EXIT xor rdi, rdi syscall ; Handle one line: RSI -> "L123" or "R45" handle_line: mov al, [rsi] cmp al, 'L' je .left mov ebx, 1 ; direction = +1 for 'R' or anything else jmp .after .left: mov ebx, -1 ; direction = -1 for 'L' .after: inc rsi ; skip 'L' or 'R' call parse_int ; EAX = steps mov ecx, eax ; Part 1 p1_start: mov eax, ecx imul eax, ebx add eax, [angle1] call normalize_angle mov [angle1], eax cmp eax, 0 jne p2_start mov rdi, password1 mov edx, [rdi] inc edx mov [rdi], edx ; Part 2 p2_start: mov rdi, password2 .loop: test ecx, ecx jle p2_exit dec ecx mov eax, [angle2] call update_angle_count mov [angle2], eax jmp .loop p2_exit: ret ; normalize eax to 0..99 normalize_angle: cmp eax, 0 jl .add cmp eax, 100 jl .done sub eax, 100 jmp normalize_angle .add: add eax, 100 jmp normalize_angle .done: ret ; eax = angle, ebx = direction, rdi = password counter addr update_angle_count: add eax, ebx call normalize_angle cmp eax, 0 jne .done mov edx, [rdi] inc edx mov [rdi], edx .done: ret ; parse ASCII integer into EAX parse_int: xor eax, eax ; result = 0 .parse_loop: mov dl, [rsi] cmp dl, '0' jl .done cmp dl, '9' jg .done sub dl, '0' ; digit value 0..9 movzx edx, dl imul eax, eax, 10 ; result *= 10 add eax, edx ; result += digit inc rsi jmp .parse_loop .done: ret