; Advent of Code 2025 – Day 2 bits 64 default rel global _start %include "utils/io.asm" %include "utils/parse.asm" section .data input_path db "input/day2.txt", 0 label1 db "Part 1: ", 0 label2 db "Part 2: ", 0 section .bss buffer resb 4096 line_buffer resb 65536 line_len resd 1 sum1 resq 1 sum2 resq 1 section .text _start: mov rax, SYS_OPEN mov rdi, input_path mov rsi, O_RDONLY xor rdx, rdx syscall mov r12, rax ; file descriptor mov dword [line_len], 0 read_loop: mov rax, SYS_READ mov rdi, r12 mov rsi, buffer mov rdx, 4096 syscall cmp rax, 0 jle eof mov rdi, buffer mov rbp, rax scan_chunk: cmp rbp, 0 je read_loop mov al, [rdi] cmp al, 13 je skip_cr cmp al, 10 je end_line mov edx, [line_len] cmp edx, 65535 jae skip_store 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_line: mov edx, [line_len] mov byte [line_buffer+rdx], 0 cmp edx, 0 je clear_line push rdi mov rsi, line_buffer call handle_line pop rdi clear_line: mov dword [line_len], 0 inc rdi dec rbp jmp scan_chunk eof: ; process final partial line if needed 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 rax, [sum1] call print_int64 call print_newline mov rcx, label2 call print_string mov rax, [sum2] call print_int64 call print_newline mov rax, SYS_CLOSE mov rdi, r12 syscall mov rax, SYS_EXIT xor rdi, rdi syscall ; parse "A-B,C-D,..."l, RSI = pointer to line ; uses r13 = current ID, r14 = end of range handle_line: next_range: ; parse start call parse_u64 mov r13, rax cmp byte [rsi], '-' jne done_line inc rsi ; parse end call parse_u64 mov r14, rax range_loop: cmp r13, r14 jg after_range ; Save RSI (line pointer) across the checks, ; because number_to_string / check_invalid* use RSI push rsi ; Part 1: exact AA mov rax, r13 call check_invalid1 test rax, rax jz .skip_p1 add qword [sum1], r13 .skip_p1: ; Part 2: any repeating block >= 2 mov rax, r13 call check_invalid2 test rax, rax jz .skip_p2 add qword [sum2], r13 .skip_p2: pop rsi ; restore line pointer inc r13 jmp range_loop after_range: cmp byte [rsi], ',' jne done_line inc rsi jmp next_range done_line: ret ; number_to_string: convert RAX to decimal ASCII, MSB-first ; Output: ; RSI = pointer to first digit ; ECX = length number_to_string: mov rdi, number_buffer+31 mov byte [rdi], 0 ; terminator (for convenience) dec rdi cmp rax, 0 jne .conv ; handle zero mov byte [rdi], '0' mov rsi, rdi mov ecx, 1 ret .conv: mov rbx, 10 .next_digit: xor rdx, rdx div rbx ; RAX = quotient, RDX = remainder add dl, '0' mov [rdi], dl dec rdi test rax, rax jnz .next_digit inc rdi ; now RDI points to first digit mov rsi, rdi mov rdx, number_buffer+31 sub rdx, rsi ; RDX = length mov ecx, edx ret ; check_invalid1: "AA" pattern ; RAX = number ; Returns RAX = 1 if digits == A A, else 0 check_invalid1: push rbx push rdx call number_to_string ; RSI=string, ECX=len mov eax, ecx ; EAX = len cmp eax, 2 jl .no1 test eax, 1 jnz .no1 ; odd length => cannot be AA shr eax, 1 ; half = len/2 mov r8d, eax ; half xor r9d, r9d ; i = 0 .loop1: cmp r9d, r8d jge .yes1 ; s[i] mov rdx, r9 movzx eax, byte [rsi + rdx] ; s[i+half] mov rdx, r9 add rdx, r8 movzx ebx, byte [rsi + rdx] cmp eax, ebx jne .no1 inc r9d jmp .loop1 .yes1: mov rax, 1 jmp .ret1 .no1: xor rax, rax .ret1: pop rdx pop rbx ret ; check_invalid2: any repeating block >= 2 ; RAX = number ; Returns RAX = 1 if string is some A repeated k>=2 times check_invalid2: push rbx push rdx call number_to_string ; RSI=string, ECX=len mov r11d, ecx ; n = length cmp r11d, 2 jb .no2 mov r8d, 1 ; m = 1 (candidate period) .outer: cmp r8d, r11d jge .no2 ; if n % m != 0 => cannot tile mov eax, r11d xor edx, edx div r8d ; EAX=q, EDX=r test edx, edx jnz .next_m mov r9d, r11d sub r9d, r8d ; limit = n - m xor r10d, r10d ; i = 0 .inner: cmp r10d, r9d jge .yes2 ; all positions checked ; s[i] mov rdx, r10 movzx eax, byte [rsi + rdx] ; s[i+m] mov rdx, r10 add rdx, r8 movzx ebx, byte [rsi + rdx] cmp eax, ebx jne .next_m inc r10d jmp .inner .yes2: mov rax, 1 jmp .ret2 .next_m: inc r8d jmp .outer .no2: xor rax, rax .ret2: pop rdx pop rbx ret