;==============================================================================
; hashline64.asm - x86_64 NASM Implementation
; Autor des Original-C-Codes: reinhard@finalmedia.de
; Lizenz: PUBLIC DOMAIN
;
; Kompilieren und Linken:
;   nasm -f elf64 hashline64.asm -o hashline64.o
;   ld -nostdlib -static -o hashline64 hashline64.o
;==============================================================================

global _start

%define BUFSIZE 65536

section .bss
    out_buf:    resb BUFSIZE
    out_pos:    resd 1
    in_buf:     resb BUFSIZE

section .rodata
    hex_chars:  db "0123456789abcdef"
    space_char: db " "
    nl_char:    db 10

section .text

;------------------------------------------------------------------------------
; uint64 fnv1a_64(const char *rdi, int rsi)
;------------------------------------------------------------------------------
fnv1a_64:
    mov rax, 14695981039346656037
    mov r8, 1099511628211
    test rsi, rsi
    jle .done
.loop:
    movzx edx, byte [rdi]
    inc rdi
    xor rax, rdx
    mul r8
    dec rsi
    jnz .loop
.done:
    ret

;------------------------------------------------------------------------------
; void flush_out(void)
;------------------------------------------------------------------------------
flush_out:
    push rbx
    mov ecx, [out_pos]
    test ecx, ecx
    jz .done

    xor ebx, ebx                        ; pos = 0
.loop:
    cmp ebx, [out_pos]
    jge .reset_out_pos

    mov eax, 1                          ; sys_write
    mov edi, 1                          ; stdout
    lea rsi, [out_buf + rbx]            ; out_buf + pos
    mov edx, [out_pos]
    sub edx, ebx                        ; out_pos - pos
    syscall

    test rax, rax
    jle .error

    add ebx, eax                        ; pos += w
    jmp .loop

.reset_out_pos:
    mov dword [out_pos], 0
.done:
    pop rbx
    ret
.error:
    mov eax, 60                         ; sys_exit
    mov edi, 111                        ; Code 111
    syscall

;------------------------------------------------------------------------------
; void put_buffered(const char *rdi, int rsi)
; Input: RDI = s, RSI = len
;------------------------------------------------------------------------------
put_buffered:
    push rbx
    push rbp
    push r12

    mov rbx, rdi                        ; rbx = s
    mov rbp, rsi                        ; rbp = len

.loop:
    test rbp, rbp
    jle .done

    mov eax, BUFSIZE
    sub eax, [out_pos]                  ; space = BUFSIZE - out_pos
    jnz .has_space

    call flush_out
    mov eax, BUFSIZE                    ; nach flush ist space wieder BUFSIZE

.has_space:
    ; chunk = (len < space) ? len : space
    cmp rbp, rax
    cmovg r12, rax                      ; r12 = chunk (space)
    cmovle r12, rbp                     ; r12 = chunk (len)

    ; Kopierschleife: out_buf[out_pos++] = *s++
    mov edx, [out_pos]                  ; aktueller Offset im out_buf
    xor ecx, ecx                        ; Schleifenzähler i = 0
.copy_loop:
    cmp rcx, r12
    jge .copy_done
    mov al, [rbx + rcx]
    mov [out_buf + rdx], al
    inc rdx
    inc rcx
    jmp .copy_loop

.copy_done:
    mov [out_pos], edx                  ; out_pos aktualisieren
    add rbx, r12                        ; s += chunk
    sub rbp, r12                        ; len -= chunk
    jmp .loop

.done:
    pop r12
    pop rbp
    pop rbx
    ret

;------------------------------------------------------------------------------
; void put_hex64_buffered(uint64 rdi)
;------------------------------------------------------------------------------
put_hex64_buffered:
    sub rsp, 16
    mov rcx, 16
    lea r8, [hex_chars]
.loop:
    dec rcx
    mov rdx, rdi
    and rdx, 15
    mov al, [r8 + rdx]
    mov [rsp + rcx], al
    shr rdi, 4
    test rcx, rcx
    jnz .loop

    mov rdi, rsp
    mov rsi, 16
    call put_buffered
    add rsp, 16
    ret

;------------------------------------------------------------------------------
; main Entry Point
;------------------------------------------------------------------------------
_start:
    xor r12, r12                        ; rlen = 0
    xor r13, r13                        ; pos = 0
    lea r14, [in_buf]
    mov dword [out_pos], 0

.main_loop:
    cmp r13, r12
    jl .find_newline

    mov eax, 0                          ; sys_read
    mov edi, 0                          ; stdin
    mov rsi, r14
    mov rdx, BUFSIZE
    syscall

    test rax, rax
    jle .break_main_loop

    mov r12, rax                        ; rlen = n
    xor r13, r13                        ; pos = 0

.find_newline:
    mov r15, r13                        ; start = pos

.scan_loop:
    cmp r13, r12
    jge .check_newline
    mov al, [r14 + r13]
    cmp al, 10
    je .check_newline
    inc r13
    jmp .scan_loop

.check_newline:
    cmp r13, r12
    jge .no_newline
    mov al, [r14 + r13]
    cmp al, 10
    jne .no_newline

    inc r13                             ; pos++ (inklusive '\n')

    ; FNV-1a Hash berechnen
    lea rdi, [r14 + r15]
    mov rsi, r13
    sub rsi, r15
    call fnv1a_64

    ; Hexadezimalen Hash in den Puffer schreiben
    mov rdi, rax
    call put_hex64_buffered

    ; Leerzeichen schreiben
    lea rdi, [space_char]
    mov rsi, 1
    call put_buffered

    ; Originalzeile schreiben
    lea rdi, [r14 + r15]
    mov rsi, r13
    sub rsi, r15
    call put_buffered

    jmp .main_loop

.no_newline:
    mov rcx, r12
    sub rcx, r15                        ; left = rlen - start

    test rcx, rcx
    jle .read_more
    test r15, r15
    jle .read_more
    xor rdx, rdx

.copy_left:
    mov r8, r15
    add r8, rdx
    mov al, [r14 + r8]
    mov [r14 + rdx], al
    inc rdx
    cmp rdx, rcx
    jl .copy_left

.read_more:
    mov eax, 0                          ; sys_read
    mov edi, 0                          ; stdin
    lea rsi, [r14 + rcx]                ; in_buf + left
    mov rdx, BUFSIZE
    sub rdx, rcx
    push rcx                            ; left sichern
    syscall
    pop rcx                             ; left wiederherstellen

    test rax, rax
    jg .successful_read

    test rcx, rcx
    jle .break_main_loop

    ; FNV-1a für den Rest
    mov rdi, r14
    mov rsi, rcx
    push rcx
    call fnv1a_64

    mov rdi, rax
    call put_hex64_buffered

    lea rdi, [space_char]
    mov rsi, 1
    call put_buffered

    pop rcx
    mov rdi, r14
    mov rsi, rcx
    push rcx
    call put_buffered
    pop rcx

    dec rcx
    mov al, [r14 + rcx]
    cmp al, 10
    je .break_main_loop
    lea rdi, [nl_char]
    mov rsi, 1
    call put_buffered
    jmp .break_main_loop

.successful_read:
    add rcx, rax                        ; rlen = left + n
    mov r12, rcx
    xor r13, r13                        ; pos = 0
    jmp .main_loop

.break_main_loop:
    call flush_out

    mov eax, 60                         ; sys_exit
    xor edi, edi
    syscall

