hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/aoc-2025/day4.asm
blob: 57867b1fe29657d9757b4624f62d95fe723d95a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
; Advent of Code 2025 - Day 4

bits 64
default rel

global _start

%define SYS_EXIT   60
%define SYS_READ   0
%define SYS_WRITE  1
%define SYS_OPEN   2
%define SYS_CLOSE  3

%define O_RDONLY   0
%define STDOUT     1

%define RAW_SIZE   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

    newline    db 10

    label1     db "Part 1: ", 0
    label2     db "Part 2: ", 0

section .bss
    raw           resb RAW_SIZE   ; raw file bytes
    grid          resb GRID_SIZE  ; padded grid: 1 = roll, 0 = empty
    mark          resb GRID_SIZE  ; per-round removal marks

    raw_len       resq 1
    width         resq 1  ; real columns per row
    height        resq 1  ; real rows
    stride        resq 1  ; width + 2 (padding on both sides)

    part1         resq 1
    part2         resq 1

    number_buffer resb 32  ; for printing

section .text

_start:
    mov     rax, SYS_OPEN
    mov     rdi, input_path
    mov     rsi, O_RDONLY
    xor     rdx, rdx
    syscall
    mov     r12, rax  ; fd

    xor     r13, r13  ; bytes read so far
read_loop:
    mov     rax, SYS_READ
    mov     rdi, r12
    lea     rsi, [raw + r13]
    mov     rdx, RAW_SIZE
    sub     rdx, r13  ; remaining capacity
    syscall

    cmp     rax, 0
    jle     read_done
    add     r13, rax
    jmp     read_loop
read_done:
    mov     [raw_len], r13

    mov     rax, SYS_CLOSE
    mov     rdi, r12
    syscall

    xor     rcx, rcx  ; column scanner
width_scan:
    cmp     rcx, [raw_len]
    jge     width_done
    mov     al, [raw + rcx]
    cmp     al, 13  ; CR
    je      width_done
    cmp     al, 10  ; LF
    je      width_done
    inc     rcx
    jmp     width_scan
width_done:
    mov     [width], rcx
    add     rcx, 2  ; padding on left + right
    mov     [stride], rcx

    ; real cell (r, c) lives at grid[(r+1)*stride + (c+1)]
    mov     r10, [raw_len]
    xor     r8, r8    ; i  (raw index)
    xor     r9, r9    ; r  (row, 0-based)
    xor     r11, r11  ; c  (col, 0-based)
fill_loop:
    cmp     r8, r10
    jge     fill_end
    mov     al, [raw + r8]
    inc     r8

    cmp     al, 13  ; CR -> ignore
    je      fill_loop
    cmp     al, 10  ; LF -> end of line
    je      fill_newline

    cmp     al, '@'
    jne     fill_next_col  ; '.' leaves the zeroed cell alone

    ; grid[(r9+1)*stride + (r11+1)] = 1
    mov     rbx, r9
    inc     rbx
    imul    rbx, [stride]
    add     rbx, r11
    add     rbx, 1
    mov     byte [grid + rbx], 1
fill_next_col:
    inc     r11
    jmp     fill_loop
fill_newline:
    cmp     r11, 0  ; blank line -> skip (e.g. trailing newline)
    je      fill_loop
    inc     r9
    xor     r11, r11
    jmp     fill_loop
fill_end:
    cmp     r11, 0  ; last line had no trailing newline
    je      fill_height
    inc     r9
fill_height:
    mov     [height], r9

    mov     r13, 1  ; row = 1..height
p1_row:
    cmp     r13, [height]
    jg      p1_done
    mov     rbx, r13
    imul    rbx, [stride]
    inc     rbx     ; idx of (row, col=1)
    mov     r12, 1  ; col = 1..width
p1_col:
    cmp     r12, [width]
    jg      p1_row_next
    movzx   eax, byte [grid + rbx]
    test    eax, eax
    jz      p1_next  ; not a roll
    mov     rdi, rbx
    call    count_neighbors
    cmp     rax, 4
    jge     p1_next  ; 4 or more neighbors -> blocked
    inc     qword [part1]
