## Copyright (C) 2019 Jeremiah Orians
## Copyright (C) 2020 Sanne Wouda
## This file is part of stage0.
##
## stage0 is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## stage0 is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with stage0.  If not, see <http://www.gnu.org/licenses/>.

DEFINE SET_X0_FROM_X13 e0030daa
DEFINE SET_X0_FROM_X14 e0030eaa
DEFINE SET_X0_FROM_X15 e0030faa
DEFINE SET_X1_FROM_X14 e1030eaa
DEFINE SET_X2_FROM_X0  e20300aa
DEFINE SET_X13_FROM_X0 ed0300aa
DEFINE SET_X14_FROM_X0 ee0300aa
DEFINE SET_X15_FROM_X0 ef0300aa
DEFINE SET_X4_FROM_SP  e4030091

DEFINE SET_X0_TO_0 000080d2
DEFINE SET_X0_TO_FCNTL_H_AT_FDCWD 600c8092
DEFINE SET_X2_TO_0 020080d2
DEFINE SET_X2_TO_577 224880d2
DEFINE SET_X2_TO_0x100000 0202a0d2
DEFINE SET_X3_TO_0 030080d2
DEFINE SET_X3_TO_384 033080d2

DEFINE LDR_X1_[X4,8]_WB  818c40f8
DEFINE LDR_X1_[SP,16]_WB e10f41f8

DEFINE CMP_X0_TO_0x100000 1f0044f1
DEFINE CMP_X1_TO_0 3f0000f1
DEFINE SKIP_INST_NE 41000054

DEFINE ADD_X0_X0_0x100000 00004491

DEFINE SET_X8_TO_SYS_BRK c81a80d2
DEFINE SET_X8_TO_SYS_EXIT a80b80d2
DEFINE SET_X8_TO_SYS_OPENAT 080780d2
DEFINE SET_X8_TO_SYS_READ e80780d2
DEFINE SET_X8_TO_SYS_WRITE 080880d2
DEFINE SYSCALL 010000d4

DEFINE FBRANCH 14
DEFINE RBRANCH 17

    # Register usage:
    # X15 => OUTPUT
    # X14 => Buffer
    # X13 => INPUT

# Where the ELF Header is going to hit
# Simply jump to _start
# Our main function
:_start
    LDR_X1_[SP,16]_WB           # Get the actual output name
    SET_X0_TO_FCNTL_H_AT_FDCWD  # AT_FDCWD, relative to current working directory
    SET_X2_TO_577               # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    SET_X3_TO_384               # Prepare file as RW for owner only (600 in octal)
    SET_X8_TO_SYS_OPENAT        # The syscall number for openat(), aarch64 has no open()
    SYSCALL                     # Now open that file
    SET_X15_FROM_X0             # Preserve the file pointer we were given

    SET_X8_TO_SYS_BRK           # the Syscall # for SYS_BRK
    SET_X0_TO_0                 # Get current brk
    SYSCALL                     # Let the kernel do the work
    SET_X14_FROM_X0             # Set our malloc pointer

    SET_X8_TO_SYS_BRK           # the Syscall # for SYS_BRK
    SET_X0_FROM_X14             # Using current pointer
    ADD_X0_X0_0x100000          # Allocate 1MB
    SYSCALL                     # Let the kernel do the work

    SET_X4_FROM_SP
:core
    LDR_X1_[X4,8]_WB            # Get the actual input name
    CMP_X1_TO_0                 # Check for null string
    SKIP_INST_NE                # Hit null be done
    ^~done FBRANCH

    SET_X0_TO_FCNTL_H_AT_FDCWD  # AT_FDCWD, relative to current working directory
    SET_X2_TO_0                 # prepare read_only
    SET_X3_TO_0                 # prevent any interactions
    SET_X8_TO_SYS_OPENAT        # The syscall number for openat(), aarch64 has no open()
    SYSCALL                     # Open file!
    SET_X13_FROM_X0             # Protect INPUT
:keep
    SET_X2_TO_0x100000          # set the size of chars we want
    SET_X1_FROM_X14             # Where to put it
    SET_X0_FROM_X13             # Where are we reading from
    SET_X8_TO_SYS_READ          # the syscall number for read
    SYSCALL                     # call the Kernel

    SET_X2_FROM_X0              # Number of bytes to write
    SET_X1_FROM_X14             # What we are writing
    SET_X0_FROM_X15             # Write to target file
    SET_X8_TO_SYS_WRITE         # the syscall number for write
    SYSCALL                     # call the Kernel

    CMP_X0_TO_0x100000          # Check if buffer was fully used
    SKIP_INST_NE                # Keep looping if was full
    ^~keep RBRANCH
    ^~core RBRANCH              # Otherwise move to next file

:done
    # program completed Successfully
    SET_X0_TO_0                 # All is well
    SET_X8_TO_SYS_EXIT          # put the exit syscall number in x8
    SYSCALL                     # Call it a good day

:ELF_end
