hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/aoc-2025
diff options
context:
space:
mode:
Diffstat (limited to 'aoc-2025')
-rw-r--r--aoc-2025/day1.asm178
-rw-r--r--aoc-2025/day2.asm192
-rw-r--r--aoc-2025/day3.asm146
-rw-r--r--aoc-2025/day4.asm322
-rw-r--r--aoc-2025/day5.asm331
-rw-r--r--aoc-2025/day6.asm419
-rw-r--r--aoc-2025/day7.asm390
-rw-r--r--aoc-2025/day8.asm431
8 files changed, 2140 insertions, 269 deletions
diff --git a/aoc-2025/day1.asm b/aoc-2025/day1.asm
index 8ba8f0e..f130675 100644
--- a/aoc-2025/day1.asm
+++ b/aoc-2025/day1.asm
@@ -15,7 +15,7 @@ global _start
%define STDOUT 1
section .data
- inputPath db "input/day1.txt", 0
+ input_path db "input/day1.txt", 0
newline db 10
label1 db "Password 1: ", 0
@@ -29,24 +29,24 @@ section .data
section .bss
buffer resb 4096
- lineBuffer resb 256
- numberBuffer resb 16
- lineLen resd 1
+ line_buffer resb 256
+ number_buffer resb 16
+ line_len resd 1
section .text
_start:
; open file
mov rax, SYS_OPEN
- mov rdi, inputPath
+ mov rdi, input_path
mov rsi, O_RDONLY
xor rdx, rdx
syscall
- mov r12, rax ; fd in R12
- mov dword [lineLen], 0 ; no chars in current line yet
+ mov r12, rax ; fd in R12
+ mov dword [line_len], 0 ; no chars in current line yet
-ReadLoop:
+read_loop:
; read up to 4096 bytes
mov rax, SYS_READ
mov rdi, r12
@@ -55,80 +55,80 @@ ReadLoop:
syscall
cmp rax, 0
- jle EOF ; <=0 -> EOF or error
+ jle eof ; <=0 -> EOF or error
- mov rdi, buffer ; scan pointer
- mov rbp, rax ; bytes remaining in this chunk
+ mov rdi, buffer ; scan pointer
+ mov rbp, rax ; bytes remaining in this chunk
-ScanChunk:
+scan_chunk:
cmp rbp, 0
- je ReadLoop ; chunk done, read next
+ je read_loop ; chunk done, read next
mov al, [rdi]
; windows can be a fucking bitch
; handle (CR) -> just skip
cmp al, 13
- je SkipCR
+ je skip_cr
; handle LF (10) -> end of line
cmp al, 10
- je EndOfLine
+ je end_of_line
- ; normal character -> append to lineBuffer if space left
- mov edx, [lineLen]
- cmp edx, 255 ; leave 1 byte for NULL
- jae SkipStore ; ignore extra characters if too long
- mov [lineBuffer+rdx], al
+ ; 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 [lineLen], edx
+ mov [line_len], edx
-SkipStore:
+skip_store:
inc rdi
dec rbp
- jmp ScanChunk
+ jmp scan_chunk
-SkipCR:
+skip_cr:
inc rdi
dec rbp
- jmp ScanChunk
+ jmp scan_chunk
-EndOfLine:
+end_of_line:
; terminate current line
- mov edx, [lineLen]
- mov byte [lineBuffer+rdx], 0
+ mov edx, [line_len]
+ mov byte [line_buffer+rdx], 0
; if line isn't empty, process it
cmp edx, 0
- je LineDone
+ je line_done
push rdi
- mov rsi, lineBuffer
- call HandleLine
+ mov rsi, line_buffer
+ call handle_line
pop rdi
-LineDone:
- mov dword [lineLen], 0 ; reset line length
+line_done:
+ mov dword [line_len], 0 ; reset line length
inc rdi
dec rbp
- jmp ScanChunk
+ jmp scan_chunk
; process last line if not newline-terminated, then print result
-EOF:
+eof:
; if there is a partial line at EOF, process it
- mov edx, [lineLen]
+ mov edx, [line_len]
cmp edx, 0
- jle PrintResults
- mov byte [lineBuffer+rdx], 0
+ jle print_results
+ mov byte [line_buffer+rdx], 0
- mov rsi, lineBuffer
- call HandleLine
+ mov rsi, line_buffer
+ call handle_line
-PrintResults:
+print_results:
mov rcx, label1
- call PrintString
+ call print_string
mov eax, [password1]
- call PrintInt
+ call print_int
; newline
mov rax, SYS_WRITE
@@ -138,9 +138,9 @@ PrintResults:
syscall
mov rcx, label2
- call PrintString
+ call print_string
mov eax, [password2]
- call PrintInt
+ call print_int
; new linhe
mov rax, SYS_WRITE
@@ -157,67 +157,67 @@ PrintResults:
syscall
; Handle one line: RSI -> "L123" or "R45"
-HandleLine:
+handle_line:
mov al, [rsi]
cmp al, 'L'
je .left
- mov ebx, 1 ; direction = +1 for 'R' or anything else
+ 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 ParseInt ; EAX = steps
+ inc rsi ; skip 'L' or 'R'
+ call parse_int ; EAX = steps
mov ecx, eax
; Part 1
-P1_Start:
+p1_start:
mov eax, ecx
imul eax, ebx
add eax, [angle1]
- call NormalizeAngle
+ call normalize_angle
mov [angle1], eax
cmp eax, 0
- jne P2_Start
+ jne p2_start
mov rdi, password1
mov edx, [rdi]
inc edx
mov [rdi], edx
; Part 2
-P2_Start:
+p2_start:
mov rdi, password2
.loop:
test ecx, ecx
- jle P2_Exit
+ jle p2_exit
dec ecx
mov eax, [angle2]
- call UpdateAngleCount
+ call update_angle_count
mov [angle2], eax
jmp .loop
-P2_Exit:
+p2_exit:
ret
; normalize eax to 0..99
-NormalizeAngle:
+normalize_angle:
cmp eax, 0
jl .add
cmp eax, 100
jl .done
sub eax, 100
- jmp NormalizeAngle
+ jmp normalize_angle
.add:
add eax, 100
- jmp NormalizeAngle
+ jmp normalize_angle
.done:
ret
; eax = angle, ebx = direction, rdi = password counter addr
-UpdateAngleCount:
+update_angle_count:
add eax, ebx
- call NormalizeAngle
+ call normalize_angle
cmp eax, 0
jne .done
@@ -228,29 +228,29 @@ UpdateAngleCount:
ret
; parse ASCII integer into EAX
-ParseInt:
- xor eax, eax ; result = 0
-.parseLoop:
+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
+ sub dl, '0' ; digit value 0..9
movzx edx, dl
- imul eax, eax, 10 ; result *= 10
- add eax, edx ; result += digit
+ imul eax, eax, 10 ; result *= 10
+ add eax, edx ; result += digit
inc rsi
- jmp .parseLoop
+ jmp .parse_loop
.done:
ret
-PrintInt:
- mov edi, numberBuffer+15 ; write digits from the end
- mov byte [rdi], 0 ; terminator
+print_int:
+ mov edi, number_buffer+15 ; write digits from the end
+ mov byte [rdi], 0 ; terminator
dec rdi
cmp eax, 0
@@ -262,48 +262,48 @@ PrintInt:
; q = eax / 10
; r = eax % 10
; using only subtraction
-.nextDigit:
- mov edx, eax ; tmp = current value
- mov ecx, 0 ; q = 0
+.next_digit:
+ mov edx, eax ; tmp = current value
+ mov ecx, 0 ; q = 0
- cmp edx, 10 ; if < 0, q=0, r=edx
- jb .lastDigit
-.subLoop:
+ cmp edx, 10 ; if < 0, q=0, r=edx
+ jb .last_digit
+.sub_loop:
sub edx, 10
- inc ecx ; q++
+ inc ecx ; q++
cmp edx, 10
- jae .subLoop ; while tmp >= 10
-.lastDigit:
+ jae .sub_loop ; while tmp >= 10
+.last_digit:
; now:
; ecx = quotient
; edx = remainder (0..9)
add dl, '0'
mov [rdi], dl ; store digit
dec rdi
- mov eax, ecx ; eax = quotient
+ mov eax, ecx ; eax = quotient
test eax, eax
- jnz .nextDigit ; more digits to extract?
+ jnz .next_digit ; more digits to extract?
- inc rdi ; move to first digit
+ inc rdi ; move to first digit
.finish:
mov rax, SYS_WRITE
- mov rsi, rdi ; buffer pointer
+ mov rsi, rdi ; buffer pointer
mov rdi, STDOUT
- mov rdx, numberBuffer+15
- sub rdx, rsi ; length
+ mov rdx, number_buffer+15
+ sub rdx, rsi ; length
syscall
ret
-PrintString:
+print_string:
push rcx
push rdx
mov rdx, 0
.count:
cmp byte [rcx+rdx], 0
- je .doneCount
+ je .done_count
inc rdx
jmp .count
-.doneCount:
+.done_count:
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, rcx
diff --git a/aoc-2025/day2.asm b/aoc-2025/day2.asm
index dfbfb38..c1ae232 100644
--- a/aoc-2025/day2.asm
+++ b/aoc-2025/day2.asm
@@ -15,7 +15,7 @@ global _start
%define STDOUT 1
section .data
- inputPath db "input/day2.txt", 0
+ input_path db "input/day2.txt", 0
newline db 10
label1 db "Part 1: ", 0
@@ -23,27 +23,27 @@ section .data
section .bss
buffer resb 4096
- lineBuffer resb 65536
- lineLen resd 1
+ line_buffer resb 65536
+ line_len resd 1
sum1 resq 1
sum2 resq 1
- numberBuffer resb 32 ; for printing and NumberToString
+ number_buffer resb 32 ; for printing and number_to_string
section .text
_start:
mov rax, SYS_OPEN
- mov rdi, inputPath
+ mov rdi, input_path
mov rsi, O_RDONLY
xor rdx, rdx
syscall
- mov r12, rax ; file descriptor
- mov dword [lineLen], 0
+ mov r12, rax ; file descriptor
+ mov dword [line_len], 0
-ReadLoop:
+read_loop:
mov rax, SYS_READ
mov rdi, r12
mov rsi, buffer
@@ -51,80 +51,80 @@ ReadLoop:
syscall
cmp rax, 0
- jle EOF
+ jle eof
mov rdi, buffer
mov rbp, rax
-ScanChunk:
+scan_chunk:
cmp rbp, 0
- je ReadLoop
+ je read_loop
mov al, [rdi]
cmp al, 13
- je SkipCR
+ je skip_cr
cmp al, 10
- je EndLine
+ je end_line
- mov edx, [lineLen]
+ mov edx, [line_len]
cmp edx, 65535
- jae SkipStore
- mov [lineBuffer+rdx], al
+ jae skip_store
+ mov [line_buffer+rdx], al
inc edx
- mov [lineLen], edx
+ mov [line_len], edx
-SkipStore:
+skip_store:
inc rdi
dec rbp
- jmp ScanChunk
+ jmp scan_chunk
-SkipCR:
+skip_cr:
inc rdi
dec rbp
- jmp ScanChunk
+ jmp scan_chunk
-EndLine:
- mov edx, [lineLen]
- mov byte [lineBuffer+rdx], 0
+end_line:
+ mov edx, [line_len]
+ mov byte [line_buffer+rdx], 0
cmp edx, 0
- je ClearLine
+ je clear_line
push rdi
- mov rsi, lineBuffer
- call HandleLine
+ mov rsi, line_buffer
+ call handle_line
pop rdi
-ClearLine:
- mov dword [lineLen], 0
+clear_line:
+ mov dword [line_len], 0
inc rdi
dec rbp
- jmp ScanChunk
+ jmp scan_chunk
-EOF:
+eof:
; process final partial line if needed
- mov edx, [lineLen]
+ mov edx, [line_len]
cmp edx, 0
- jle PrintResults
+ jle print_results
- mov byte [lineBuffer+rdx], 0
- mov rsi, lineBuffer
- call HandleLine
+ mov byte [line_buffer+rdx], 0
+ mov rsi, line_buffer
+ call handle_line
-PrintResults:
+print_results:
mov rcx, label1
- call PrintString
+ call print_string
mov rax, [sum1]
- call PrintInt64
- call Newline
+ call print_int64
+ call print_newline
mov rcx, label2
- call PrintString
+ call print_string
mov rax, [sum2]
- call PrintInt64
- call Newline
+ call print_int64
+ call print_newline
mov rax, SYS_CLOSE
mov rdi, r12
@@ -136,58 +136,58 @@ PrintResults:
; parse "A-B,C-D,..."l, RSI = pointer to line
; uses r13 = current ID, r14 = end of range
-HandleLine:
-NextRange:
+handle_line:
+next_range:
; parse start
- call ParseInt64
+ call parse_int64
mov r13, rax
cmp byte [rsi], '-'
- jne DoneLine
+ jne done_line
inc rsi
; parse end
- call ParseInt64
+ call parse_int64
mov r14, rax
-RangeLoop:
+range_loop:
cmp r13, r14
- jg AfterRange
+ jg after_range
; Save RSI (line pointer) across the checks,
- ; because NumberToString / CheckInvalid* use RSI
+ ; because number_to_string / check_invalid* use RSI
push rsi
; Part 1: exact AA
mov rax, r13
- call CheckInvalid1
+ call check_invalid1
test rax, rax
- jz .skipP1
+ jz .skip_p1
add qword [sum1], r13
-.skipP1:
+.skip_p1:
; Part 2: any repeating block >= 2
mov rax, r13
- call CheckInvalid2
+ call check_invalid2
test rax, rax
- jz .skipP2
+ jz .skip_p2
add qword [sum2], r13
-.skipP2:
- pop rsi ; restore line pointer
+.skip_p2:
+ pop rsi ; restore line pointer
inc r13
- jmp RangeLoop
+ jmp range_loop
-AfterRange:
+after_range:
cmp byte [rsi], ','
- jne DoneLine
+ jne done_line
inc rsi
- jmp NextRange
+ jmp next_range
-DoneLine:
+done_line:
ret
; RSI points at decimal digits; stops on first non-digit
-ParseInt64:
+parse_int64:
xor rax, rax
.parse:
mov dl, [rsi]
@@ -206,13 +206,13 @@ ParseInt64:
.done:
ret
-; NumberToString: convert RAX to decimal ASCII, MSB-first
+; number_to_string: convert RAX to decimal ASCII, MSB-first
; Output:
; RSI = pointer to first digit
; ECX = length
-NumberToString:
- mov rdi, numberBuffer+31
- mov byte [rdi], 0 ; terminator (for convenience)
+number_to_string:
+ mov rdi, number_buffer+31
+ mov byte [rdi], 0 ; terminator (for convenience)
dec rdi
cmp rax, 0
@@ -226,41 +226,41 @@ NumberToString:
.conv:
mov rbx, 10
-.nextDigit:
+.next_digit:
xor rdx, rdx
- div rbx ; RAX = quotient, RDX = remainder
+ div rbx ; RAX = quotient, RDX = remainder
add dl, '0'
mov [rdi], dl
dec rdi
test rax, rax
- jnz .nextDigit
+ jnz .next_digit
- inc rdi ; now RDI points to first digit
+ inc rdi ; now RDI points to first digit
mov rsi, rdi
- mov rdx, numberBuffer+31
- sub rdx, rsi ; RDX = length
+ mov rdx, number_buffer+31
+ sub rdx, rsi ; RDX = length
mov ecx, edx
ret
-; CheckInvalid1: "AA" pattern
+; check_invalid1: "AA" pattern
; RAX = number
; Returns RAX = 1 if digits == A A, else 0
-CheckInvalid1:
+check_invalid1:
push rbx
push rdx
- call NumberToString ; RSI=string, ECX=len
- mov eax, ecx ; EAX = len
+ 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
+ jnz .no1 ; odd length => cannot be AA
- shr eax, 1 ; half = len/2
- mov r8d, eax ; half
- xor r9d, r9d ; i = 0
+ shr eax, 1 ; half = len/2
+ mov r8d, eax ; half
+ xor r9d, r9d ; i = 0
.loop1:
cmp r9d, r8d
@@ -293,20 +293,20 @@ CheckInvalid1:
pop rbx
ret
-; CheckInvalid2: any repeating block >= 2
+; check_invalid2: any repeating block >= 2
; RAX = number
; Returns RAX = 1 if string is some A repeated k>=2 times
-CheckInvalid2:
+check_invalid2:
push rbx
push rdx
- call NumberToString ; RSI=string, ECX=len
- mov r11d, ecx ; n = length
+ 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)
+ mov r8d, 1 ; m = 1 (candidate period)
.outer:
cmp r8d, r11d
jge .no2
@@ -314,16 +314,16 @@ CheckInvalid2:
; if n % m != 0 => cannot tile
mov eax, r11d
xor edx, edx
- div r8d ; EAX=q, EDX=r
+ 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
+ sub r9d, r8d ; limit = n - m
+ xor r10d, r10d ; i = 0
.inner:
cmp r10d, r9d
- jge .yes2 ; all positions checked
+ jge .yes2 ; all positions checked
; s[i]
mov rdx, r10
@@ -352,7 +352,7 @@ CheckInvalid2:
pop rbx
ret
-Newline:
+print_newline:
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, newline
@@ -360,8 +360,8 @@ Newline:
syscall
ret
-PrintInt64:
- mov rdi, numberBuffer+31
+print_int64:
+ mov rdi, number_buffer+31
mov byte [rdi], 0
dec rdi
@@ -386,12 +386,12 @@ PrintInt64:
mov rax, SYS_WRITE
mov rsi, rdi
mov rdi, STDOUT
- mov rdx, numberBuffer+31
+ mov rdx, number_buffer+31
sub rdx, rsi
syscall
ret
-PrintString:
+print_string:
push rcx
push rdx
mov rdx, 0
diff --git a/aoc-2025/day3.asm b/aoc-2025/day3.asm
index e547475..c8b8b75 100644
--- a/aoc-2025/day3.asm
+++ b/aoc-2025/day3.asm
@@ -15,7 +15,7 @@ global _start
%define STDOUT 1
section .data
- inputPath db "input/day3.txt", 0
+ input_path db "input/day3.txt", 0
newline db 10
@@ -24,28 +24,28 @@ section .data
section .bss
buffer resb 4096
- lineBuffer resb 65536
- lineLen resd 1
+ line_buffer resb 65536
+ line_len resd 1
sum1 resq 1
sum2 resq 1
- workDigits resb 64 ; stack for greedy subsequence
- numberBuffer resb 32 ; for printing
+ work_digits resb 64 ; stack for greedy subsequence
+ number_buffer resb 32 ; for printing
section .text
_start:
mov rax, SYS_OPEN
- mov rdi, inputPath
+ mov rdi, input_path
mov rsi, O_RDONLY
xor rdx, rdx
syscall
- mov r12, rax ; fd
- mov dword [lineLen], 0
+ mov r12, rax ; fd
+ mov dword [line_len], 0
-ReadLoop:
+read_loop:
mov rax, SYS_READ
mov rdi, r12
mov rsi, buffer
@@ -53,80 +53,80 @@ ReadLoop:
syscall
cmp rax, 0
- jle EOF
+ jle eof
mov rdi, buffer
mov rbp, rax
-ScanChunk:
+scan_chunk:
cmp rbp, 0
- je ReadLoop
+ je read_loop
mov al, [rdi]
cmp al, 13
- je SkipCR
+ je skip_cr
cmp al, 10
- je EndLine
+ je end_line
- mov edx, [lineLen]
+ mov edx, [line_len]
cmp edx, 65535
- jae SkipStore
- mov [lineBuffer+rdx], al
+ jae skip_store
+ mov [line_buffer+rdx], al
inc edx
- mov [lineLen], edx
+ mov [line_len], edx
-SkipStore:
+skip_store:
inc rdi
dec rbp
- jmp ScanChunk
+ jmp scan_chunk
-SkipCR:
+skip_cr:
inc rdi
dec rbp
- jmp ScanChunk
+ jmp scan_chunk
-EndLine:
- mov edx, [lineLen]
- mov byte [lineBuffer+edx], 0
+end_line:
+ mov edx, [line_len]
+ mov byte [line_buffer+edx], 0
cmp edx, 0
- je ClearLine
+ je clear_line
push rdi
- mov rsi, lineBuffer
- call HandleLine
+ mov rsi, line_buffer
+ call handle_line
pop rdi
-ClearLine:
- mov dword [lineLen], 0
+clear_line:
+ mov dword [line_len], 0
inc rdi
dec rbp
- jmp ScanChunk
+ jmp scan_chunk
-EOF:
+eof:
; possible last partial line
- mov edx, [lineLen]
+ mov edx, [line_len]
cmp edx, 0
- jle PrintResults
+ jle print_results
- mov byte [lineBuffer+edx], 0
- mov rsi, lineBuffer
- call HandleLine
+ mov byte [line_buffer+edx], 0
+ mov rsi, line_buffer
+ call handle_line
-PrintResults:
+print_results:
mov rcx, label1
- call PrintString
+ call print_string
mov rax, [sum1]
- call PrintInt64
- call Newline
+ call print_int64
+ call print_newline
mov rcx, label2
- call PrintString
+ call print_string
mov rax, [sum2]
- call PrintInt64
- call Newline
+ call print_int64
+ call print_newline
mov rax, SYS_CLOSE
mov rdi, r12
@@ -136,34 +136,34 @@ PrintResults:
xor rdi, rdi
syscall
-HandleLine:
+handle_line:
mov rdi, rsi
- xor ecx, ecx ; ECX = 0
-.lengthLoop:
+ xor ecx, ecx ; ECX = 0
+.length_loop:
mov al, [rdi+rcx]
cmp al, 0
- je .lengthDone
+ je .length_done
inc ecx
- jmp .lengthLoop
-.lengthDone:
- mov r15d, ecx ; r15d = n
+ jmp .length_loop
+.length_done:
+ mov r15d, ecx ; r15d = n
; part 1: k = 2
- mov ecx, r15d ; ECX = n
- mov edi, 2 ; K = 2
- call GetMaxForK ; RAX = best 2-digit value
+ mov ecx, r15d ; ECX = n
+ mov edi, 2 ; K = 2
+ call get_max_for_k ; RAX = best 2-digit value
add qword [sum1], rax
; part 2: k = 12, only if n >= 12
cmp r15d, 12
- jl .skipPart2
+ jl .skip_part2
mov ecx, r15d
mov edi, 12
- call GetMaxForK ; RAX = best 12-digit value
+ call get_max_for_k ; RAX = best 12-digit value
add qword [sum2], rax
-.skipPart2:
+.skip_part2:
ret
; Greedy lexicographically largest subsequence of length K
@@ -175,14 +175,14 @@ HandleLine:
; Output:
; RAX = numeric value of chosen k digits
; Clobbers: R8..R11, EAX, EDX
-GetMaxForK:
- ; dropsRemaining = n - k
+get_max_for_k:
+ ; drops_remaining = n - k
mov eax, ecx
sub eax, edi
- mov r8d, eax ; r8d = drops
+ mov r8d, eax ; r8d = drops
- xor r9d, r9d ; r9d = stackSize
- xor r10d, r10d ; r10d = i
+ xor r9d, r9d ; r9d = stack_size
+ xor r10d, r10d ; r10d = i
.loop:
cmp r10d, ecx
@@ -201,7 +201,7 @@ GetMaxForK:
mov edx, r9d
dec edx
mov r11d, edx
- mov bl, [workDigits + r11]
+ mov bl, [work_digits + r11]
cmp bl, al
jge .push
@@ -211,13 +211,13 @@ GetMaxForK:
jmp .pop
.push:
mov r11d, r9d
- mov [workDigits + r11], al
+ mov [work_digits + r11], al
inc r9d
inc r10d
jmp .loop
.after_input:
- ; trim if stackSize > k
+ ; trim if stack_size > k
.trim:
cmp r9d, edi
jle .convert
@@ -226,12 +226,12 @@ GetMaxForK:
.convert:
xor rax, rax
xor r11d, r11d
-.convertLoop:
+.convert_loop:
cmp r11d, edi
jge .done
mov rdx, r11
- mov dl, [workDigits + rdx]
+ mov dl, [work_digits + rdx]
sub dl, '0'
movzx rdx, dl
@@ -239,11 +239,11 @@ GetMaxForK:
add rax, rdx
inc r11d
- jmp .convertLoop
+ jmp .convert_loop
.done:
ret
-Newline:
+print_newline:
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, newline
@@ -251,8 +251,8 @@ Newline:
syscall
ret
-PrintInt64:
- mov rdi, numberBuffer+31
+print_int64:
+ mov rdi, number_buffer+31
mov byte [rdi], 0
dec rdi
@@ -277,12 +277,12 @@ PrintInt64:
mov rax, SYS_WRITE
mov rsi, rdi
mov rdi, STDOUT
- mov rdx, numberBuffer+31
+ mov rdx, number_buffer+31
sub rdx, rsi
syscall
ret
-PrintString:
+print_string:
push rcx
push rdx
mov rdx, 0
diff --git a/aoc-2025/day4.asm b/aoc-2025/day4.asm
index 475f2ef..57867b1 100644
--- a/aoc-2025/day4.asm
+++ b/aoc-2025/day4.asm
@@ -1,25 +1,325 @@
+; Advent of Code 2025 - Day 4
+
bits 64
default rel
global _start
-%define SYS_EXIT 60
-%define SYS_WRITE 1
+%define SYS_EXIT 60
+%define SYS_READ 0
+%define SYS_WRITE 1
+%define SYS_OPEN 2
+%define SYS_CLOSE 3
+
+%define O_RDONLY 0
+%define STDOUT 1
-%define STDOUT 1
+%define RAW_SIZE 4194304 ; 4 MiB scratch for the raw file
+%define GRID_SIZE 4194304 ; 4 MiB for the padded grid / mark buffers
section .data
- message db "Hello Day 4!", 0xA
- length equ $-message
+ input_path db "input/day4.txt", 0
+
+ newline db 10
+
+ label1 db "Part 1: ", 0
+ label2 db "Part 2: ", 0
+
+section .bss
+ raw resb RAW_SIZE ; raw file bytes
+ grid resb GRID_SIZE ; padded grid: 1 = roll, 0 = empty
+ mark resb GRID_SIZE ; per-round removal marks
+
+ raw_len resq 1
+ width resq 1 ; real columns per row
+ height resq 1 ; real rows
+ stride resq 1 ; width + 2 (padding on both sides)
+
+ part1 resq 1
+ part2 resq 1
+
+ number_buffer resb 32 ; for printing
section .text
+
_start:
- mov rax, SYS_WRITE
- mov rdi, STDOUT
- mov rsi, message
- mov rdx, length
+ mov rax, SYS_OPEN
+ mov rdi, input_path
+ mov rsi, O_RDONLY
+ xor rdx, rdx
+ syscall
+ mov r12, rax ; fd
+
+ xor r13, r13 ; bytes read so far
+read_loop:
+ mov rax, SYS_READ
+ mov rdi, r12
+ lea rsi, [raw + r13]
+ mov rdx, RAW_SIZE
+ sub rdx, r13 ; remaining capacity
+ syscall
+
+ cmp rax, 0
+ jle read_done
+ add r13, rax
+ jmp read_loop
+read_done:
+ mov [raw_len], r13
+
+ mov rax, SYS_CLOSE
+ mov rdi, r12
+ syscall
+
+ xor rcx, rcx ; column scanner
+width_scan:
+ cmp rcx, [raw_len]
+ jge width_done
+ mov al, [raw + rcx]
+ cmp al, 13 ; CR
+ je width_done
+ cmp al, 10 ; LF
+ je width_done
+ inc rcx
+ jmp width_scan
+width_done:
+ mov [width], rcx
+ add rcx, 2 ; padding on left + right
+ mov [stride], rcx
+
+ ; real cell (r, c) lives at grid[(r+1)*stride + (c+1)]
+ mov r10, [raw_len]
+ xor r8, r8 ; i (raw index)
+ xor r9, r9 ; r (row, 0-based)
+ xor r11, r11 ; c (col, 0-based)
+fill_loop:
+ cmp r8, r10
+ jge fill_end
+ mov al, [raw + r8]
+ inc r8
+
+ cmp al, 13 ; CR -> ignore
+ je fill_loop
+ cmp al, 10 ; LF -> end of line
+ je fill_newline
+
+ cmp al, '@'
+ jne fill_next_col ; '.' leaves the zeroed cell alone
+
+ ; grid[(r9+1)*stride + (r11+1)] = 1
+ mov rbx, r9
+ inc rbx
+ imul rbx, [stride]
+ add rbx, r11
+ add rbx, 1
+ mov byte [grid + rbx], 1
+fill_next_col:
+ inc r11
+ jmp fill_loop
+fill_newline:
+ cmp r11, 0 ; blank line -> skip (e.g. trailing newline)
+ je fill_loop
+ inc r9
+ xor r11, r11
+ jmp fill_loop
+fill_end:
+ cmp r11, 0 ; last line had no trailing newline
+ je fill_height
+ inc r9
+fill_height:
+ mov [height], r9
+
+ mov r13, 1 ; row = 1..height
+p1_row:
+ cmp r13, [height]
+ jg p1_done
+ mov rbx, r13
+ imul rbx, [stride]
+ inc rbx ; idx of (row, col=1)
+ mov r12, 1 ; col = 1..width
+p1_col:
+ cmp r12, [width]
+ jg p1_row_next
+ movzx eax, byte [grid + rbx]
+ test eax, eax
+ jz p1_next ; not a roll
+ mov rdi, rbx
+ call count_neighbors
+ cmp rax, 4
+ jge p1_next ; 4 or more neighbors -> blocked
+ inc qword [part1]
+p1_next:
+ inc rbx
+ inc r12
+ jmp p1_col
+p1_row_next:
+ inc r13
+ jmp p1_row
+p1_done:
+
+peel_loop:
+ mov r13, 1
+mark_row:
+ cmp r13, [height]
+ jg mark_done
+ mov rbx, r13
+ imul rbx, [stride]
+ inc rbx
+ mov r12, 1
+mark_col:
+ cmp r12, [width]
+ jg mark_row_next
+ movzx eax, byte [grid + rbx]
+ test eax, eax
+ jz mark_zero ; empty cell
+ mov rdi, rbx
+ call count_neighbors
+ cmp rax, 4
+ jl mark_one ; accessible
+mark_zero:
+ mov byte [mark + rbx], 0
+ jmp mark_next
+mark_one:
+ mov byte [mark + rbx], 1
+mark_next:
+ inc rbx
+ inc r12
+ jmp mark_col
+mark_row_next:
+ inc r13
+ jmp mark_row
+mark_done:
+
+ xor r11, r11 ; removed this round
+ mov r13, 1
+apply_row:
+ cmp r13, [height]
+ jg apply_done
+ mov rbx, r13
+ imul rbx, [stride]
+ inc rbx
+ mov r12, 1
+apply_col:
+ cmp r12, [width]
+ jg apply_row_next
+ movzx eax, byte [mark + rbx]
+ test eax, eax
+ jz apply_next
+ mov byte [grid + rbx], 0
+ inc r11
+apply_next:
+ inc rbx
+ inc r12
+ jmp apply_col
+apply_row_next:
+ inc r13
+ jmp apply_row
+apply_done:
+ add [part2], r11
+ test r11, r11
+ jnz peel_loop ; something changed -> another round
+
+print_results:
+ mov rcx, label1
+ call print_string
+ mov rax, [part1]
+ call print_int64
+ call print_newline
+
+ mov rcx, label2
+ call print_string
+ mov rax, [part2]
+ call print_int64
+ call print_newline
+
+ mov rax, SYS_EXIT
+ xor rdi, rdi
+ syscall
+
+; count_neighbors: sum of the 8 cells around a padded index
+; Input: RDI = grid index
+; Output: RAX = number of neighboring rolls (0..8)
+; Clobbers: RAX, RCX, RDX, R8
+count_neighbors:
+ mov rcx, [stride]
+ xor eax, eax
+
+ mov rdx, rdi
+ sub rdx, rcx ; row above
+ movzx r8d, byte [grid + rdx - 1]
+ add eax, r8d
+ movzx r8d, byte [grid + rdx]
+ add eax, r8d
+ movzx r8d, byte [grid + rdx + 1]
+ add eax, r8d
+
+ movzx r8d, byte [grid + rdi - 1] ; same row, sides
+ add eax, r8d
+ movzx r8d, byte [grid + rdi + 1]
+ add eax, r8d
+
+ mov rdx, rdi
+ add rdx, rcx ; row below
+ movzx r8d, byte [grid + rdx - 1]
+ add eax, r8d
+ movzx r8d, byte [grid + rdx]
+ add eax, r8d
+ movzx r8d, byte [grid + rdx + 1]
+ add eax, r8d
+ ret
+
+print_newline:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, newline
+ mov rdx, 1
+ syscall
+ ret
+
+print_int64:
+ mov rdi, number_buffer+31
+ mov byte [rdi], 0
+ dec rdi
+
+ cmp rax, 0
+ jne .conv
+
+ mov byte [rdi], '0'
+ jmp .done
+.conv:
+ mov rbx, 10
+.next:
+ xor rdx, rdx
+ div rbx
+ add dl, '0'
+ mov [rdi], dl
+ dec rdi
+ test rax, rax
+ jnz .next
+
+ inc rdi
+.done:
+ mov rax, SYS_WRITE
+ mov rsi, rdi
+ mov rdi, STDOUT
+ mov rdx, number_buffer+31
+ sub rdx, rsi
syscall
+ ret
- mov rax, SYS_EXIT
- xor rdi, rdi
+print_string:
+ push rcx
+ push rdx
+ mov rdx, 0
+.length:
+ cmp byte [rcx+rdx], 0
+ je .submit
+ inc rdx
+ jmp .length
+.submit:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, rcx
syscall
+ pop rdx
+ pop rcx
+ ret
diff --git a/aoc-2025/day5.asm b/aoc-2025/day5.asm
new file mode 100644
index 0000000..4f395a0
--- /dev/null
+++ b/aoc-2025/day5.asm
@@ -0,0 +1,331 @@
+; Advent of Code 2025 - Day 5
+
+bits 64
+default rel
+
+global _start
+
+%define SYS_EXIT 60
+%define SYS_READ 0
+%define SYS_WRITE 1
+%define SYS_OPEN 2
+%define SYS_CLOSE 3
+
+%define O_RDONLY 0
+%define STDOUT 1
+
+%define RAW_SIZE 1048576 ; 1 MiB scratch for the raw file
+%define MAX_RANGES 8192 ; plenty for the ~177 input ranges
+
+section .data
+ input_path db "input/day5.txt", 0
+
+ newline db 10
+
+ label1 db "Part 1: ", 0
+ label2 db "Part 2: ", 0
+
+section .bss
+ raw resb RAW_SIZE
+ starts resq MAX_RANGES ; range/merged-interval starts
+ ends resq MAX_RANGES ; range/merged-interval ends
+
+ raw_len resq 1
+ n_ranges resq 1 ; ranges parsed in section 1
+ n_merged resq 1 ; disjoint intervals after merge
+ phase resq 1 ; 0 = ranges, 1 = available IDs
+
+ part1 resq 1
+ part2 resq 1
+
+ number_buffer resb 32
+
+section .text
+
+_start:
+ mov rax, SYS_OPEN
+ mov rdi, input_path
+ mov rsi, O_RDONLY
+ xor rdx, rdx
+ syscall
+ mov r12, rax ; fd
+
+ xor r13, r13
+read_loop:
+ mov rax, SYS_READ
+ mov rdi, r12
+ lea rsi, [raw + r13]
+ mov rdx, RAW_SIZE
+ sub rdx, r13
+ syscall
+ cmp rax, 0
+ jle read_done
+ add r13, rax
+ jmp read_loop
+read_done:
+ mov [raw_len], r13
+
+ mov rax, SYS_CLOSE
+ mov rdi, r12
+ syscall
+
+ ; RSI = cursor, R14 = end pointer (both preserved across helper calls)
+ lea rsi, [raw]
+ mov rax, [raw_len]
+ lea r14, [raw + rax]
+ mov qword [phase], 0
+
+scan_line:
+ cmp rsi, r14
+ jae scan_done
+ mov al, [rsi]
+ cmp al, 10 ; LF -> blank line
+ je got_blank
+ cmp al, 13 ; CR -> blank line
+ je got_blank
+
+ cmp qword [phase], 0
+ jne do_avail
+
+ call parse_u64 ; RAX = start
+ mov rcx, [n_ranges]
+ mov [starts + rcx*8], rax
+ inc rsi ; skip '-'
+ call parse_u64 ; RAX = end
+ mov rcx, [n_ranges]
+ mov [ends + rcx*8], rax
+ inc qword [n_ranges]
+ jmp skip_eol
+
+do_avail:
+ call parse_u64
+ mov rdi, rax
+ call is_fresh ; RAX = 1 if fresh
+ test rax, rax
+ jz skip_eol
+ inc qword [part1]
+ jmp skip_eol
+
+got_blank:
+ ; the blank line separates the two sections
+ cmp qword [phase], 0
+ jne skip_eol
+ call finalize_ranges ; sort + merge, compute part2
+ mov qword [phase], 1
+ ; fall through
+
+skip_eol:
+ cmp rsi, r14
+ jae scan_done
+ mov al, [rsi]
+ inc rsi
+ cmp al, 10 ; consume through the LF
+ jne skip_eol
+ jmp scan_line
+
+scan_done:
+ ; safety: if there was no blank line, still finalize the ranges
+ cmp qword [phase], 0
+ jne print_results
+ call finalize_ranges
+
+print_results:
+ mov rcx, label1
+ call print_string
+ mov rax, [part1]
+ call print_int64
+ call print_newline
+
+ mov rcx, label2
+ call print_string
+ mov rax, [part2]
+ call print_int64
+ call print_newline
+
+ mov rax, SYS_EXIT
+ xor rdi, rdi
+ syscall
+
+; parse_u64: read decimal digits at [RSI], advancing RSI past them.
+; Output: RAX = value. Stops on the first non-digit. Clobbers RAX, RDX.
+parse_u64:
+ xor rax, rax
+.digit:
+ mov dl, [rsi]
+ cmp dl, '0'
+ jl .done
+ cmp dl, '9'
+ jg .done
+ sub dl, '0'
+ movzx rdx, dl
+ imul rax, rax, 10
+ add rax, rdx
+ inc rsi
+ jmp .digit
+.done:
+ ret
+
+; is_fresh: test membership of an ID in the merged intervals.
+; Input: RDI = id
+; Output: RAX = 1 if the id lies in any interval, else 0
+; Intervals are sorted ascending and disjoint, so we can stop early.
+; Clobbers: RAX, RCX
+is_fresh:
+ xor rcx, rcx
+.loop:
+ cmp rcx, [n_merged]
+ jge .no
+ mov rax, [starts + rcx*8]
+ cmp rdi, rax
+ jl .no ; id below this start -> below all later ones
+ mov rax, [ends + rcx*8]
+ cmp rdi, rax
+ jle .yes ; start <= id <= end
+ inc rcx
+ jmp .loop
+.no:
+ xor eax, eax
+ ret
+.yes:
+ mov eax, 1
+ ret
+
+; finalize_ranges: insertion-sort the ranges by start, merge overlapping /
+; touching ones in place, store the disjoint intervals back into starts/ends,
+; set n_merged, and accumulate their total size into part2.
+; Clobbers: RAX, RCX, RDX, R8, R9, R10, R11, R12, R13
+finalize_ranges:
+ mov r8, 1 ; i
+.sort_i:
+ cmp r8, [n_ranges]
+ jge .sorted
+ mov r10, [starts + r8*8] ; key_s
+ mov r11, [ends + r8*8] ; key_e
+ mov r9, r8
+ dec r9 ; j = i - 1
+.sort_j:
+ cmp r9, 0
+ jl .sort_place
+ mov rax, [starts + r9*8]
+ cmp rax, r10
+ jle .sort_place ; starts[j] <= key -> in place
+ mov rdx, r9
+ inc rdx ; j + 1
+ mov [starts + rdx*8], rax ; shift start
+ mov rcx, [ends + r9*8]
+ mov [ends + rdx*8], rcx ; shift end
+ dec r9
+ jmp .sort_j
+.sort_place:
+ mov rdx, r9
+ inc rdx
+ mov [starts + rdx*8], r10
+ mov [ends + rdx*8], r11
+ inc r8
+ jmp .sort_i
+.sorted:
+
+ cmp qword [n_ranges], 0
+ je .empty
+
+ mov r10, [starts] ; cur_lo
+ mov r11, [ends] ; cur_hi
+ mov r8, 1 ; i
+ xor r12, r12 ; part2 accumulator
+ xor r13, r13 ; write index
+.merge_loop:
+ cmp r8, [n_ranges]
+ jge .flush
+ mov rax, [starts + r8*8] ; s
+ mov rcx, [ends + r8*8] ; e
+ cmp rax, r11
+ jg .gap ; s > cur_hi -> disjoint
+ cmp rcx, r11 ; overlap: cur_hi = max(cur_hi, e)
+ jle .no_extend
+ mov r11, rcx
+.no_extend:
+ inc r8
+ jmp .merge_loop
+.gap:
+ call .emit
+ mov r10, rax ; begin new interval
+ mov r11, rcx
+ inc r8
+ jmp .merge_loop
+.flush:
+ call .emit
+ mov [n_merged], r13
+ mov [part2], r12
+ ret
+.empty:
+ mov qword [n_merged], 0
+ mov qword [part2], 0
+ ret
+
+; local helper: emit [r10, r11] as merged interval r13, add its size to r12
+.emit:
+ mov [starts + r13*8], r10
+ mov [ends + r13*8], r11
+ inc r13
+ mov rdx, r11
+ sub rdx, r10
+ inc rdx ; end - start + 1
+ add r12, rdx
+ ret
+
+print_newline:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, newline
+ mov rdx, 1
+ syscall
+ ret
+
+print_int64:
+ mov rdi, number_buffer+31
+ mov byte [rdi], 0
+ dec rdi
+
+ cmp rax, 0
+ jne .conv
+
+ mov byte [rdi], '0'
+ jmp .done
+.conv:
+ mov rbx, 10
+.next:
+ xor rdx, rdx
+ div rbx
+ add dl, '0'
+ mov [rdi], dl
+ dec rdi
+ test rax, rax
+ jnz .next
+
+ inc rdi
+.done:
+ mov rax, SYS_WRITE
+ mov rsi, rdi
+ mov rdi, STDOUT
+ mov rdx, number_buffer+31
+ sub rdx, rsi
+ syscall
+ ret
+
+print_string:
+ push rcx
+ push rdx
+ mov rdx, 0
+.length:
+ cmp byte [rcx+rdx], 0
+ je .submit
+ inc rdx
+ jmp .length
+.submit:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, rcx
+ syscall
+ pop rdx
+ pop rcx
+ ret
diff --git a/aoc-2025/day6.asm b/aoc-2025/day6.asm
new file mode 100644
index 0000000..774ca4c
--- /dev/null
+++ b/aoc-2025/day6.asm
@@ -0,0 +1,419 @@
+; Advent of Code 2025 - Day 6
+
+bits 64
+default rel
+
+global _start
+
+%define SYS_EXIT 60
+%define SYS_READ 0
+%define SYS_WRITE 1
+%define SYS_OPEN 2
+%define SYS_CLOSE 3
+
+%define O_RDONLY 0
+%define STDOUT 1
+
+%define RAW_SIZE 1048576
+%define MAX_W 8192
+%define MAX_H 64
+%define GRID_SIZE (MAX_W * MAX_H)
+
+section .data
+ input_path db "input/day6.txt", 0
+
+ newline db 10
+
+ label1 db "Part 1: ", 0
+ label2 db "Part 2: ", 0
+
+section .bss
+ raw resb RAW_SIZE
+ grid resb GRID_SIZE ; height x width, space-filled
+
+ raw_len resq 1
+ width resq 1
+ height resq 1
+
+ blk_c0 resq 1 ; current block: columns [blk_c0, blk_c1)
+ blk_c1 resq 1
+ op resq 1 ; 0 = add, 1 = mul, -1 = none
+
+ part1 resq 1
+ part2 resq 1
+
+ number_buffer resb 32
+
+section .text
+
+_start:
+ mov rax, SYS_OPEN
+ mov rdi, input_path
+ mov rsi, O_RDONLY
+ xor rdx, rdx
+ syscall
+ mov r12, rax
+
+ xor r13, r13
+read_loop:
+ mov rax, SYS_READ
+ mov rdi, r12
+ lea rsi, [raw + r13]
+ mov rdx, RAW_SIZE
+ sub rdx, r13
+ syscall
+ cmp rax, 0
+ jle read_done
+ add r13, rax
+ jmp read_loop
+read_done:
+ mov [raw_len], r13
+
+ mov rax, SYS_CLOSE
+ mov rdi, r12
+ syscall
+
+ ; width = length of the first line
+ xor rcx, rcx
+width_scan:
+ cmp rcx, [raw_len]
+ jge width_done
+ mov al, [raw + rcx]
+ cmp al, 10
+ je width_done
+ cmp al, 13
+ je width_done
+ inc rcx
+ jmp width_scan
+width_done:
+ mov [width], rcx
+
+ ; height = number of lines (newlines, plus one if the last line has no newline)
+ xor rcx, rcx
+ xor r8, r8
+height_scan:
+ cmp rcx, [raw_len]
+ jge height_tail
+ mov al, [raw + rcx]
+ cmp al, 10
+ jne height_next
+ inc r8
+height_next:
+ inc rcx
+ jmp height_scan
+height_tail:
+ mov rax, [raw_len]
+ test rax, rax
+ jz height_store
+ mov dl, [raw + rax - 1]
+ cmp dl, 10
+ je height_store
+ inc r8
+height_store:
+ mov [height], r8
+
+ ; space-fill the grid, then copy the raw bytes into it
+ mov rax, [height]
+ imul rax, [width]
+ mov rcx, rax
+ lea rdi, [grid]
+ mov al, ' '
+ rep stosb
+
+ xor r8, r8 ; raw index
+ xor r9, r9 ; row
+ xor r10, r10 ; col
+fill_loop:
+ cmp r8, [raw_len]
+ jge fill_done
+ mov al, [raw + r8]
+ inc r8
+ cmp al, 10
+ je fill_newline
+ cmp al, 13
+ je fill_loop
+ cmp r10, [width]
+ jge fill_loop
+ mov rdx, r9
+ imul rdx, [width]
+ add rdx, r10
+ mov [grid + rdx], al
+ inc r10
+ jmp fill_loop
+fill_newline:
+ inc r9
+ xor r10, r10
+ jmp fill_loop
+fill_done:
+
+ ; walk the columns, carving out blocks between all-space separators
+ xor r15, r15 ; column cursor
+block_scan:
+ cmp r15, [width]
+ jge blocks_done
+ mov rdi, r15
+ call col_is_sep
+ test rax, rax
+ jnz block_sep
+ mov [blk_c0], r15
+block_extend:
+ cmp r15, [width]
+ jge block_end
+ mov rdi, r15
+ call col_is_sep
+ test rax, rax
+ jnz block_end
+ inc r15
+ jmp block_extend
+block_end:
+ mov [blk_c1], r15
+ call process_block
+ jmp block_scan
+block_sep:
+ inc r15
+ jmp block_scan
+blocks_done:
+
+print_results:
+ mov rcx, label1
+ call print_string
+ mov rax, [part1]
+ call print_int64
+ call print_newline
+
+ mov rcx, label2
+ call print_string
+ mov rax, [part2]
+ call print_int64
+ call print_newline
+
+ mov rax, SYS_EXIT
+ xor rdi, rdi
+ syscall
+
+; col_is_sep: is every row of column RDI a space?
+; Output: RAX = 1 if the column is a separator, else 0
+; Clobbers: RAX, RDX, R8
+col_is_sep:
+ xor r8, r8
+.loop:
+ cmp r8, [height]
+ jge .sep
+ mov rax, r8
+ imul rax, [width]
+ add rax, rdi
+ movzx edx, byte [grid + rax]
+ cmp dl, ' '
+ jne .not
+ inc r8
+ jmp .loop
+.sep:
+ mov rax, 1
+ ret
+.not:
+ xor rax, rax
+ ret
+
+; process_block: fold the block [blk_c0, blk_c1) for both parts.
+; Part 1 reads each row as a number; part 2 reads each column as a number.
+; The bottom row supplies the operator. Clobbers RAX, RCX, RDX, R8-R14.
+process_block:
+ ; find the operator on the bottom row within the block
+ mov rax, [height]
+ dec rax
+ imul rax, [width]
+ mov r11, rax ; offset of the operator row
+ mov qword [op], -1
+ mov rcx, [blk_c0]
+.find_op:
+ cmp rcx, [blk_c1]
+ jge .op_done
+ mov rdx, r11
+ add rdx, rcx
+ mov al, [grid + rdx]
+ cmp al, '+'
+ je .set_add
+ cmp al, '*'
+ je .set_mul
+ inc rcx
+ jmp .find_op
+.set_add:
+ mov qword [op], 0
+ jmp .op_done
+.set_mul:
+ mov qword [op], 1
+.op_done:
+ cmp qword [op], 0
+ jl .ret ; no operator -> not a real problem
+
+ ; part 1: one number per row
+ mov r12, [op]
+ xor r13, r13 ; accumulator
+ test r12, r12
+ jz .p1_acc
+ mov r13, 1
+.p1_acc:
+ xor r14, r14 ; count of numbers folded
+ xor r10, r10 ; row
+.p1_rows:
+ mov rax, [height]
+ dec rax
+ cmp r10, rax
+ jge .p1_done
+ mov rax, r10
+ imul rax, [width]
+ mov r11, rax ; row offset
+ xor r8, r8 ; number
+ xor r9, r9 ; saw a digit?
+ mov rcx, [blk_c0]
+.p1_cols:
+ cmp rcx, [blk_c1]
+ jge .p1_fold
+ mov rdx, r11
+ add rdx, rcx
+ movzx eax, byte [grid + rdx]
+ cmp al, '0'
+ jl .p1_col_next
+ cmp al, '9'
+ jg .p1_col_next
+ sub al, '0'
+ movzx rax, al
+ imul r8, r8, 10
+ add r8, rax
+ mov r9, 1
+.p1_col_next:
+ inc rcx
+ jmp .p1_cols
+.p1_fold:
+ test r9, r9
+ jz .p1_row_next
+ test r12, r12
+ jnz .p1_mul
+ add r13, r8
+ jmp .p1_counted
+.p1_mul:
+ imul r13, r8
+.p1_counted:
+ inc r14
+.p1_row_next:
+ inc r10
+ jmp .p1_rows
+.p1_done:
+ test r14, r14
+ jz .p2
+ add [part1], r13
+
+ ; part 2: one number per column
+.p2:
+ mov r12, [op]
+ xor r13, r13
+ test r12, r12
+ jz .p2_acc
+ mov r13, 1
+.p2_acc:
+ xor r14, r14
+ mov rcx, [blk_c0] ; column
+.p2_cols:
+ cmp rcx, [blk_c1]
+ jge .p2_done
+ xor r8, r8 ; number
+ xor r9, r9 ; saw a digit?
+ xor r10, r10 ; row
+.p2_rows:
+ mov rax, [height]
+ dec rax
+ cmp r10, rax
+ jge .p2_fold
+ mov rax, r10
+ imul rax, [width]
+ add rax, rcx
+ movzx edx, byte [grid + rax]
+ cmp dl, '0'
+ jl .p2_row_next
+ cmp dl, '9'
+ jg .p2_row_next
+ sub dl, '0'
+ movzx rdx, dl
+ imul r8, r8, 10
+ add r8, rdx
+ mov r9, 1
+.p2_row_next:
+ inc r10
+ jmp .p2_rows
+.p2_fold:
+ test r9, r9
+ jz .p2_col_next
+ test r12, r12
+ jnz .p2_mul
+ add r13, r8
+ jmp .p2_counted
+.p2_mul:
+ imul r13, r8
+.p2_counted:
+ inc r14
+.p2_col_next:
+ inc rcx
+ jmp .p2_cols
+.p2_done:
+ test r14, r14
+ jz .ret
+ add [part2], r13
+.ret:
+ ret
+
+print_newline:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, newline
+ mov rdx, 1
+ syscall
+ ret
+
+print_int64:
+ mov rdi, number_buffer+31
+ mov byte [rdi], 0
+ dec rdi
+
+ cmp rax, 0
+ jne .conv
+
+ mov byte [rdi], '0'
+ jmp .done
+.conv:
+ mov rbx, 10
+.next:
+ xor rdx, rdx
+ div rbx
+ add dl, '0'
+ mov [rdi], dl
+ dec rdi
+ test rax, rax
+ jnz .next
+
+ inc rdi
+.done:
+ mov rax, SYS_WRITE
+ mov rsi, rdi
+ mov rdi, STDOUT
+ mov rdx, number_buffer+31
+ sub rdx, rsi
+ syscall
+ ret
+
+print_string:
+ push rcx
+ push rdx
+ mov rdx, 0
+.length:
+ cmp byte [rcx+rdx], 0
+ je .submit
+ inc rdx
+ jmp .length
+.submit:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, rcx
+ syscall
+ pop rdx
+ pop rcx
+ ret
diff --git a/aoc-2025/day7.asm b/aoc-2025/day7.asm
new file mode 100644
index 0000000..40d700d
--- /dev/null
+++ b/aoc-2025/day7.asm
@@ -0,0 +1,390 @@
+; Advent of Code 2025 - Day 7
+
+bits 64
+default rel
+
+global _start
+
+%define SYS_EXIT 60
+%define SYS_READ 0
+%define SYS_WRITE 1
+%define SYS_OPEN 2
+%define SYS_CLOSE 3
+
+%define O_RDONLY 0
+%define STDOUT 1
+
+%define RAW_SIZE 1048576
+%define MAX_W 1024
+%define MAX_H 1024
+%define GRID_SIZE (MAX_W * MAX_H)
+
+section .data
+ input_path db "input/day7.txt", 0
+
+ newline db 10
+
+ label1 db "Part 1: ", 0
+ label2 db "Part 2: ", 0
+
+section .bss
+ raw resb RAW_SIZE
+ grid resb GRID_SIZE ; height x width, '.'-filled
+ g resq (MAX_W * MAX_H) ; part 2 timeline counts
+ cur resb MAX_W ; part 1 active columns (this row)
+ nxt resb MAX_W ; part 1 active columns (next row)
+
+ raw_len resq 1
+ width resq 1
+ height resq 1
+ srow resq 1
+ scol resq 1
+
+ part1 resq 1
+ part2 resq 1
+
+ number_buffer resb 32
+
+section .text
+
+_start:
+ mov rax, SYS_OPEN
+ mov rdi, input_path
+ mov rsi, O_RDONLY
+ xor rdx, rdx
+ syscall
+ mov r12, rax
+
+ xor r13, r13
+read_loop:
+ mov rax, SYS_READ
+ mov rdi, r12
+ lea rsi, [raw + r13]
+ mov rdx, RAW_SIZE
+ sub rdx, r13
+ syscall
+ cmp rax, 0
+ jle read_done
+ add r13, rax
+ jmp read_loop
+read_done:
+ mov [raw_len], r13
+
+ mov rax, SYS_CLOSE
+ mov rdi, r12
+ syscall
+
+ ; width = length of the first line
+ xor rcx, rcx
+width_scan:
+ cmp rcx, [raw_len]
+ jge width_done
+ mov al, [raw + rcx]
+ cmp al, 10
+ je width_done
+ cmp al, 13
+ je width_done
+ inc rcx
+ jmp width_scan
+width_done:
+ mov [width], rcx
+
+ ; height = number of lines
+ xor rcx, rcx
+ xor r8, r8
+height_scan:
+ cmp rcx, [raw_len]
+ jge height_tail
+ mov al, [raw + rcx]
+ cmp al, 10
+ jne height_next
+ inc r8
+height_next:
+ inc rcx
+ jmp height_scan
+height_tail:
+ mov rax, [raw_len]
+ test rax, rax
+ jz height_store
+ mov dl, [raw + rax - 1]
+ cmp dl, 10
+ je height_store
+ inc r8
+height_store:
+ mov [height], r8
+
+ ; '.'-fill the grid, then copy the raw bytes into it
+ mov rax, [height]
+ imul rax, [width]
+ mov rcx, rax
+ lea rdi, [grid]
+ mov al, '.'
+ rep stosb
+
+ xor r8, r8 ; raw index
+ xor r9, r9 ; row
+ xor r10, r10 ; col
+fill_loop:
+ cmp r8, [raw_len]
+ jge fill_done
+ mov al, [raw + r8]
+ inc r8
+ cmp al, 10
+ je fill_newline
+ cmp al, 13
+ je fill_loop
+ cmp r10, [width]
+ jge fill_loop
+ mov rdx, r9
+ imul rdx, [width]
+ add rdx, r10
+ mov [grid + rdx], al
+ inc r10
+ jmp fill_loop
+fill_newline:
+ inc r9
+ xor r10, r10
+ jmp fill_loop
+fill_done:
+
+ ; locate S
+ xor r8, r8
+ mov rax, [height]
+ imul rax, [width]
+ mov r9, rax
+find_s:
+ cmp r8, r9
+ jge s_done
+ mov al, [grid + r8]
+ cmp al, 'S'
+ je s_found
+ inc r8
+ jmp find_s
+s_found:
+ mov rax, r8
+ xor rdx, rdx
+ div qword [width]
+ mov [srow], rax
+ mov [scol], rdx
+s_done:
+
+ ; part 1: flood a set of active columns, counting splitter hits
+ lea rdi, [cur]
+ mov rcx, MAX_W
+ xor al, al
+ rep stosb
+ mov rax, [scol]
+ mov byte [cur + rax], 1
+
+ xor r15, r15 ; row
+p1_row:
+ cmp r15, [height]
+ jge p1_done
+ lea rdi, [nxt]
+ mov rcx, MAX_W
+ xor al, al
+ rep stosb
+
+ mov rax, r15
+ imul rax, [width]
+ mov r14, rax ; row offset
+ xor r13, r13 ; col
+ xor r12, r12 ; any beam alive?
+p1_col:
+ cmp r13, [width]
+ jge p1_col_done
+ mov al, [cur + r13]
+ test al, al
+ jz p1_next
+ mov rdx, r14
+ add rdx, r13
+ mov al, [grid + rdx]
+ cmp al, '^'
+ jne p1_pass
+ inc qword [part1]
+ mov rbx, r13 ; left neighbor
+ test rbx, rbx
+ jz p1_no_left
+ dec rbx
+ mov byte [nxt + rbx], 1
+ mov r12, 1
+p1_no_left:
+ mov rbx, r13 ; right neighbor
+ inc rbx
+ cmp rbx, [width]
+ jge p1_no_right
+ mov byte [nxt + rbx], 1
+ mov r12, 1
+p1_no_right:
+ jmp p1_next
+p1_pass:
+ mov byte [nxt + r13], 1
+ mov r12, 1
+p1_next:
+ inc r13
+ jmp p1_col
+p1_col_done:
+ lea rsi, [nxt]
+ lea rdi, [cur]
+ mov rcx, MAX_W
+ rep movsb
+ test r12, r12
+ jz p1_done
+ inc r15
+ jmp p1_row
+p1_done:
+
+ ; part 2: bottom-up DP of timeline counts, g[r][c]
+ mov r15, [height]
+ dec r15 ; row, counting down
+p2_row:
+ cmp r15, 0
+ jl p2_done
+ mov rax, r15
+ imul rax, [width]
+ mov r14, rax ; row offset
+ xor r13, r13 ; col
+p2_col:
+ cmp r13, [width]
+ jge p2_col_done
+ mov rdx, r14
+ add rdx, r13 ; idx = r*W + c
+ mov al, [grid + rdx]
+ cmp al, '^'
+ je p2_split
+
+ ; pass straight down: g = (r+1 in grid ? g[r+1][c] : 1)
+ mov rbx, r15
+ inc rbx
+ cmp rbx, [height]
+ jge p2_pass_exit
+ mov rcx, rdx
+ add rcx, [width]
+ mov rax, [g + rcx*8]
+ jmp p2_store
+p2_pass_exit:
+ mov rax, 1
+ jmp p2_store
+
+p2_split:
+ ; left child enters (r+1, c-1)
+ mov rbx, r15
+ inc rbx
+ cmp rbx, [height]
+ jge p2_left_one
+ cmp r13, 0
+ jle p2_left_one
+ mov rcx, rdx
+ add rcx, [width]
+ dec rcx
+ mov r8, [g + rcx*8]
+ jmp p2_left_done
+p2_left_one:
+ mov r8, 1
+p2_left_done:
+ ; right child enters (r+1, c+1)
+ mov rbx, r15
+ inc rbx
+ cmp rbx, [height]
+ jge p2_right_one
+ mov rbx, r13
+ inc rbx
+ cmp rbx, [width]
+ jge p2_right_one
+ mov rcx, rdx
+ add rcx, [width]
+ inc rcx
+ mov r9, [g + rcx*8]
+ jmp p2_right_done
+p2_right_one:
+ mov r9, 1
+p2_right_done:
+ mov rax, r8
+ add rax, r9
+p2_store:
+ mov [g + rdx*8], rax
+ inc r13
+ jmp p2_col
+p2_col_done:
+ dec r15
+ jmp p2_row
+p2_done:
+ mov rax, [srow]
+ imul rax, [width]
+ add rax, [scol]
+ mov rax, [g + rax*8]
+ mov [part2], rax
+
+print_results:
+ mov rcx, label1
+ call print_string
+ mov rax, [part1]
+ call print_int64
+ call print_newline
+
+ mov rcx, label2
+ call print_string
+ mov rax, [part2]
+ call print_int64
+ call print_newline
+
+ mov rax, SYS_EXIT
+ xor rdi, rdi
+ syscall
+
+print_newline:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, newline
+ mov rdx, 1
+ syscall
+ ret
+
+print_int64:
+ mov rdi, number_buffer+31
+ mov byte [rdi], 0
+ dec rdi
+
+ cmp rax, 0
+ jne .conv
+
+ mov byte [rdi], '0'
+ jmp .done
+.conv:
+ mov rbx, 10
+.next:
+ xor rdx, rdx
+ div rbx
+ add dl, '0'
+ mov [rdi], dl
+ dec rdi
+ test rax, rax
+ jnz .next
+
+ inc rdi
+.done:
+ mov rax, SYS_WRITE
+ mov rsi, rdi
+ mov rdi, STDOUT
+ mov rdx, number_buffer+31
+ sub rdx, rsi
+ syscall
+ ret
+
+print_string:
+ push rcx
+ push rdx
+ mov rdx, 0
+.length:
+ cmp byte [rcx+rdx], 0
+ je .submit
+ inc rdx
+ jmp .length
+.submit:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, rcx
+ syscall
+ pop rdx
+ pop rcx
+ ret
diff --git a/aoc-2025/day8.asm b/aoc-2025/day8.asm
new file mode 100644
index 0000000..407d0e4
--- /dev/null
+++ b/aoc-2025/day8.asm
@@ -0,0 +1,431 @@
+; Advent of Code 2025 - Day 8
+
+bits 64
+default rel
+
+global _start
+
+%define SYS_EXIT 60
+%define SYS_READ 0
+%define SYS_WRITE 1
+%define SYS_OPEN 2
+%define SYS_CLOSE 3
+
+%define O_RDONLY 0
+%define STDOUT 1
+
+%define RAW_SIZE 262144
+%define MAXN 1024
+%define MAXE (MAXN * (MAXN - 1) / 2)
+%define K_LIMIT 1000 ; part 1 connects the K_LIMIT closest pairs
+
+section .data
+ input_path db "input/day8.txt", 0
+
+ newline db 10
+
+ label1 db "Part 1: ", 0
+ label2 db "Part 2: ", 0
+
+section .bss
+ raw resb RAW_SIZE
+ x resq MAXN
+ y resq MAXN
+ z resq MAXN
+
+ edges resb (MAXE * 16) ; records: [dist2:8][i:4][j:4]
+ edges2 resb (MAXE * 16) ; radix ping-pong buffer
+ counts resq 256
+
+ parent resq MAXN
+ csize resq MAXN
+
+ raw_len resq 1
+ n_boxes resq 1
+ m_edges resq 1
+ sorted_ptr resq 1
+ comp_count resq 1
+
+ part1 resq 1
+ part2 resq 1
+
+ number_buffer resb 32
+
+section .text
+
+_start:
+ mov rax, SYS_OPEN
+ mov rdi, input_path
+ mov rsi, O_RDONLY
+ xor rdx, rdx
+ syscall
+ mov r12, rax
+
+ xor r13, r13
+read_loop:
+ mov rax, SYS_READ
+ mov rdi, r12
+ lea rsi, [raw + r13]
+ mov rdx, RAW_SIZE
+ sub rdx, r13
+ syscall
+ cmp rax, 0
+ jle read_done
+ add r13, rax
+ jmp read_loop
+read_done:
+ mov [raw_len], r13
+
+ mov rax, SYS_CLOSE
+ mov rdi, r12
+ syscall
+
+ ; parse "X,Y,Z" lines into x/y/z, counting boxes
+ lea rsi, [raw]
+ mov rax, [raw_len]
+ lea r14, [raw + rax] ; end pointer
+ xor r12, r12 ; box index
+parse_line:
+ cmp rsi, r14
+ jae parse_done
+ mov al, [rsi]
+ cmp al, 10
+ je parse_skipnl
+ cmp al, 13
+ je parse_skipnl
+ call parse_u64
+ mov [x + r12*8], rax
+ inc rsi
+ call parse_u64
+ mov [y + r12*8], rax
+ inc rsi
+ call parse_u64
+ mov [z + r12*8], rax
+ inc r12
+parse_eol:
+ cmp rsi, r14
+ jae parse_done
+ mov al, [rsi]
+ inc rsi
+ cmp al, 10
+ jne parse_eol
+ jmp parse_line
+parse_skipnl:
+ inc rsi
+ jmp parse_line
+parse_done:
+ mov [n_boxes], r12
+
+ ; build every pairwise edge with squared distance as the key
+ lea r11, [edges] ; write pointer
+ xor r12, r12 ; edge count
+ xor r8, r8 ; i
+gen_i:
+ cmp r8, [n_boxes]
+ jge gen_done
+ mov r13, [x + r8*8]
+ mov r14, [y + r8*8]
+ mov r15, [z + r8*8]
+ lea r9, [r8 + 1] ; j = i + 1
+gen_j:
+ cmp r9, [n_boxes]
+ jge gen_j_done
+ mov rax, r13
+ sub rax, [x + r9*8]
+ imul rax, rax
+ mov rcx, rax
+ mov rax, r14
+ sub rax, [y + r9*8]
+ imul rax, rax
+ add rcx, rax
+ mov rax, r15
+ sub rax, [z + r9*8]
+ imul rax, rax
+ add rcx, rax ; key = dx^2 + dy^2 + dz^2
+ mov [r11], rcx
+ mov [r11 + 8], r8d
+ mov [r11 + 12], r9d
+ add r11, 16
+ inc r12
+ inc r9
+ jmp gen_j
+gen_j_done:
+ inc r8
+ jmp gen_i
+gen_done:
+ mov [m_edges], r12
+
+ ; LSD radix sort by the 5 low bytes of the key (max key < 2^40)
+ lea r11, [edges] ; src
+ lea r12, [edges2] ; dst
+ xor r13, r13 ; byte pass 0..4
+rsort_pass:
+ cmp r13, 5
+ jge rsort_done
+ lea rdi, [counts]
+ mov rcx, 256
+ xor rax, rax
+ rep stosq
+
+ xor rcx, rcx ; k
+rs_hist:
+ cmp rcx, [m_edges]
+ jge rs_hist_done
+ mov rax, rcx
+ shl rax, 4
+ add rax, r11
+ movzx edx, byte [rax + r13]
+ inc qword [counts + rdx*8]
+ inc rcx
+ jmp rs_hist
+rs_hist_done:
+ xor rcx, rcx ; t
+ xor r8, r8 ; running offset
+rs_prefix:
+ cmp rcx, 256
+ jge rs_prefix_done
+ mov rax, [counts + rcx*8]
+ mov [counts + rcx*8], r8
+ add r8, rax
+ inc rcx
+ jmp rs_prefix
+rs_prefix_done:
+ xor rcx, rcx ; k
+rs_scatter:
+ cmp rcx, [m_edges]
+ jge rs_scatter_done
+ mov rax, rcx
+ shl rax, 4
+ add rax, r11 ; src record
+ movzx edx, byte [rax + r13]
+ mov r9, [counts + rdx*8]
+ inc qword [counts + rdx*8]
+ mov r10, r9
+ shl r10, 4
+ add r10, r12 ; dst slot
+ mov r8, [rax]
+ mov [r10], r8
+ mov r8, [rax + 8]
+ mov [r10 + 8], r8
+ inc rcx
+ jmp rs_scatter
+rs_scatter_done:
+ xchg r11, r12
+ inc r13
+ jmp rsort_pass
+rsort_done:
+ mov [sorted_ptr], r11
+
+ ; union-find init
+ xor rcx, rcx
+dsu_init:
+ cmp rcx, [n_boxes]
+ jge dsu_done
+ mov [parent + rcx*8], rcx
+ mov qword [csize + rcx*8], 1
+ inc rcx
+ jmp dsu_init
+dsu_done:
+ mov rax, [n_boxes]
+ mov [comp_count], rax
+ mov qword [part2], 0
+
+ ; Kruskal: one sorted pass drives both parts
+ mov r11, [sorted_ptr]
+ xor r15, r15 ; edge index
+kloop:
+ cmp r15, [m_edges]
+ jge kdone
+ mov r12, r15
+ shl r12, 4
+ add r12, r11 ; record pointer
+ mov r13d, [r12 + 8] ; i
+ mov r14d, [r12 + 12] ; j
+
+ mov edi, r13d
+ call find
+ mov rbx, rax ; root of i
+ mov edi, r14d
+ call find ; root of j
+
+ cmp rbx, rax
+ je k_after ; already connected
+
+ mov rcx, [csize + rbx*8]
+ mov rdx, [csize + rax*8]
+ cmp rcx, rdx
+ jge k_no_swap
+ xchg rbx, rax ; keep rbx as the larger root
+k_no_swap:
+ mov [parent + rax*8], rbx
+ mov rcx, [csize + rax*8]
+ add [csize + rbx*8], rcx
+ dec qword [comp_count]
+
+ cmp qword [comp_count], 1
+ jne k_after
+ cmp qword [part2], 0
+ jne k_after
+ mov rax, [x + r13*8]
+ imul rax, [x + r14*8]
+ mov [part2], rax
+k_after:
+ cmp r15, (K_LIMIT - 1)
+ jne k_check_break
+ call compute_part1
+k_check_break:
+ cmp r15, (K_LIMIT - 1)
+ jl k_next
+ cmp qword [part2], 0
+ je k_next
+ jmp kdone
+k_next:
+ inc r15
+ jmp kloop
+kdone:
+
+print_results:
+ mov rcx, label1
+ call print_string
+ mov rax, [part1]
+ call print_int64
+ call print_newline
+
+ mov rcx, label2
+ call print_string
+ mov rax, [part2]
+ call print_int64
+ call print_newline
+
+ mov rax, SYS_EXIT
+ xor rdi, rdi
+ syscall
+
+; find: union-find root of node RDI, with path halving.
+; Output: RAX = root. Clobbers RAX, RDX, RDI.
+find:
+.loop:
+ mov rax, [parent + rdi*8]
+ cmp rax, rdi
+ je .done
+ mov rdx, [parent + rax*8]
+ mov [parent + rdi*8], rdx
+ mov rdi, rdx
+ jmp .loop
+.done:
+ ret
+
+; compute_part1: product of the three largest component sizes into part1.
+; Clobbers RAX, RCX, R8, R9, R10.
+compute_part1:
+ xor r8, r8 ; largest
+ xor r9, r9 ; 2nd
+ xor r10, r10 ; 3rd
+ xor rcx, rcx
+.loop:
+ cmp rcx, [n_boxes]
+ jge .done
+ mov rax, [parent + rcx*8]
+ cmp rax, rcx
+ jne .next ; not a root
+ mov rax, [csize + rcx*8]
+ cmp rax, r8
+ jle .try2
+ mov r10, r9
+ mov r9, r8
+ mov r8, rax
+ jmp .next
+.try2:
+ cmp rax, r9
+ jle .try3
+ mov r10, r9
+ mov r9, rax
+ jmp .next
+.try3:
+ cmp rax, r10
+ jle .next
+ mov r10, rax
+.next:
+ inc rcx
+ jmp .loop
+.done:
+ mov rax, r8
+ imul rax, r9
+ imul rax, r10
+ mov [part1], rax
+ ret
+
+; parse_u64: read decimal digits at [RSI], advancing RSI. Clobbers RAX, RDX.
+parse_u64:
+ xor rax, rax
+.digit:
+ mov dl, [rsi]
+ cmp dl, '0'
+ jl .done
+ cmp dl, '9'
+ jg .done
+ sub dl, '0'
+ movzx rdx, dl
+ imul rax, rax, 10
+ add rax, rdx
+ inc rsi
+ jmp .digit
+.done:
+ ret
+
+print_newline:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, newline
+ mov rdx, 1
+ syscall
+ ret
+
+print_int64:
+ mov rdi, number_buffer+31
+ mov byte [rdi], 0
+ dec rdi
+
+ cmp rax, 0
+ jne .conv
+
+ mov byte [rdi], '0'
+ jmp .done
+.conv:
+ mov rbx, 10
+.next:
+ xor rdx, rdx
+ div rbx
+ add dl, '0'
+ mov [rdi], dl
+ dec rdi
+ test rax, rax
+ jnz .next
+
+ inc rdi
+.done:
+ mov rax, SYS_WRITE
+ mov rsi, rdi
+ mov rdi, STDOUT
+ mov rdx, number_buffer+31
+ sub rdx, rsi
+ syscall
+ ret
+
+print_string:
+ push rcx
+ push rdx
+ mov rdx, 0
+.length:
+ cmp byte [rcx+rdx], 0
+ je .submit
+ inc rdx
+ jmp .length
+.submit:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, rcx
+ syscall
+ pop rdx
+ pop rcx
+ ret