p1_next:
    inc     rbx
    inc     r12
    jmp     p1_col
p1_row_next:
    inc     r13
    jmp     p1_row
p1_done:

peel_loop:
    mov     r13, 1
mark_row:
    cmp     r13, [height]
    jg      mark_done
    mov     rbx, r13
    imul    rbx, [stride]
    inc     rbx
    mov     r12, 1
mark_col:
    cmp     r12, [width]
    jg      mark_row_next
    movzx   eax, byte [grid + rbx]
    test    eax, eax
    jz      mark_zero  ; empty cell
    mov     rdi, rbx
    call    count_neighbors
    cmp     rax, 4
    jl      mark_one  ; accessible
mark_zero:
    mov     byte [mark + rbx], 0
    jmp     mark_next
mark_one:
    mov     byte [mark + rbx], 1
mark_next:
    inc     rbx
    inc     r12
    jmp     mark_col
mark_row_next:
    inc     r13
    jmp     mark_row
mark_done:

    xor     r11, r11  ; removed this round
    mov     r13, 1
apply_row:
    cmp     r13, [height]
    jg      apply_done
    mov     rbx, r13
    imul    rbx, [stride]
    inc     rbx
    mov     r12, 1
apply_col:
    cmp     r12, [width]
    jg      apply_row_next
    movzx   eax, byte [mark + rbx]
    test    eax, eax
    jz      apply_next
    mov     byte [grid + rbx], 0
    inc     r11
apply_next:
    inc     rbx
    inc     r12
    jmp     apply_col
apply_row_next:
    inc     r13
    jmp     apply_row
apply_done:
    add     [part2], r11
    test    r11, r11
    jnz     peel_loop  ; something changed -> another round

print_results:
    mov     rcx, label1
    call    print_string
    mov     rax, [part1]
    call    print_int64
    call    print_newline

    mov     rcx, label2
    call    print_string
    mov     rax, [part2]
    call    print_int64
    call    print_newline

    mov     rax, SYS_EXIT
    xor     rdi, rdi
    syscall

; count_neighbors: sum of the 8 cells around a padded index
;   Input:  RDI = grid index
;   Output: RAX = number of neighboring rolls (0..8)
;   Clobbers: RAX, RCX, RDX, R8
count_neighbors:
    mov     rcx, [stride]
    xor     eax, eax

    mov     rdx, rdi
    sub     rdx, rcx  ; row above
    movzx   r8d, byte [grid + rdx - 1]
    add     eax, r8d
    movzx   r8d, byte [grid + rdx]
    add     eax, r8d
    movzx   r8d, byte [grid + rdx + 1]
    add     eax, r8d

    movzx   r8d, byte [grid + rdi - 1]  ; same row, sides
    add     eax, r8d
    movzx   r8d, byte [grid + rdi + 1]
    add     eax, r8d

    mov     rdx, rdi
    add     rdx, rcx  ; row below
    movzx   r8d, byte [grid + rdx - 1]
    add     eax, r8d
    movzx   r8d, byte [grid + rdx]
    add     eax, r8d
    movzx   r8d, byte [grid + rdx + 1]
    add     eax, r8d
    ret

print_newline:
    mov     rax, SYS_WRITE
    mov     rdi, STDOUT
    mov     rsi, newline
    mov     rdx, 1
    syscall
    ret

print_int64:
    mov     rdi, number_buffer+31
    mov     byte [rdi], 0
    dec     rdi

    cmp     rax, 0
    jne     .conv

    mov     byte [rdi], '0'
    jmp     .done
.conv:
    mov     rbx, 10
.next:
    xor     rdx, rdx
    div     rbx
    add     dl, '0'
    mov     [rdi], dl
    dec     rdi
    test    rax, rax
    jnz     .next

    inc     rdi
.done:
    mov     rax, SYS_WRITE
    mov     rsi, rdi
    mov     rdi, STDOUT
    mov     rdx, number_buffer+31
    sub     rdx, rsi
    syscall
    ret

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