segment .data
        align   8
infd    dq      0
outfd   dq      0
in_name dq      0
out_name:
        dq      0
in_size dq      0
data    dq      0
        segment .text
        global  main
        extern  open, malloc, lseek, read, write, close, printf
;       rcx/xmm0, rdx/xmm1, r8/xmm2, r9/xmm3 then stack
;       must had 4 quadwords free on the stack top before a call
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 32

;       Check the command line parameter count
        cmp     rcx, 3
        jne     .param_error

;       Save the input and output file names
        mov     r8, [rdx+8]
        mov     r9, [rdx+16]
        mov     [in_name], r8
        mov     [out_name], r9

;       Report the files being copied
        segment .data
.files_fmt:
        db      "copying %s to %s",0x0a,0
        segment .text
        lea     rcx, [.files_fmt]
        mov     rdx, [in_name]
        mov     r8, [out_name]
        xor     eax, eax
        call    printf

;       Try to open the input file
        mov     rcx, [in_name]
        xor     edx, edx
        call    open
        cmp     eax, 0
        jl      .input_open_failed
        mov     [infd], rax

;       Try to open the output file
        mov     rcx, [out_name]
        xor     edx, edx
        call    open
        cmp     eax, 0
        jge     .output_exists

;       Try to create the output file
        mov     rcx, [out_name]
        mov     edx, 0x8101      ;  0x8000 = O_BINARY
                                 ;  0x0100 = O_CREAT
                                 ;  0x0000 = O_RDONLY
                                 ;  0x0001 = O_WRONLY
                                 ;  0x0002 = O_RDWR
        mov     r8d, 700o
        call    open
        cmp     eax, 0
        jl      .output_open_failed
        mov     [outfd], rax

;       Determine the input file size
        mov     rcx, [infd]
        xor     edx, edx
        mov     r8d, 2
        call    lseek
        mov     [in_size], rax
        mov     rcx, [infd]
        xor     edx, edx
        mov     r8d, 0
        call    lseek

;       Allocate the data array
        mov     rcx, [in_size]
        call    malloc
        cmp     rax, 0
        je      .malloc_failed
        mov     [data], rax

;       Read the input file
        mov     rcx, [infd]
        mov     rdx, [data]
        mov     r8, [in_size]
        call    read

;       Write the output file
        mov     rcx, [outfd]
        mov     rdx, [data]
        mov     r8, [in_size]
        call    write

;       Close the input and output files
        mov     rcx, [infd]
        call    close
        mov     rcx, [outfd]
        call    close

        xor     eax, eax
        leave
        ret

        segment .data
.malloc_failed_fmt:
        db      "malloc failed for %ld bytes",0x0a,0
        segment .text
.malloc_failed:
        lea     rcx, [.malloc_failed_fmt]
        mov     rdx, [in_size]
        call    printf
        mov     eax, 1
        leave
        ret

        segment .data
.output_exist_fmt:
        db      "%s already exists",0x0a,0
        segment .text
.output_exists:
        lea     rcx, [.output_exist_fmt]
        mov     rdx, [out_name]
        call    printf
        mov     eax, 1
        leave
        ret

        segment .data
.output_open_fmt:
        db      "Could not open output_file %s",0x0a,0
        segment .text
.output_open_failed:
        lea     rcx, [.output_open_fmt]
        mov     rdx, [out_name]
        call    printf
        mov     eax, 1
        leave
        ret

        segment .data
.input_open_fmt:
        db      "Could not open input_file %s",0x0a,0
        segment .text
.input_open_failed:
        lea     rcx, [.input_open_fmt]
        mov     rdx, [in_name]
        call    printf
        mov     eax, 1
        leave
        ret

        segment .data
.param_fmt:
        db      "Usage: copy input_file output_file",0x0a,0
        segment .text
.param_error:
        lea     rcx, [.param_fmt]
        call    printf
        mov     eax, 1
        leave
        ret