hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/aoc-2025
diff options
context:
space:
mode:
Diffstat (limited to 'aoc-2025')
-rw-r--r--aoc-2025/day1.asm228
-rw-r--r--aoc-2025/day10.asm695
-rw-r--r--aoc-2025/day11.asm233
-rw-r--r--aoc-2025/day12.asm486
-rw-r--r--aoc-2025/day2.asm324
-rw-r--r--aoc-2025/day3.asm234
-rw-r--r--aoc-2025/day4.asm236
-rw-r--r--aoc-2025/day5.asm225
-rw-r--r--aoc-2025/day6.asm331
-rw-r--r--aoc-2025/day7.asm302
-rw-r--r--aoc-2025/day8.asm326
-rw-r--r--aoc-2025/day9.asm540
-rw-r--r--aoc-2025/utils/io.asm107
-rw-r--r--aoc-2025/utils/parse.asm22
14 files changed, 0 insertions, 4289 deletions
diff --git a/aoc-2025/day1.asm b/aoc-2025/day1.asm
deleted file mode 100644
index 928e7a5..0000000
--- a/aoc-2025/day1.asm
+++ /dev/null
@@ -1,228 +0,0 @@
-; 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
diff --git a/aoc-2025/day10.asm b/aoc-2025/day10.asm
deleted file mode 100644
index 456752c..0000000
--- a/aoc-2025/day10.asm
+++ /dev/null
@@ -1,695 +0,0 @@
-; Advent of Code 2025 - Day 10
-
-bits 64
-default rel
-
-global _start
-
-%include "utils/io.asm"
-
-%define RAW_SIZE 262144
-%define MAXB 16
-%define MAXL 12
-%define MNCOLS (MAXB + 1) ; matrix columns: buttons + rhs
-%define CPFSTRIDE 8 ; free-coeff stride (free vars <= 3)
-
-section .data
- input_path db "input/day10.txt", 0
-
- label1 db "Part 1: ", 0
- label2 db "Part 2: ", 0
-
-section .bss
- raw resb RAW_SIZE
-
- bmask resq MAXB ; part 1: button light masks
- btn_ctrs resq (MAXB * MAXL) ; part 2: counters each button hits
- btn_len resq MAXB
- jolt resq MAXL ; part 2: target counter values
-
- Mnum resq (MAXL * MNCOLS) ; augmented matrix numerators
- rowden resq MAXL ; per-row common denominators
- pivcol resq MAXL
- is_piv resb MAXB
- free_cols resq MAXB
- ub_free resq MAXB
- x_free resq MAXB
- RP resq MAXL ; scaled rhs per pivot row
- CPF resq (MAXL * CPFSTRIDE); scaled free coeffs per pivot row
-
- raw_len resq 1
- cur_t resq 1
- nb resq 1
- nc resq 1
- npiv resq 1
- nfree resq 1
- Dval resq 1
- best resq 1
- total1 resq 1
- total2 resq 1
-
-section .text
-
-_start:
- mov rdi, input_path
- lea rsi, [raw]
- mov rdx, RAW_SIZE
- call read_file
- mov [raw_len], rax
-
- lea rsi, [raw]
- mov rax, [raw_len]
- lea r14, [raw + rax]
- mov qword [cur_t], 0
- mov qword [nb], 0
- mov qword [nc], 0
-scan:
- cmp rsi, r14
- jae finish_last
- mov al, [rsi]
- cmp al, '['
- je do_bracket
- cmp al, '('
- je do_button
- cmp al, '{'
- je do_curly
- cmp al, 10
- je end_line
- inc rsi
- jmp scan
-
-do_bracket:
- inc rsi
- xor r10, r10 ; light index
- xor r11, r11 ; target mask
-.loop:
- mov al, [rsi]
- cmp al, ']'
- je .done
- cmp al, '#'
- jne .off
- mov rcx, r10
- mov rdx, 1
- shl rdx, cl
- or r11, rdx
-.off:
- inc r10
- inc rsi
- jmp .loop
-.done:
- inc rsi
- mov [cur_t], r11
- jmp scan
-
-do_button:
- inc rsi
- xor r11, r11 ; light mask
- xor r10, r10 ; current index
- xor r9, r9 ; counter list length
-.loop:
- mov al, [rsi]
- cmp al, ')'
- je .fin
- cmp al, ','
- je .comma
- sub al, '0'
- movzx rax, al
- imul r10, r10, 10
- add r10, rax
- inc rsi
- jmp .loop
-.comma:
- mov rcx, r10
- mov rdx, 1
- shl rdx, cl
- or r11, rdx
- mov rax, [nb]
- imul rax, MAXL
- add rax, r9
- mov [btn_ctrs + rax*8], r10
- inc r9
- xor r10, r10
- inc rsi
- jmp .loop
-.fin:
- mov rcx, r10
- mov rdx, 1
- shl rdx, cl
- or r11, rdx
- mov rax, [nb]
- imul rax, MAXL
- add rax, r9
- mov [btn_ctrs + rax*8], r10
- inc r9
- inc rsi
- mov rax, [nb]
- mov [bmask + rax*8], r11
- mov [btn_len + rax*8], r9
- inc qword [nb]
- jmp scan
-
-do_curly:
- inc rsi
- xor r10, r10 ; current number
-.loop:
- mov al, [rsi]
- cmp al, '}'
- je .fin
- cmp al, ','
- je .comma
- sub al, '0'
- movzx rax, al
- imul r10, r10, 10
- add r10, rax
- inc rsi
- jmp .loop
-.comma:
- mov rax, [nc]
- mov [jolt + rax*8], r10
- inc qword [nc]
- xor r10, r10
- inc rsi
- jmp .loop
-.fin:
- mov rax, [nc]
- mov [jolt + rax*8], r10
- inc qword [nc]
- inc rsi
- jmp scan
-
-end_line:
- inc rsi
- push rsi
- push r14
- call solve1
- call solve2
- pop r14
- pop rsi
- mov qword [cur_t], 0
- mov qword [nb], 0
- mov qword [nc], 0
- jmp scan
-
-finish_last:
- cmp qword [nb], 0
- je all_done
- push rsi
- push r14
- call solve1
- call solve2
- pop r14
- pop rsi
-all_done:
-
-print_results:
- mov rcx, label1
- call print_string
- mov rax, [total1]
- call print_int64
- call print_newline
-
- mov rcx, label2
- call print_string
- mov rax, [total2]
- call print_int64
- call print_newline
-
- mov rax, SYS_EXIT
- xor rdi, rdi
- syscall
-
-; solve1: part 1 - fewest button presses to match the light target (GF(2)).
-solve1:
- mov rcx, [nb]
- mov r8, 1
- shl r8, cl
- mov r9, 0x7fffffff
- xor r10, r10
-.mask:
- cmp r10, r8
- jge .done
- xor r11, r11
- xor rbx, rbx
- xor rcx, rcx
-.bits:
- cmp rcx, [nb]
- jge .check
- mov rdx, r10
- shr rdx, cl
- test rdx, 1
- jz .bit_next
- xor r11, [bmask + rcx*8]
- inc rbx
-.bit_next:
- inc rcx
- jmp .bits
-.check:
- cmp r11, [cur_t]
- jne .next
- cmp rbx, r9
- jge .next
- mov r9, rbx
-.next:
- inc r10
- jmp .mask
-.done:
- cmp r9, 0x7fffffff
- je .ret
- add [total1], r9
-.ret:
- ret
-
-; solve2: part 2 - fewest button presses to reach the joltage targets.
-; Builds the incidence matrix, reduces it to RREF over the rationals (per-row
-; denominators), then enumerates the free variables.
-solve2:
- ; clear matrix and set row denominators to 1
- mov rax, [nc]
- imul rax, MNCOLS
- mov rcx, rax
- lea rdi, [Mnum]
- xor rax, rax
- rep stosq
- xor rcx, rcx
-.rd_init:
- cmp rcx, [nc]
- jge .rd_done
- mov qword [rowden + rcx*8], 1
- inc rcx
- jmp .rd_init
-.rd_done:
- ; A[counter][button] = 1
- xor r8, r8
-.fill_b:
- cmp r8, [nb]
- jge .fill_done
- mov r9, [btn_len + r8*8]
- xor rcx, rcx
-.fill_k:
- cmp rcx, r9
- jge .fill_k_done
- mov r10, r8
- imul r10, MAXL
- add r10, rcx
- mov r11, [btn_ctrs + r10*8]
- mov rax, r11
- imul rax, MNCOLS
- add rax, r8
- mov qword [Mnum + rax*8], 1
- inc rcx
- jmp .fill_k
-.fill_k_done:
- inc r8
- jmp .fill_b
-.fill_done:
- ; rhs column = jolt targets
- xor rcx, rcx
-.rhs:
- cmp rcx, [nc]
- jge .rhs_done
- mov rax, rcx
- imul rax, MNCOLS
- add rax, [nb]
- mov rdx, [jolt + rcx*8]
- mov [Mnum + rax*8], rdx
- inc rcx
- jmp .rhs
-.rhs_done:
-
- ; RREF
- lea rdi, [is_piv]
- mov rcx, [nb]
- xor al, al
- rep stosb
- mov qword [npiv], 0
- xor r15, r15 ; column
-.rref_c:
- cmp r15, [nb]
- jge .rref_done
- ; find a pivot row in [npiv, nc)
- mov r12, [npiv]
- mov r13, -1
-.find_pr:
- cmp r12, [nc]
- jge .find_pr_done
- mov rax, r12
- imul rax, MNCOLS
- add rax, r15
- cmp qword [Mnum + rax*8], 0
- je .find_pr_next
- mov r13, r12
- jmp .find_pr_done
-.find_pr_next:
- inc r12
- jmp .find_pr
-.find_pr_done:
- cmp r13, -1
- je .rref_c_next
- mov r12, [npiv]
- cmp r12, r13
- je .noswap
- ; swap rows r12 and r13
- xor rcx, rcx
-.swap_j:
- cmp rcx, [nb]
- jg .swap_done
- mov rax, r12
- imul rax, MNCOLS
- add rax, rcx
- mov rbx, r13
- imul rbx, MNCOLS
- add rbx, rcx
- mov r8, [Mnum + rax*8]
- mov r9, [Mnum + rbx*8]
- mov [Mnum + rax*8], r9
- mov [Mnum + rbx*8], r8
- inc rcx
- jmp .swap_j
-.swap_done:
- mov r8, [rowden + r12*8]
- mov r9, [rowden + r13*8]
- mov [rowden + r12*8], r9
- mov [rowden + r13*8], r8
-.noswap:
- ; normalise pivot row: denominator = pivot numerator
- mov r12, [npiv]
- mov rax, r12
- imul rax, MNCOLS
- add rax, r15
- mov rdx, [Mnum + rax*8]
- mov [rowden + r12*8], rdx
- mov rdi, r12
- call reduce_row
- ; eliminate column from every other row
- xor r13, r13
-.elim_rr:
- cmp r13, [nc]
- jge .elim_done
- cmp r13, r12
- je .elim_next
- mov rax, r13
- imul rax, MNCOLS
- add rax, r15
- mov r8, [Mnum + rax*8] ; fc
- test r8, r8
- jz .elim_next
- mov r9, [rowden + r12*8] ; rd
- xor rcx, rcx
-.elim_j:
- cmp rcx, [nb]
- jg .elim_j_done
- mov rax, r13
- imul rax, MNCOLS
- add rax, rcx
- mov r10, [Mnum + rax*8]
- imul r10, r9
- mov rbx, r12
- imul rbx, MNCOLS
- add rbx, rcx
- mov r11, [Mnum + rbx*8]
- imul r11, r8
- sub r10, r11
- mov [Mnum + rax*8], r10
- inc rcx
- jmp .elim_j
-.elim_j_done:
- mov rax, [rowden + r13*8]
- imul rax, r9
- mov [rowden + r13*8], rax
- mov rdi, r13
- call reduce_row
-.elim_next:
- inc r13
- jmp .elim_rr
-.elim_done:
- mov r12, [npiv]
- mov [pivcol + r12*8], r15
- mov byte [is_piv + r15], 1
- inc qword [npiv]
-.rref_c_next:
- inc r15
- jmp .rref_c
-.rref_done:
-
- ; free columns
- xor r12, r12
- xor r15, r15
-.free_scan:
- cmp r15, [nb]
- jge .free_done
- cmp byte [is_piv + r15], 0
- jne .free_next
- mov [free_cols + r12*8], r15
- inc r12
-.free_next:
- inc r15
- jmp .free_scan
-.free_done:
- mov [nfree], r12
-
- ; D = lcm of the pivot-row denominators
- mov qword [Dval], 1
- xor r13, r13
-.lcm:
- cmp r13, [npiv]
- jge .lcm_done
- mov rdi, [Dval]
- mov rsi, [rowden + r13*8]
- call gcd
- mov rcx, rax
- mov rax, [Dval]
- xor rdx, rdx
- div rcx
- imul rax, [rowden + r13*8]
- mov [Dval], rax
- inc r13
- jmp .lcm
-.lcm_done:
-
- ; scaled rhs and free coefficients per pivot row
- xor r13, r13
-.setup_pr:
- cmp r13, [npiv]
- jge .setup_done
- mov rax, [Dval]
- xor rdx, rdx
- div qword [rowden + r13*8]
- mov r8, rax ; q = D / rowden
- mov rax, r13
- imul rax, MNCOLS
- add rax, [nb]
- mov rcx, [Mnum + rax*8]
- imul rcx, r8
- mov [RP + r13*8], rcx
- xor r9, r9
-.setup_f:
- cmp r9, [nfree]
- jge .setup_f_done
- mov r10, [free_cols + r9*8]
- mov rax, r13
- imul rax, MNCOLS
- add rax, r10
- mov rcx, [Mnum + rax*8]
- imul rcx, r8
- mov rax, r13
- imul rax, CPFSTRIDE
- add rax, r9
- mov [CPF + rax*8], rcx
- inc r9
- jmp .setup_f
-.setup_f_done:
- inc r13
- jmp .setup_pr
-.setup_done:
-
- ; free-variable upper bounds = min covered target
- xor r9, r9
-.ub_loop:
- cmp r9, [nfree]
- jge .ub_done
- mov r10, [free_cols + r9*8]
- mov r11, [btn_len + r10*8]
- mov r12, 0x7fffffff
- xor rcx, rcx
-.ub_k:
- cmp rcx, r11
- jge .ub_k_done
- mov rax, r10
- imul rax, MAXL
- add rax, rcx
- mov rbx, [btn_ctrs + rax*8]
- mov rdx, [jolt + rbx*8]
- cmp rdx, r12
- jge .ub_k_next
- mov r12, rdx
-.ub_k_next:
- inc rcx
- jmp .ub_k
-.ub_k_done:
- mov [ub_free + r9*8], r12
- inc r9
- jmp .ub_loop
-.ub_done:
-
- mov qword [best], 0x3fffffff
- xor rdi, rdi
- xor rsi, rsi
- call enum_free
- mov rax, [best]
- add [total2], rax
- ret
-
-; enum_free: recurse over free variables (RDI=depth, RSI=partial presses),
-; pruning branches that already reach the best. Preserves R12-R15.
-enum_free:
- cmp rsi, [best]
- jae .ret
- cmp rdi, [nfree]
- je .leaf
- push r12
- push r13
- push r14
- push r15
- mov r12, rdi
- mov r13, rsi
- mov r14, [ub_free + r12*8]
- xor r15, r15
-.vloop:
- mov rsi, r13
- add rsi, r15
- cmp rsi, [best]
- jae .vdone
- mov [x_free + r12*8], r15
- lea rdi, [r12 + 1]
- call enum_free
- inc r15
- cmp r15, r14
- jbe .vloop
-.vdone:
- pop r15
- pop r14
- pop r13
- pop r12
- ret
-.leaf:
- call eval_leaf
-.ret:
- ret
-
-; eval_leaf: with all free variables fixed, recover the basic variables and
-; update best. RSI = presses used by free variables. Preserves R12-R15.
-eval_leaf:
- mov r11, rsi ; running total
- xor r8, r8 ; pivot row
-.pr:
- cmp r8, [npiv]
- jge .accept
- mov rax, [RP + r8*8]
- xor r9, r9
-.f:
- cmp r9, [nfree]
- jge .fd
- mov rcx, r8
- imul rcx, CPFSTRIDE
- add rcx, r9
- mov rdx, [CPF + rcx*8]
- imul rdx, [x_free + r9*8]
- sub rax, rdx
- inc r9
- jmp .f
-.fd:
- test rax, rax
- js .ret
- cqo
- idiv qword [Dval]
- test rdx, rdx
- jnz .ret
- add r11, rax
- cmp r11, [best]
- jae .ret
- inc r8
- jmp .pr
-.accept:
- mov [best], r11
-.ret:
- ret
-
-; gcd(RDI, RSI) -> RAX, inputs non-negative. Clobbers RAX, RDX, RDI, RSI.
-gcd:
-.loop:
- test rsi, rsi
- jz .done
- xor rdx, rdx
- mov rax, rdi
- div rsi
- mov rdi, rsi
- mov rsi, rdx
- jmp .loop
-.done:
- mov rax, rdi
- ret
-
-; reduce_row(RDI=row): make denominator positive and divide the row by the gcd
-; of all its entries. Clobbers RAX, RCX, RDX, RSI, RDI, R8, R9, R10, R11.
-reduce_row:
- mov r11, rdi
- mov r8, r11
- imul r8, MNCOLS
- mov rax, [rowden + r11*8]
- test rax, rax
- jns .signok
- neg qword [rowden + r11*8]
- xor r10, r10
-.sneg:
- cmp r10, [nb]
- jg .signok
- mov rax, r8
- add rax, r10
- neg qword [Mnum + rax*8]
- inc r10
- jmp .sneg
-.signok:
- mov r9, [rowden + r11*8] ; g accumulator
- xor r10, r10
-.gloop:
- cmp r10, [nb]
- jg .gdone
- cmp r9, 1
- je .gdone
- mov rax, r8
- add rax, r10
- mov rsi, [Mnum + rax*8]
- mov rdx, rsi
- sar rdx, 63
- xor rsi, rdx
- sub rsi, rdx
- test rsi, rsi
- jz .gnext
- mov rdi, r9
- call gcd
- mov r9, rax
-.gnext:
- inc r10
- jmp .gloop
-.gdone:
- cmp r9, 1
- jle .done
- mov rax, [rowden + r11*8]
- xor rdx, rdx
- div r9
- mov [rowden + r11*8], rax
- xor r10, r10
-.divl:
- cmp r10, [nb]
- jg .done
- mov rax, r8
- add rax, r10
- mov rcx, rax
- mov rax, [Mnum + rcx*8]
- cqo
- idiv r9
- mov [Mnum + rcx*8], rax
- inc r10
- jmp .divl
-.done:
- ret
diff --git a/aoc-2025/day11.asm b/aoc-2025/day11.asm
deleted file mode 100644
index c3cd930..0000000
--- a/aoc-2025/day11.asm
+++ /dev/null
@@ -1,233 +0,0 @@
-; Advent of Code 2025 - Day 11
-
-bits 64
-default rel
-
-global _start
-
-%include "utils/io.asm"
-
-%define RAW_SIZE 262144
-%define NCODES 17576 ; 26^3 possible 3-letter names
-%define OUT_CODE 10003 ; encode("out")
-%define YOU_CODE 16608 ; encode("you")
-%define SVR_CODE 12731 ; encode("svr")
-%define DAC_CODE 2030 ; encode("dac")
-%define FFT_CODE 3529 ; encode("fft")
-%define MAXEDGES 8192
-
-section .data
- input_path db "input/day11.txt", 0
-
- label1 db "Part 1: ", 0
- label2 db "Part 2: ", 0
-
-section .bss
- raw resb RAW_SIZE
- adj_head resq NCODES ; first child index in `child`
- adj_cnt resq NCODES ; number of children
- child resq MAXEDGES ; flat child codes
- memo resq NCODES ; path counts, -1 = not computed
- blocked resb NCODES ; 1 = treat node as a dead end
-
- raw_len resq 1
- t1 resq 1
- t2 resq 1
-
-section .text
-
-_start:
- mov rdi, input_path
- lea rsi, [raw]
- mov rdx, RAW_SIZE
- call read_file
- mov [raw_len], rax
-
- ; memo = -1 everywhere
- mov rax, -1
- lea rdi, [memo]
- mov rcx, NCODES
- rep stosq
-
- ; parse "name: a b c" adjacency lines
- lea rsi, [raw]
- mov rax, [raw_len]
- lea r13, [raw + rax] ; end pointer
- xor r12, r12 ; child write index
-parse_line:
- cmp rsi, r13
- jae parse_done
- mov al, [rsi]
- cmp al, 10
- je parse_skip
- cmp al, 13
- je parse_skip
- call encode3 ; RAX = node code
- mov r10, rax
- mov [adj_head + r10*8], r12
-skip_sep:
- mov al, [rsi]
- cmp al, ':'
- je .adv
- cmp al, ' '
- je .adv
- jmp children
-.adv:
- inc rsi
- jmp skip_sep
-children:
- xor r11, r11 ; child count
-child_loop:
- cmp rsi, r13
- jae child_done
- mov al, [rsi]
- cmp al, 10
- je child_done
- cmp al, 13
- je child_done
- cmp al, ' '
- je child_space
- call encode3
- mov [child + r12*8], rax
- inc r12
- inc r11
- jmp child_loop
-child_space:
- inc rsi
- jmp child_loop
-child_done:
- mov [adj_cnt + r10*8], r11
-skip_eol:
- cmp rsi, r13
- jae parse_done
- mov al, [rsi]
- inc rsi
- cmp al, 10
- jne skip_eol
- jmp parse_line
-parse_skip:
- inc rsi
- jmp parse_line
-parse_done:
-
- ; part 1: paths you -> out
- call clear_memo
- mov rdi, YOU_CODE
- call count
- mov [t1], rax
-
- ; part 2: paths svr -> out visiting both dac and fft, via inclusion-exclusion
- call clear_memo
- mov rdi, SVR_CODE
- call count
- mov r12, rax ; N(all)
-
- mov byte [blocked + DAC_CODE], 1
- call clear_memo
- mov rdi, SVR_CODE
- call count
- mov r13, rax ; N(avoid dac)
- mov byte [blocked + DAC_CODE], 0
-
- mov byte [blocked + FFT_CODE], 1
- call clear_memo
- mov rdi, SVR_CODE
- call count
- mov r14, rax ; N(avoid fft)
- mov byte [blocked + FFT_CODE], 0
-
- mov byte [blocked + DAC_CODE], 1
- mov byte [blocked + FFT_CODE], 1
- call clear_memo
- mov rdi, SVR_CODE
- call count ; N(avoid both) in rax
- mov byte [blocked + DAC_CODE], 0
- mov byte [blocked + FFT_CODE], 0
-
- sub r12, r13
- sub r12, r14
- add r12, rax
- mov [t2], r12
-
- mov rcx, label1
- call print_string
- mov rax, [t1]
- call print_int64
- call print_newline
-
- mov rcx, label2
- call print_string
- mov rax, [t2]
- call print_int64
- call print_newline
-
- mov rax, SYS_EXIT
- xor rdi, rdi
- syscall
-
-; clear_memo: reset all memo entries to -1. Clobbers RAX, RCX, RDI.
-clear_memo:
- mov rax, -1
- lea rdi, [memo]
- mov rcx, NCODES
- rep stosq
- ret
-
-; encode3: read 3 lowercase letters at [RSI] into a base-26 code, advance RSI.
-; Output: RAX = code. Clobbers RAX, RDX.
-encode3:
- movzx eax, byte [rsi]
- sub eax, 'a'
- imul eax, eax, 676
- movzx edx, byte [rsi + 1]
- sub edx, 'a'
- imul edx, edx, 26
- add eax, edx
- movzx edx, byte [rsi + 2]
- sub edx, 'a'
- add eax, edx
- add rsi, 3
- ret
-
-; count: number of paths from node RDI to out. Memoised DFS over the DAG.
-; Output: RAX. Clobbers RAX; preserves everything else via the stack.
-count:
- cmp rdi, OUT_CODE
- je .one
- cmp byte [blocked + rdi], 0
- jne .zero
- mov rax, [memo + rdi*8]
- cmp rax, -1
- jne .ret
- push r12
- push r13
- push r14
- push r15
- mov r12, rdi
- mov r13, [adj_head + r12*8]
- mov r14, r13
- add r14, [adj_cnt + r12*8]
- xor r15, r15
-.loop:
- cmp r13, r14
- jge .done
- mov rdi, [child + r13*8]
- call count
- add r15, rax
- inc r13
- jmp .loop
-.done:
- mov [memo + r12*8], r15
- mov rax, r15
- pop r15
- pop r14
- pop r13
- pop r12
- ret
-.one:
- mov rax, 1
- ret
-.zero:
- xor eax, eax
-.ret:
- ret
diff --git a/aoc-2025/day12.asm b/aoc-2025/day12.asm
deleted file mode 100644
index 8931a2b..0000000
--- a/aoc-2025/day12.asm
+++ /dev/null
@@ -1,486 +0,0 @@
-; Advent of Code 2025 - Day 12
-
-bits 64
-default rel
-
-global _start
-
-%include "utils/io.asm"
-%include "utils/parse.asm"
-
-%define RAW_SIZE 262144
-%define GRID_SIZE 4096 ; regions are at most 50x50
-
-section .data
- input_path db "input/day12.txt", 0
-
- label1 db "Part 1: ", 0
-
-section .bss
- raw resb RAW_SIZE
-
- base_r resd (6 * 9) ; parsed shape cells
- base_c resd (6 * 9)
- base_n resq 6 ; cells per shape (also its area cost)
-
- odr resd (6 * 8 * 9) ; orientation cell offsets from anchor
- odc resd (6 * 8 * 9)
- oncells resq (6 * 8)
-
- tr resd 9 ; scratch for orientation building
- tc resd 9
-
- grid resb GRID_SIZE
- remaining resq 6
- counts resq 6
-
- raw_len resq 1
- cursor resq 1
- pend resq 1
- cur_shape resq 1
- shape_row resq 1
- orients_done resq 1
- rW resq 1
- rL resq 1
- area resq 1
- left resq 1
- count_feasible resq 1
-
-section .text
-
-_start:
- mov rdi, input_path
- lea rsi, [raw]
- mov rdx, RAW_SIZE
- call read_file
- mov [raw_len], rax
-
- ; parse shapes and regions
- lea rsi, [raw]
- mov rax, [raw_len]
- lea rax, [raw + rax]
- mov [pend], rax
- mov qword [cur_shape], 0
- mov qword [orients_done], 0
-pline:
- cmp rsi, [pend]
- jae pdone
- mov al, [rsi]
- cmp al, 10
- je pskip
- cmp al, 13
- je pskip
- cmp al, ' '
- je pskip
- cmp al, '0'
- jl maybe_grid
- cmp al, '9'
- jg maybe_grid
- ; a number begins the line
- call parse_u64
- mov r10, rax
- mov al, [rsi]
- cmp al, 'x'
- je region_line
- ; shape header "N:"
- inc rsi
- mov [cur_shape], r10
- mov qword [base_n + r10*8], 0
- mov qword [shape_row], 0
- jmp eol
-
-region_line:
- cmp qword [orients_done], 0
- jne .have
- mov [cursor], rsi
- call gen_orients
- mov rsi, [cursor]
- mov qword [orients_done], 1
-.have:
- inc rsi ; skip 'x'
- call parse_u64
- mov r11, rax ; L
-.find_colon:
- mov al, [rsi]
- inc rsi
- cmp al, ':'
- jne .find_colon
- xor rcx, rcx
-.rcounts:
- cmp rcx, 6
- jge .rcounts_done
-.rsp:
- mov al, [rsi]
- cmp al, ' '
- jne .rsp_done
- inc rsi
- jmp .rsp
-.rsp_done:
- call parse_u64
- mov [counts + rcx*8], rax
- inc rcx
- jmp .rcounts
-.rcounts_done:
- mov [cursor], rsi
- mov rdi, r10 ; W
- mov rsi, r11 ; L
- call solve_region
- mov rsi, [cursor]
- jmp eol
-
-maybe_grid:
- cmp al, '#'
- je grid_row
- cmp al, '.'
- je grid_row
- inc rsi
- jmp pline
-grid_row:
- mov r10, [cur_shape]
- mov r11, [shape_row]
- xor rcx, rcx ; column
-.cell:
- mov al, [rsi]
- cmp al, '#'
- je .hash
- cmp al, '.'
- je .dot
- jmp .row_done
-.hash:
- mov rax, r10
- imul rax, 9
- add rax, [base_n + r10*8]
- mov edx, r11d
- mov [base_r + rax*4], edx
- mov edx, ecx
- mov [base_c + rax*4], edx
- inc qword [base_n + r10*8]
-.dot:
- inc rcx
- inc rsi
- jmp .cell
-.row_done:
- inc qword [shape_row]
- jmp eol
-
-eol:
- cmp rsi, [pend]
- jae pdone
- mov al, [rsi]
- inc rsi
- cmp al, 10
- jne eol
- jmp pline
-pskip:
- inc rsi
- jmp pline
-pdone:
-
- mov rcx, label1
- call print_string
- mov rax, [count_feasible]
- call print_int64
- call print_newline
-
- mov rax, SYS_EXIT
- xor rdi, rdi
- syscall
-
-; gen_orients: build the 8 dihedral orientations of every shape as offset lists
-; anchored at their row-major-first cell.
-gen_orients:
- xor r12, r12 ; shape
-.s_loop:
- cmp r12, 6
- jge .done
- xor r13, r13 ; transform 0..7
-.t_loop:
- cmp r13, 8
- jge .s_next
- mov r14, [base_n + r12*8]
- xor rcx, rcx
-.k_loop:
- cmp rcx, r14
- jge .k_done
- mov rax, r12
- imul rax, 9
- add rax, rcx
- movsxd r8, dword [base_r + rax*4]
- movsxd r9, dword [base_c + rax*4]
- test r13, 4
- jz .noflip
- neg r9
-.noflip:
- mov rdx, r13
- and rdx, 3
-.rot:
- test rdx, rdx
- jz .rot_done
- mov r15, r8
- mov r8, r9 ; new r = c
- mov r9, r15
- neg r9 ; new c = -r
- dec rdx
- jmp .rot
-.rot_done:
- mov [tr + rcx*4], r8d
- mov [tc + rcx*4], r9d
- inc rcx
- jmp .k_loop
-.k_done:
- ; min row
- movsxd r8, dword [tr]
- xor rcx, rcx
-.minr:
- cmp rcx, r14
- jge .minr_done
- movsxd rax, dword [tr + rcx*4]
- cmp rax, r8
- jge .minr_next
- mov r8, rax
-.minr_next:
- inc rcx
- jmp .minr
-.minr_done:
- ; min col among cells on the min row
- mov r9, 0x7fffffff
- xor rcx, rcx
-.minc:
- cmp rcx, r14
- jge .minc_done
- movsxd rax, dword [tr + rcx*4]
- cmp rax, r8
- jne .minc_next
- movsxd rax, dword [tc + rcx*4]
- cmp rax, r9
- jge .minc_next
- mov r9, rax
-.minc_next:
- inc rcx
- jmp .minc
-.minc_done:
- mov r11, r12
- shl r11, 3
- add r11, r13 ; orientation index
- mov [oncells + r11*8], r14
- xor rcx, rcx
-.store:
- cmp rcx, r14
- jge .store_done
- movsxd rax, dword [tr + rcx*4]
- sub rax, r8 ; dr
- movsxd rdx, dword [tc + rcx*4]
- sub rdx, r9 ; dc
- mov rbx, r11
- imul rbx, 9
- add rbx, rcx
- mov [odr + rbx*4], eax
- mov [odc + rbx*4], edx
- inc rcx
- jmp .store
-.store_done:
- inc r13
- jmp .t_loop
-.s_next:
- inc r12
- jmp .s_loop
-.done:
- ret
-
-; solve_region(RDI=W, RSI=L): if the region can fit all presents in `counts`,
-; increment count_feasible.
-solve_region:
- xor r8, r8 ; total cells
- xor rcx, rcx
-.tot:
- cmp rcx, 6
- jge .tot_done
- mov rax, [counts + rcx*8]
- imul rax, [base_n + rcx*8]
- add r8, rax
- inc rcx
- jmp .tot
-.tot_done:
- mov rax, rdi
- imul rax, rsi
- mov [area], rax
- mov [rW], rdi
- mov [rL], rsi
- cmp r8, rax
- jg .ret ; more cells than space -> impossible
- mov r9, rax
- sub r9, r8 ; empty budget
-
- xor r10, r10 ; piece count
- xor rcx, rcx
-.setrem:
- cmp rcx, 6
- jge .setrem_done
- mov rax, [counts + rcx*8]
- mov [remaining + rcx*8], rax
- add r10, rax
- inc rcx
- jmp .setrem
-.setrem_done:
- mov [left], r10
-
- mov rcx, [area]
- lea rdi, [grid]
- xor al, al
- rep stosb
-
- xor rdi, rdi ; pos = 0
- mov rsi, r9 ; empty budget
- call rec
- test rax, rax
- jz .ret
- inc qword [count_feasible]
-.ret:
- ret
-
-; rec(RDI=pos, RSI=empty_budget) -> RAX 1 if the remaining pieces fit.
-; State: R12=pos R13=eb R14=row R15=col RBX=shape RBP=orientation.
-rec:
- push rbx
- push rbp
- push r12
- push r13
- push r14
- push r15
- mov r12, rdi
- mov r13, rsi
- cmp qword [left], 0
- je .true
-.adv:
- cmp r12, [area]
- jae .false
- cmp byte [grid + r12], 0
- je .found
- inc r12
- jmp .adv
-.found:
- mov rax, r12
- xor rdx, rdx
- div qword [rW]
- mov r14, rax ; row
- mov r15, rdx ; col
-
- xor rbx, rbx ; shape
-.s_loop:
- cmp rbx, 6
- jge .try_empty
- cmp qword [remaining + rbx*8], 0
- je .s_next
- xor rbp, rbp ; orientation
-.t_loop:
- cmp rbp, 8
- jge .s_next
- mov rax, rbx
- shl rax, 3
- add rax, rbp
- mov r10, rax ; orientation index
- mov r11, [oncells + r10*8]
- ; validate placement
- xor rcx, rcx
-.chk:
- cmp rcx, r11
- jge .place
- mov rax, r10
- imul rax, 9
- add rax, rcx
- movsxd r8, dword [odr + rax*4]
- movsxd r9, dword [odc + rax*4]
- add r8, r14 ; rr
- add r9, r15 ; cc
- cmp r8, [rL]
- jge .t_next
- cmp r9, 0
- jl .t_next
- cmp r9, [rW]
- jge .t_next
- mov rax, r8
- imul rax, [rW]
- add rax, r9
- cmp byte [grid + rax], 0
- jne .t_next
- inc rcx
- jmp .chk
-.place:
- xor rcx, rcx
-.place_k:
- cmp rcx, r11
- jge .place_done
- mov rax, r10
- imul rax, 9
- add rax, rcx
- movsxd r8, dword [odr + rax*4]
- movsxd r9, dword [odc + rax*4]
- add r8, r14
- add r9, r15
- imul r8, [rW]
- add r8, r9
- mov byte [grid + r8], 1
- inc rcx
- jmp .place_k
-.place_done:
- dec qword [remaining + rbx*8]
- dec qword [left]
- mov rdi, r12
- mov rsi, r13
- call rec
- test rax, rax
- jnz .true
- inc qword [remaining + rbx*8]
- inc qword [left]
- mov rax, rbx ; child clobbered r10/r11 - rebuild them
- shl rax, 3
- add rax, rbp
- mov r10, rax
- mov r11, [oncells + r10*8]
- xor rcx, rcx
-.unplace_k:
- cmp rcx, r11
- jge .unplace_done
- mov rax, r10
- imul rax, 9
- add rax, rcx
- movsxd r8, dword [odr + rax*4]
- movsxd r9, dword [odc + rax*4]
- add r8, r14
- add r9, r15
- imul r8, [rW]
- add r8, r9
- mov byte [grid + r8], 0
- inc rcx
- jmp .unplace_k
-.unplace_done:
-.t_next:
- inc rbp
- jmp .t_loop
-.s_next:
- inc rbx
- jmp .s_loop
-.try_empty:
- test r13, r13
- jz .false
- mov byte [grid + r12], 1
- mov rdi, r12
- mov rsi, r13
- dec rsi
- call rec
- test rax, rax
- jnz .true
- mov byte [grid + r12], 0
-.false:
- xor eax, eax
- jmp .ret
-.true:
- mov eax, 1
-.ret:
- pop r15
- pop r14
- pop r13
- pop r12
- pop rbp
- pop rbx
- ret
diff --git a/aoc-2025/day2.asm b/aoc-2025/day2.asm
deleted file mode 100644
index 987fbf9..0000000
--- a/aoc-2025/day2.asm
+++ /dev/null
@@ -1,324 +0,0 @@
-; 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
diff --git a/aoc-2025/day3.asm b/aoc-2025/day3.asm
deleted file mode 100644
index 8330755..0000000
--- a/aoc-2025/day3.asm
+++ /dev/null
@@ -1,234 +0,0 @@
-; Advent of Code 2025 - Day 3
-
-bits 64
-default rel
-
-global _start
-
-%include "utils/io.asm"
-
-section .data
- input_path db "input/day3.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
-
- work_digits resb 64 ; stack for greedy subsequence
-
-section .text
-
-_start:
- mov rax, SYS_OPEN
- mov rdi, input_path
- mov rsi, O_RDONLY
- xor rdx, rdx
- syscall
-
- mov r12, rax ; fd
- 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+edx], 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:
- ; possible last partial line
- mov edx, [line_len]
- cmp edx, 0
- jle print_results
-
- mov byte [line_buffer+edx], 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
-
-handle_line:
- mov rdi, rsi
- xor ecx, ecx ; ECX = 0
-.length_loop:
- mov al, [rdi+rcx]
- cmp al, 0
- je .length_done
- inc ecx
- 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 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 .skip_part2
-
- mov ecx, r15d
- mov edi, 12
- call get_max_for_k ; RAX = best 12-digit value
- add qword [sum2], rax
-
-.skip_part2:
- ret
-
-; Greedy lexicographically largest subsequence of length K
-;
-; Input:
-; RSI -> digits string
-; ECX = n (length)
-; EDI = k
-; Output:
-; RAX = numeric value of chosen k digits
-; Clobbers: R8..R11, EAX, EDX
-get_max_for_k:
- ; drops_remaining = n - k
- mov eax, ecx
- sub eax, edi
- mov r8d, eax ; r8d = drops
-
- xor r9d, r9d ; r9d = stack_size
- xor r10d, r10d ; r10d = i
-
-.loop:
- cmp r10d, ecx
- jge .after_input
-
- ; current digit in AL
- mov r11, r10
- mov al, [rsi + r11]
-
-.pop:
- cmp r9d, 0
- jle .push
- cmp r8d, 0
- jle .push
-
- mov edx, r9d
- dec edx
- mov r11d, edx
- mov bl, [work_digits + r11]
-
- cmp bl, al
- jge .push
-
- dec r9d
- dec r8d
- jmp .pop
-.push:
- mov r11d, r9d
- mov [work_digits + r11], al
- inc r9d
- inc r10d
- jmp .loop
-
-.after_input:
- ; trim if stack_size > k
-.trim:
- cmp r9d, edi
- jle .convert
- dec r9d
- jmp .trim
-.convert:
- xor rax, rax
- xor r11d, r11d
-.convert_loop:
- cmp r11d, edi
- jge .done
-
- mov rdx, r11
- mov dl, [work_digits + rdx]
- sub dl, '0'
- movzx rdx, dl
-
- imul rax, rax, 10
- add rax, rdx
-
- inc r11d
- jmp .convert_loop
-.done:
- ret
diff --git a/aoc-2025/day4.asm b/aoc-2025/day4.asm
deleted file mode 100644
index 60e53ec..0000000
--- a/aoc-2025/day4.asm
+++ /dev/null
@@ -1,236 +0,0 @@
-; Advent of Code 2025 - Day 4
-
-bits 64
-default rel
-
-global _start
-
-%include "utils/io.asm"
-
-%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
- input_path db "input/day4.txt", 0
-
- 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
-
-section .text
-
-_start:
- mov rdi, input_path
- lea rsi, [raw]
- mov rdx, RAW_SIZE
- call read_file
- mov [raw_len], rax
-
- 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
diff --git a/aoc-2025/day5.asm b/aoc-2025/day5.asm
deleted file mode 100644
index 4718343..0000000
--- a/aoc-2025/day5.asm
+++ /dev/null
@@ -1,225 +0,0 @@
-; Advent of Code 2025 - Day 5
-
-bits 64
-default rel
-
-global _start
-
-%include "utils/io.asm"
-%include "utils/parse.asm"
-
-%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
-
- 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
-
-section .text
-
-_start:
- mov rdi, input_path
- lea rsi, [raw]
- mov rdx, RAW_SIZE
- call read_file
- mov [raw_len], rax
-
- ; 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
-
-; 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
diff --git a/aoc-2025/day6.asm b/aoc-2025/day6.asm
deleted file mode 100644
index 728b8b0..0000000
--- a/aoc-2025/day6.asm
+++ /dev/null
@@ -1,331 +0,0 @@
-; Advent of Code 2025 - Day 6
-
-bits 64
-default rel
-
-global _start
-
-%include "utils/io.asm"
-
-%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
-
- 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
-
-section .text
-
-_start:
- mov rdi, input_path
- lea rsi, [raw]
- mov rdx, RAW_SIZE
- call read_file
- mov [raw_len], rax
-
- ; 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
diff --git a/aoc-2025/day7.asm b/aoc-2025/day7.asm
deleted file mode 100644
index 1d990a7..0000000
--- a/aoc-2025/day7.asm
+++ /dev/null
@@ -1,302 +0,0 @@
-; Advent of Code 2025 - Day 7
-
-bits 64
-default rel
-
-global _start
-
-%include "utils/io.asm"
-
-%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
-
- 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
-
-section .text
-
-_start:
- mov rdi, input_path
- lea rsi, [raw]
- mov rdx, RAW_SIZE
- call read_file
- mov [raw_len], rax
-
- ; 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
diff --git a/aoc-2025/day8.asm b/aoc-2025/day8.asm
deleted file mode 100644
index 6a4ecec..0000000
--- a/aoc-2025/day8.asm
+++ /dev/null
@@ -1,326 +0,0 @@
-; Advent of Code 2025 - Day 8
-
-bits 64
-default rel
-
-global _start
-
-%include "utils/io.asm"
-%include "utils/parse.asm"
-
-%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
-
- 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
-
-section .text
-
-_start:
- mov rdi, input_path
- lea rsi, [raw]
- mov rdx, RAW_SIZE
- call read_file
- mov [raw_len], rax
-
- ; 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
diff --git a/aoc-2025/day9.asm b/aoc-2025/day9.asm
deleted file mode 100644
index e5a7d0d..0000000
--- a/aoc-2025/day9.asm
+++ /dev/null
@@ -1,540 +0,0 @@
-; Advent of Code 2025 - Day 9
-
-bits 64
-default rel
-
-global _start
-
-%include "utils/io.asm"
-%include "utils/parse.asm"
-
-%define RAW_SIZE 262144
-%define MAXN 512
-%define MAXC 1024
-%define MAXR 1024
-%define MAXP ((MAXC + 1) * (MAXR + 1))
-
-section .data
- input_path db "input/day9.txt", 0
-
- label1 db "Part 1: ", 0
- label2 db "Part 2: ", 0
-
-section .bss
- raw resb RAW_SIZE
- vx resq MAXN
- vy resq MAXN
- xs resq MAXN
- ys resq MAXN
- xs_to_col resd MAXN
- ys_to_row resd MAXN
- col_lo resq MAXC
- row_lo resq MAXR
- thincol resd MAXN
- thinrow resd MAXN
-
- vex resq MAXN ; vertical edges: x, ylo, yhi
- veylo resq MAXN
- veyhi resq MAXN
- hey resq MAXN ; horizontal edges: y, xlo, xhi
- hexlo resq MAXN
- hexhi resq MAXN
-
- pref resd MAXP ; 2D prefix sum of unfilled cells
-
- raw_len resq 1
- n_v resq 1
- xn resq 1
- yn resq 1
- c_cnt resq 1
- r_cnt resq 1
- cp1 resq 1
- ve_cnt resq 1
- he_cnt resq 1
- best1 resq 1
- best2 resq 1
-
-section .text
-
-_start:
- mov rdi, input_path
- lea rsi, [raw]
- mov rdx, RAW_SIZE
- call read_file
- mov [raw_len], rax
-
- ; parse "X,Y" vertices in order
- lea rsi, [raw]
- mov rax, [raw_len]
- lea r14, [raw + rax]
- xor r12, r12
-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 [vx + r12*8], rax
- inc rsi
- call parse_u64
- mov [vy + 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_v], r12
-
- ; xs = sorted, deduped x coordinates
- xor rcx, rcx
-copy_xs:
- cmp rcx, [n_v]
- jge copy_xs_done
- mov rax, [vx + rcx*8]
- mov [xs + rcx*8], rax
- inc rcx
- jmp copy_xs
-copy_xs_done:
- lea rdi, [xs]
- mov rsi, [n_v]
- call sort_q
- lea rdi, [xs]
- mov rsi, [n_v]
- call dedup_q
- mov [xn], rax
-
- ; ys = sorted, deduped y coordinates
- xor rcx, rcx
-copy_ys:
- cmp rcx, [n_v]
- jge copy_ys_done
- mov rax, [vy + rcx*8]
- mov [ys + rcx*8], rax
- inc rcx
- jmp copy_ys
-copy_ys_done:
- lea rdi, [ys]
- mov rsi, [n_v]
- call sort_q
- lea rdi, [ys]
- mov rsi, [n_v]
- call dedup_q
- mov [yn], rax
-
- ; refined columns: thin at each xs, thick strip between
- xor rcx, rcx ; i
- xor r12, r12 ; column count
-build_cols:
- cmp rcx, [xn]
- jge build_cols_done
- mov [xs_to_col + rcx*4], r12d
- mov rax, [xs + rcx*8]
- mov [col_lo + r12*8], rax
- inc r12
- mov rdx, rcx
- inc rdx
- cmp rdx, [xn]
- jge build_cols_next
- mov r8, [xs + rdx*8]
- mov rax, [xs + rcx*8]
- sub r8, rax
- cmp r8, 1
- jle build_cols_next
- inc rax
- mov [col_lo + r12*8], rax
- inc r12
-build_cols_next:
- inc rcx
- jmp build_cols
-build_cols_done:
- mov [c_cnt], r12
- inc r12
- mov [cp1], r12
-
- ; refined rows: thin at each ys, thick strip between
- xor rcx, rcx
- xor r12, r12
-build_rows:
- cmp rcx, [yn]
- jge build_rows_done
- mov [ys_to_row + rcx*4], r12d
- mov rax, [ys + rcx*8]
- mov [row_lo + r12*8], rax
- inc r12
- mov rdx, rcx
- inc rdx
- cmp rdx, [yn]
- jge build_rows_next
- mov r8, [ys + rdx*8]
- mov rax, [ys + rcx*8]
- sub r8, rax
- cmp r8, 1
- jle build_rows_next
- inc rax
- mov [row_lo + r12*8], rax
- inc r12
-build_rows_next:
- inc rcx
- jmp build_rows
-build_rows_done:
- mov [r_cnt], r12
-
- ; per-vertex refined thin column / row index
- xor r8, r8
-map_v:
- cmp r8, [n_v]
- jge map_v_done
- mov r13, [vx + r8*8]
- xor rcx, rcx
-map_v_xs:
- mov rax, [xs + rcx*8]
- cmp rax, r13
- je map_v_xf
- inc rcx
- jmp map_v_xs
-map_v_xf:
- mov edx, [xs_to_col + rcx*4]
- mov [thincol + r8*4], edx
- mov r13, [vy + r8*8]
- xor rcx, rcx
-map_v_ys:
- mov rax, [ys + rcx*8]
- cmp rax, r13
- je map_v_yf
- inc rcx
- jmp map_v_ys
-map_v_yf:
- mov edx, [ys_to_row + rcx*4]
- mov [thinrow + r8*4], edx
- inc r8
- jmp map_v
-map_v_done:
-
- ; classify edges into vertical / horizontal
- xor r8, r8
- xor r12, r12 ; ve count
- xor r13, r13 ; he count
-gen_edges:
- cmp r8, [n_v]
- jge gen_edges_done
- mov r9, r8
- inc r9
- cmp r9, [n_v]
- jne ge_nowrap
- xor r9, r9
-ge_nowrap:
- mov r10, [vx + r8*8]
- mov r11, [vx + r9*8]
- cmp r10, r11
- je ge_vert
- mov rax, [vy + r8*8]
- mov [hey + r13*8], rax
- mov rcx, r10
- mov rdx, r11
- cmp rcx, rdx
- jle ge_h_ok
- xchg rcx, rdx
-ge_h_ok:
- mov [hexlo + r13*8], rcx
- mov [hexhi + r13*8], rdx
- inc r13
- jmp ge_next
-ge_vert:
- mov [vex + r12*8], r10
- mov rcx, [vy + r8*8]
- mov rdx, [vy + r9*8]
- cmp rcx, rdx
- jle ge_v_ok
- xchg rcx, rdx
-ge_v_ok:
- mov [veylo + r12*8], rcx
- mov [veyhi + r12*8], rdx
- inc r12
-ge_next:
- inc r8
- jmp gen_edges
-gen_edges_done:
- mov [ve_cnt], r12
- mov [he_cnt], r13
-
- ; 2D prefix sum of unfilled cells
- xor r15, r15 ; refined row r
-pref_r:
- cmp r15, [r_cnt]
- jge pref_done
- mov r13, [row_lo + r15*8] ; py
- xor r14, r14 ; refined col c
-pref_c:
- cmp r14, [c_cnt]
- jge pref_c_done
- mov rdi, [col_lo + r14*8]
- mov rsi, r13
- call filled
- mov rbx, 1
- sub rbx, rax ; unfilled indicator
-
- mov rax, r15
- imul rax, [cp1] ; r*cp1
- mov rcx, rax
- add rcx, r14
- add rcx, 1
- mov edx, [pref + rcx*4] ; P[r][c+1]
- mov rcx, rax
- add rcx, r14
- mov r8d, [pref + rcx*4] ; P[r][c]
- mov rcx, rax
- add rcx, [cp1]
- mov r9, rcx ; (r+1)*cp1
- add rcx, r14
- mov r10d, [pref + rcx*4] ; P[r+1][c]
- mov eax, ebx
- add eax, edx
- add eax, r10d
- sub eax, r8d
- mov rcx, r9
- add rcx, r14
- add rcx, 1
- mov [pref + rcx*4], eax ; P[r+1][c+1]
- inc r14
- jmp pref_c
-pref_c_done:
- inc r15
- jmp pref_r
-pref_done:
-
- ; all pairs: part 1 = max area, part 2 = max area fully inside
- mov qword [best1], 0
- mov qword [best2], 0
- xor r8, r8
-pair_i:
- cmp r8, [n_v]
- jge pair_done
- mov r12d, [thincol + r8*4]
- mov r13d, [thinrow + r8*4]
- mov r14, [vx + r8*8]
- mov r15, [vy + r8*8]
- lea r9, [r8 + 1]
-pair_j:
- cmp r9, [n_v]
- jge pair_j_done
-
- ; area = (|xi-xj| + 1) * (|yi-yj| + 1)
- mov rax, r14
- sub rax, [vx + r9*8]
- jns .ax
- neg rax
-.ax:
- inc rax
- mov rbx, rax
- mov rax, r15
- sub rax, [vy + r9*8]
- jns .ay
- neg rax
-.ay:
- inc rax
- imul rax, rbx
- cmp rax, [best1]
- jle .no1
- mov [best1], rax
-.no1:
- push rax ; save area
-
- ; refined block: c1,c2 then r1,r2
- mov eax, [thincol + r9*4]
- mov ecx, r12d
- cmp ecx, eax
- jle .cc
- xchg ecx, eax
-.cc:
- mov edi, ecx ; c1
- mov esi, eax ; c2
- mov eax, [thinrow + r9*4]
- mov ecx, r13d
- cmp ecx, eax
- jle .rr
- xchg ecx, eax
-.rr:
- mov edx, ecx ; r1
- mov ecx, eax ; r2
- call block_unf
- pop rbx ; area
- test eax, eax
- jnz .no2 ; contains unfilled cells
- cmp rbx, [best2]
- jle .no2
- mov [best2], rbx
-.no2:
- inc r9
- jmp pair_j
-pair_j_done:
- inc r8
- jmp pair_i
-pair_done:
-
-print_results:
- mov rcx, label1
- call print_string
- mov rax, [best1]
- call print_int64
- call print_newline
-
- mov rcx, label2
- call print_string
- mov rax, [best2]
- call print_int64
- call print_newline
-
- mov rax, SYS_EXIT
- xor rdi, rdi
- syscall
-
-; filled: is tile (RDI=px, RSI=py) red or green (inside or on the loop)?
-; Output: RAX = 1 if filled, else 0. Clobbers RAX, RCX, RDX, R10, R11.
-filled:
- xor ecx, ecx ; on-edge flag
- xor r11, r11 ; crossing count
- xor r10, r10 ; edge index
-.v_loop:
- cmp r10, [ve_cnt]
- jge .v_done
- mov rax, [vex + r10*8]
- cmp rax, rdi
- jne .v_cross
- mov rdx, [veylo + r10*8]
- cmp rsi, rdx
- jl .v_cross
- mov rdx, [veyhi + r10*8]
- cmp rsi, rdx
- jg .v_cross
- mov ecx, 1 ; on a vertical edge
-.v_cross:
- mov rax, [vex + r10*8]
- cmp rax, rdi
- jle .v_next
- mov rdx, [veylo + r10*8]
- cmp rsi, rdx
- jl .v_next
- mov rdx, [veyhi + r10*8]
- cmp rsi, rdx
- jge .v_next
- inc r11
-.v_next:
- inc r10
- jmp .v_loop
-.v_done:
- test ecx, ecx
- jnz .yes
- xor r10, r10
-.h_loop:
- cmp r10, [he_cnt]
- jge .h_done
- mov rax, [hey + r10*8]
- cmp rax, rsi
- jne .h_next
- mov rdx, [hexlo + r10*8]
- cmp rdi, rdx
- jl .h_next
- mov rdx, [hexhi + r10*8]
- cmp rdi, rdx
- jg .h_next
- jmp .yes
-.h_next:
- inc r10
- jmp .h_loop
-.h_done:
- mov rax, r11
- and rax, 1
- ret
-.yes:
- mov rax, 1
- ret
-
-; block_unf: count of unfilled refined cells in [c1,c2] x [r1,r2].
-; In: RDI=c1, RSI=c2, RDX=r1, RCX=r2. Out: EAX. Clobbers RAX, R10, R11.
-block_unf:
- mov r10, rcx
- inc r10
- imul r10, [cp1] ; (r2+1)*cp1
- mov r11, r10
- add r11, rsi
- add r11, 1
- mov eax, [pref + r11*4] ; A
- mov r11, r10
- add r11, rdi
- sub eax, [pref + r11*4] ; - C
- mov r10, rdx
- imul r10, [cp1] ; r1*cp1
- mov r11, r10
- add r11, rsi
- add r11, 1
- sub eax, [pref + r11*4] ; - B
- mov r11, r10
- add r11, rdi
- add eax, [pref + r11*4] ; + D
- ret
-
-; sort_q: insertion sort of qword array RDI[0..RSI-1] ascending.
-; Clobbers RAX, RDX, R8, R9, R10.
-sort_q:
- mov r8, 1
-.i:
- cmp r8, rsi
- jge .done
- mov r10, [rdi + r8*8]
- mov r9, r8
- dec r9
-.j:
- cmp r9, 0
- jl .place
- mov rax, [rdi + r9*8]
- cmp rax, r10
- jle .place
- mov rdx, r9
- inc rdx
- mov [rdi + rdx*8], rax
- dec r9
- jmp .j
-.place:
- mov rdx, r9
- inc rdx
- mov [rdi + rdx*8], r10
- inc r8
- jmp .i
-.done:
- ret
-
-; dedup_q: remove consecutive duplicates in sorted RDI[0..RSI-1].
-; Out: RAX = new length. Clobbers RAX, RCX, RDX, R8.
-dedup_q:
- xor rax, rax
- xor rcx, rcx
-.loop:
- cmp rcx, rsi
- jge .done
- test rax, rax
- jz .keep
- mov rdx, [rdi + rcx*8]
- mov r8, rax
- dec r8
- cmp rdx, [rdi + r8*8]
- je .skip
-.keep:
- mov rdx, [rdi + rcx*8]
- mov [rdi + rax*8], rdx
- inc rax
-.skip:
- inc rcx
- jmp .loop
-.done:
- ret
diff --git a/aoc-2025/utils/io.asm b/aoc-2025/utils/io.asm
deleted file mode 100644
index 2081671..0000000
--- a/aoc-2025/utils/io.asm
+++ /dev/null
@@ -1,107 +0,0 @@
-; utils/io.asm - shared syscall constants, file reading, and output helpers.
-; This file is %included (not linked) by each day's solution.
-
-%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
-
-section .data
- newline db 10
-
-section .bss
- number_buffer resb 32
-
-section .text
-
-; read_file: open the path in RDI read-only, read up to RDX bytes into the
-; buffer in RSI, close, and return the number of bytes read in RAX.
-read_file:
- mov r8, rsi ; buffer base
- mov r9, rdx ; capacity
- mov rax, SYS_OPEN
- mov rsi, O_RDONLY
- xor rdx, rdx
- syscall
- mov r10, rax ; fd
- xor r11, r11 ; bytes read so far
-.loop:
- mov rax, SYS_READ
- mov rdi, r10
- lea rsi, [r8 + r11]
- mov rdx, r9
- sub rdx, r11
- syscall
- cmp rax, 0
- jle .done
- add r11, rax
- jmp .loop
-.done:
- mov rax, SYS_CLOSE
- mov rdi, r10
- syscall
- mov rax, r11
- ret
-
-print_newline:
- mov rax, SYS_WRITE
- mov rdi, STDOUT
- mov rsi, newline
- mov rdx, 1
- syscall
- ret
-
-; print_int64: print the unsigned integer in RAX in decimal.
-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: print the NUL-terminated string pointed to by RCX.
-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/utils/parse.asm b/aoc-2025/utils/parse.asm
deleted file mode 100644
index 833cb4e..0000000
--- a/aoc-2025/utils/parse.asm
+++ /dev/null
@@ -1,22 +0,0 @@
-; utils/parse.asm - decimal parsing helper.
-
-section .text
-
-; parse_u64: read decimal digits at [RSI] into RAX, advancing RSI past them.
-; 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