hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/aoc-2025
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-25 11:26:54 +0200
committerhachem <im@hachem.wtf>2026-08-25 11:26:54 +0200
commit2b0dad9d87b2b663ddc008876a4dbc3d17993d27 (patch)
tree7394aef05e1ca7c0518a0f7ff6d785c0304e1863 /aoc-2025
parent2806ed7d55266ececb197196af2fb84ae8c3cb35 (diff)
chore: refactor repository
Diffstat (limited to 'aoc-2025')
-rw-r--r--aoc-2025/day1.asm314
-rw-r--r--aoc-2025/day2.asm410
-rw-r--r--aoc-2025/day3.asm301
-rw-r--r--aoc-2025/day4.asm25
4 files changed, 1050 insertions, 0 deletions
diff --git a/aoc-2025/day1.asm b/aoc-2025/day1.asm
new file mode 100644
index 0000000..8ba8f0e
--- /dev/null
+++ b/aoc-2025/day1.asm
@@ -0,0 +1,314 @@
+; Advent of Code 2025 - Day 1
+
+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
+
+section .data
+ inputPath db "input/day1.txt", 0
+ newline db 10
+
+ 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
+ lineBuffer resb 256
+ numberBuffer resb 16
+ lineLen resd 1
+
+section .text
+
+_start:
+ ; open file
+ mov rax, SYS_OPEN
+ mov rdi, inputPath
+ mov rsi, O_RDONLY
+ xor rdx, rdx
+ syscall
+
+ mov r12, rax ; fd in R12
+ mov dword [lineLen], 0 ; no chars in current line yet
+
+ReadLoop:
+ ; 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
+
+ScanChunk:
+ cmp rbp, 0
+ je ReadLoop ; chunk done, read next
+
+ mov al, [rdi]
+
+ ; windows can be a fucking bitch
+ ; handle (CR) -> just skip
+ cmp al, 13
+ je SkipCR
+
+ ; handle LF (10) -> end of line
+ cmp al, 10
+ je EndOfLine
+
+ ; 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
+ inc edx
+ mov [lineLen], edx
+
+SkipStore:
+ inc rdi
+ dec rbp
+ jmp ScanChunk
+
+SkipCR:
+ inc rdi
+ dec rbp
+ jmp ScanChunk
+
+EndOfLine:
+ ; terminate current line
+ mov edx, [lineLen]
+ mov byte [lineBuffer+rdx], 0
+
+ ; if line isn't empty, process it
+ cmp edx, 0
+ je LineDone
+
+ push rdi
+ mov rsi, lineBuffer
+ call HandleLine
+ pop rdi
+
+LineDone:
+ mov dword [lineLen], 0 ; reset line length
+ inc rdi
+ dec rbp
+ jmp ScanChunk
+
+; process last line if not newline-terminated, then print result
+EOF:
+ ; if there is a partial line at EOF, process it
+ mov edx, [lineLen]
+ cmp edx, 0
+ jle PrintResults
+ mov byte [lineBuffer+rdx], 0
+
+ mov rsi, lineBuffer
+ call HandleLine
+
+PrintResults:
+ mov rcx, label1
+ call PrintString
+ mov eax, [password1]
+ call PrintInt
+
+ ; newline
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, newline
+ mov rdx, 1
+ syscall
+
+ mov rcx, label2
+ call PrintString
+ mov eax, [password2]
+ call PrintInt
+
+ ; new linhe
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, newline
+ mov rdx, 1
+ syscall
+
+ mov rax, SYS_CLOSE
+ mov rdi, r12
+ syscall
+ mov rax, SYS_EXIT
+ xor rdi, rdi
+ syscall
+
+; Handle one line: RSI -> "L123" or "R45"
+HandleLine:
+ 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 ParseInt ; EAX = steps
+ mov ecx, eax
+
+; Part 1
+P1_Start:
+ mov eax, ecx
+ imul eax, ebx
+ add eax, [angle1]
+ call NormalizeAngle
+ 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 UpdateAngleCount
+ mov [angle2], eax
+
+ jmp .loop
+P2_Exit:
+ ret
+
+; normalize eax to 0..99
+NormalizeAngle:
+ cmp eax, 0
+ jl .add
+ cmp eax, 100
+ jl .done
+ sub eax, 100
+ jmp NormalizeAngle
+.add:
+ add eax, 100
+ jmp NormalizeAngle
+.done:
+ ret
+
+; eax = angle, ebx = direction, rdi = password counter addr
+UpdateAngleCount:
+ add eax, ebx
+ call NormalizeAngle
+
+ cmp eax, 0
+ jne .done
+ mov edx, [rdi]
+ inc edx
+ mov [rdi], edx
+.done:
+ ret
+
+; parse ASCII integer into EAX
+ParseInt:
+ xor eax, eax ; result = 0
+.parseLoop:
+ 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 .parseLoop
+.done:
+ ret
+
+PrintInt:
+ mov edi, numberBuffer+15 ; write digits from the end
+ mov byte [rdi], 0 ; terminator
+ dec rdi
+
+ cmp eax, 0
+ jne .convert
+ mov byte [rdi], '0'
+ jmp .finish
+.convert:
+ ; we will repeatedly compute:
+ ; q = eax / 10
+ ; r = eax % 10
+ ; using only subtraction
+.nextDigit:
+ mov edx, eax ; tmp = current value
+ mov ecx, 0 ; q = 0
+
+ cmp edx, 10 ; if < 0, q=0, r=edx
+ jb .lastDigit
+.subLoop:
+ sub edx, 10
+ inc ecx ; q++
+ cmp edx, 10
+ jae .subLoop ; while tmp >= 10
+.lastDigit:
+ ; now:
+ ; ecx = quotient
+ ; edx = remainder (0..9)
+ add dl, '0'
+ mov [rdi], dl ; store digit
+ dec rdi
+ mov eax, ecx ; eax = quotient
+ test eax, eax
+ jnz .nextDigit ; more digits to extract?
+
+ inc rdi ; move to first digit
+.finish:
+ mov rax, SYS_WRITE
+ mov rsi, rdi ; buffer pointer
+ mov rdi, STDOUT
+ mov rdx, numberBuffer+15
+ sub rdx, rsi ; length
+ syscall
+ ret
+
+PrintString:
+ push rcx
+ push rdx
+ mov rdx, 0
+.count:
+ cmp byte [rcx+rdx], 0
+ je .doneCount
+ inc rdx
+ jmp .count
+.doneCount:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, rcx
+ syscall
+
+ pop rdx
+ pop rcx
+ ret
diff --git a/aoc-2025/day2.asm b/aoc-2025/day2.asm
new file mode 100644
index 0000000..dfbfb38
--- /dev/null
+++ b/aoc-2025/day2.asm
@@ -0,0 +1,410 @@
+; Advent of Code 2025 – Day 2
+
+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
+
+section .data
+ inputPath db "input/day2.txt", 0
+ newline db 10
+
+ label1 db "Part 1: ", 0
+ label2 db "Part 2: ", 0
+
+section .bss
+ buffer resb 4096
+ lineBuffer resb 65536
+ lineLen resd 1
+
+ sum1 resq 1
+ sum2 resq 1
+
+ numberBuffer resb 32 ; for printing and NumberToString
+
+section .text
+
+_start:
+ mov rax, SYS_OPEN
+ mov rdi, inputPath
+ mov rsi, O_RDONLY
+ xor rdx, rdx
+ syscall
+
+ mov r12, rax ; file descriptor
+ mov dword [lineLen], 0
+
+ReadLoop:
+ 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
+
+ScanChunk:
+ cmp rbp, 0
+ je ReadLoop
+
+ mov al, [rdi]
+
+ cmp al, 13
+ je SkipCR
+
+ cmp al, 10
+ je EndLine
+
+ mov edx, [lineLen]
+ cmp edx, 65535
+ jae SkipStore
+ mov [lineBuffer+rdx], al
+ inc edx
+ mov [lineLen], edx
+
+SkipStore:
+ inc rdi
+ dec rbp
+ jmp ScanChunk
+
+SkipCR:
+ inc rdi
+ dec rbp
+ jmp ScanChunk
+
+EndLine:
+ mov edx, [lineLen]
+ mov byte [lineBuffer+rdx], 0
+
+ cmp edx, 0
+ je ClearLine
+
+ push rdi
+ mov rsi, lineBuffer
+ call HandleLine
+ pop rdi
+
+ClearLine:
+ mov dword [lineLen], 0
+ inc rdi
+ dec rbp
+ jmp ScanChunk
+
+EOF:
+ ; process final partial line if needed
+ mov edx, [lineLen]
+ cmp edx, 0
+ jle PrintResults
+
+ mov byte [lineBuffer+rdx], 0
+ mov rsi, lineBuffer
+ call HandleLine
+
+PrintResults:
+ mov rcx, label1
+ call PrintString
+ mov rax, [sum1]
+ call PrintInt64
+ call Newline
+
+ mov rcx, label2
+ call PrintString
+ mov rax, [sum2]
+ call PrintInt64
+ call 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
+HandleLine:
+NextRange:
+ ; parse start
+ call ParseInt64
+ mov r13, rax
+
+ cmp byte [rsi], '-'
+ jne DoneLine
+ inc rsi
+
+ ; parse end
+ call ParseInt64
+ mov r14, rax
+
+RangeLoop:
+ cmp r13, r14
+ jg AfterRange
+
+ ; Save RSI (line pointer) across the checks,
+ ; because NumberToString / CheckInvalid* use RSI
+ push rsi
+
+ ; Part 1: exact AA
+ mov rax, r13
+ call CheckInvalid1
+ test rax, rax
+ jz .skipP1
+ add qword [sum1], r13
+.skipP1:
+ ; Part 2: any repeating block >= 2
+ mov rax, r13
+ call CheckInvalid2
+ test rax, rax
+ jz .skipP2
+ add qword [sum2], r13
+.skipP2:
+ pop rsi ; restore line pointer
+
+ inc r13
+ jmp RangeLoop
+
+AfterRange:
+ cmp byte [rsi], ','
+ jne DoneLine
+ inc rsi
+ jmp NextRange
+
+DoneLine:
+ ret
+
+; RSI points at decimal digits; stops on first non-digit
+ParseInt64:
+ xor rax, rax
+.parse:
+ 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 .parse
+.done:
+ ret
+
+; NumberToString: 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)
+ dec rdi
+
+ cmp rax, 0
+ jne .conv
+
+ ; handle zero
+ mov byte [rdi], '0'
+ mov rsi, rdi
+ mov ecx, 1
+ ret
+
+.conv:
+ mov rbx, 10
+.nextDigit:
+ xor rdx, rdx
+ div rbx ; RAX = quotient, RDX = remainder
+ add dl, '0'
+ mov [rdi], dl
+ dec rdi
+ test rax, rax
+ jnz .nextDigit
+
+ inc rdi ; now RDI points to first digit
+ mov rsi, rdi
+ mov rdx, numberBuffer+31
+ sub rdx, rsi ; RDX = length
+ mov ecx, edx
+ ret
+
+; CheckInvalid1: "AA" pattern
+; RAX = number
+; Returns RAX = 1 if digits == A A, else 0
+CheckInvalid1:
+ push rbx
+ push rdx
+
+ call NumberToString ; 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
+
+; CheckInvalid2: any repeating block >= 2
+; RAX = number
+; Returns RAX = 1 if string is some A repeated k>=2 times
+CheckInvalid2:
+ push rbx
+ push rdx
+
+ call NumberToString ; 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
+
+Newline:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, newline
+ mov rdx, 1
+ syscall
+ ret
+
+PrintInt64:
+ mov rdi, numberBuffer+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, numberBuffer+31
+ sub rdx, rsi
+ syscall
+ ret
+
+PrintString:
+ 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/day3.asm b/aoc-2025/day3.asm
new file mode 100644
index 0000000..e547475
--- /dev/null
+++ b/aoc-2025/day3.asm
@@ -0,0 +1,301 @@
+; Advent of Code 2025 - Day 3
+
+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
+
+section .data
+ inputPath db "input/day3.txt", 0
+
+ newline db 10
+
+ label1 db "Part 1: ", 0
+ label2 db "Part 2: ", 0
+
+section .bss
+ buffer resb 4096
+ lineBuffer resb 65536
+ lineLen resd 1
+
+ sum1 resq 1
+ sum2 resq 1
+
+ workDigits resb 64 ; stack for greedy subsequence
+ numberBuffer resb 32 ; for printing
+
+section .text
+
+_start:
+ mov rax, SYS_OPEN
+ mov rdi, inputPath
+ mov rsi, O_RDONLY
+ xor rdx, rdx
+ syscall
+
+ mov r12, rax ; fd
+ mov dword [lineLen], 0
+
+ReadLoop:
+ 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
+
+ScanChunk:
+ cmp rbp, 0
+ je ReadLoop
+
+ mov al, [rdi]
+
+ cmp al, 13
+ je SkipCR
+
+ cmp al, 10
+ je EndLine
+
+ mov edx, [lineLen]
+ cmp edx, 65535
+ jae SkipStore
+ mov [lineBuffer+rdx], al
+ inc edx
+ mov [lineLen], edx
+
+SkipStore:
+ inc rdi
+ dec rbp
+ jmp ScanChunk
+
+SkipCR:
+ inc rdi
+ dec rbp
+ jmp ScanChunk
+
+EndLine:
+ mov edx, [lineLen]
+ mov byte [lineBuffer+edx], 0
+
+ cmp edx, 0
+ je ClearLine
+
+ push rdi
+ mov rsi, lineBuffer
+ call HandleLine
+ pop rdi
+
+ClearLine:
+ mov dword [lineLen], 0
+ inc rdi
+ dec rbp
+ jmp ScanChunk
+
+EOF:
+ ; possible last partial line
+ mov edx, [lineLen]
+ cmp edx, 0
+ jle PrintResults
+
+ mov byte [lineBuffer+edx], 0
+ mov rsi, lineBuffer
+ call HandleLine
+
+PrintResults:
+ mov rcx, label1
+ call PrintString
+ mov rax, [sum1]
+ call PrintInt64
+ call Newline
+
+ mov rcx, label2
+ call PrintString
+ mov rax, [sum2]
+ call PrintInt64
+ call Newline
+
+ mov rax, SYS_CLOSE
+ mov rdi, r12
+ syscall
+
+ mov rax, SYS_EXIT
+ xor rdi, rdi
+ syscall
+
+HandleLine:
+ mov rdi, rsi
+ xor ecx, ecx ; ECX = 0
+.lengthLoop:
+ mov al, [rdi+rcx]
+ cmp al, 0
+ je .lengthDone
+ inc ecx
+ jmp .lengthLoop
+.lengthDone:
+ 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
+ add qword [sum1], rax
+
+ ; part 2: k = 12, only if n >= 12
+ cmp r15d, 12
+ jl .skipPart2
+
+ mov ecx, r15d
+ mov edi, 12
+ call GetMaxForK ; RAX = best 12-digit value
+ add qword [sum2], rax
+
+.skipPart2:
+ 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
+GetMaxForK:
+ ; dropsRemaining = n - k
+ mov eax, ecx
+ sub eax, edi
+ mov r8d, eax ; r8d = drops
+
+ xor r9d, r9d ; r9d = stackSize
+ 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, [workDigits + r11]
+
+ cmp bl, al
+ jge .push
+
+ dec r9d
+ dec r8d
+ jmp .pop
+.push:
+ mov r11d, r9d
+ mov [workDigits + r11], al
+ inc r9d
+ inc r10d
+ jmp .loop
+
+.after_input:
+ ; trim if stackSize > k
+.trim:
+ cmp r9d, edi
+ jle .convert
+ dec r9d
+ jmp .trim
+.convert:
+ xor rax, rax
+ xor r11d, r11d
+.convertLoop:
+ cmp r11d, edi
+ jge .done
+
+ mov rdx, r11
+ mov dl, [workDigits + rdx]
+ sub dl, '0'
+ movzx rdx, dl
+
+ imul rax, rax, 10
+ add rax, rdx
+
+ inc r11d
+ jmp .convertLoop
+.done:
+ ret
+
+Newline:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, newline
+ mov rdx, 1
+ syscall
+ ret
+
+PrintInt64:
+ mov rdi, numberBuffer+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, numberBuffer+31
+ sub rdx, rsi
+ syscall
+ ret
+
+PrintString:
+ 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/day4.asm b/aoc-2025/day4.asm
new file mode 100644
index 0000000..475f2ef
--- /dev/null
+++ b/aoc-2025/day4.asm
@@ -0,0 +1,25 @@
+bits 64
+default rel
+
+global _start
+
+%define SYS_EXIT 60
+%define SYS_WRITE 1
+
+%define STDOUT 1
+
+section .data
+ message db "Hello Day 4!", 0xA
+ length equ $-message
+
+section .text
+_start:
+ mov rax, SYS_WRITE
+ mov rdi, STDOUT
+ mov rsi, message
+ mov rdx, length
+ syscall
+
+ mov rax, SYS_EXIT
+ xor rdi, rdi
+ syscall