## Copyright (C) 2016 Jeremiah Orians
## Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
## 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/>.

## ELF Header

#:ELF_base
7F 45 4C 46        ## e_ident[EI_MAG0-3] ELF's magic number

02                 ## e_ident[EI_CLASS] Indicating 64 bit
01                 ## e_ident[EI_DATA] Indicating little endianness
01                 ## e_ident[EI_VERSION] Indicating original elf

03                 ## e_ident[EI_OSABI] Set at 0 because none cares
00                 ## e_ident[EI_ABIVERSION] See above

00 00 00 00 00 00 00 ## e_ident[EI_PAD]
02 00              ## e_type Indicating Executable
B7 00              ## e_machine Indicating AArch64
01 00 00 00        ## e_version Indicating original elf

78 00 60 00 00 00 00 00 ## e_entry Address of the entry point (Number of bytes this header is + Base Address)
40 00 00 00 00 00 00 00 ## e_phoff Address of program header table
00 00 00 00 00 00 00 00 ## e_shoff Address of section header table

00 00 00 00        ## e_flags
40 00              ## e_ehsize Indicating our 64 Byte header

38 00              ## e_phentsize size of a program header table
01 00              ## e_phnum number of entries in program table

00 00              ## e_shentsize size of a section header table
00 00              ## e_shnum number of entries in section table

00 00              ## e_shstrndx index of the section names

## Program Header
#:ELF_program_headers
01 00 00 00             ## p_type
07 00 00 00             ## Flags
00 00 00 00 00 00 00 00 ## p_offset

00 00 60 00 00 00 00 00 ## p_vaddr
00 00 60 00 00 00 00 00 ## p_physaddr

0E 02 00 00 00 00 00 00 ## p_filesz
0E 02 00 00 00 00 00 00 ## p_memsz

01 00 00 00 00 00 00 00 ## Required alignment

#:ELF_text

# Where the ELF Header is going to hit
# Simply jump to _start
# Our main function
#:_start
    E10B40F9                    ; LDR_X1_[SP,16]              # Get the actual input name
    600C8092                    ; SET_X0_TO_FCNTL_H_AT_FDCWD  # AT_FDCWD, relative to current working directory
    020080D2                    ; SET_X2_TO_0                 # Prepare file as read only
    080780D2                    ; SET_X8_TO_SYS_OPENAT        # The syscall number for openat(), aarch64 has no open()
    010000d4                    ; SYSCALL                     # Open file!
    E90300AA                    ; SET_X9_FROM_X0              # Preserve the file pointer we were given

    E10F40F9                    ; LDR_X1_[SP,24]              # Get the output name
    600C8092                    ; SET_X0_TO_FCNTL_H_AT_FDCWD  # AT_FDCWD, relative to current working directory
    224880D2                    ; SET_X2_TO_577               # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    033880D2                    ; SET_X3_TO_448               # Prepare file as RWX for owner only (700 in octal)
    080780D2                    ; SET_X8_TO_SYS_OPENAT        # The syscall number for openat(), aarch64 has no open()
    010000D4                    ; SYSCALL                     # Open file!
    EA0300AA                    ; SET_X10_FROM_X0             # Preserve the file pointer we were given

    # Our flag for byte processing
    0F008092                    ; SET_X15_TO_MINUS_1

    # temp storage for the sum
    0E0080D2                    ; SET_X14_TO_0

#:loop
    # Read a byte
    470000 94                   ; ^~Read_byte CALL

    # process byte
    120000 94                   ; ^~hex CALL

    # Deal with -1 values
    1F0000F1                    ; CMP_X0_TO_0
    4A000054                    ; SKIP_INST_GE
    FCFFFF 17                   ; ^~loop RBRANCH

    # deal with toggle
    FF0100F1                    ; CMP_X15_TO_0
    4B000054                    ; SKIP_INST_LT
    040000 14                   ; ^~print BRANCH

    # process first byte of pair
    EE0300AA                    ; SET_X14_FROM_X0
    0F0080D2                    ; SET_X15_TO_0

    F6FFFF 17                   ; ^~loop RBRANCH

# process second byte of pair
#:print
    # update the sum and store in output
    00100E8B                    ; ADD_X0_X0_X14_LSL4
    41000018                    ; LOAD_W1_AHEAD
    02000014                    ; SKIP_32_DATA
    0C02 6000                   ; $output DATA_OFFSET 40 00
    20000039                    ; STR_BYTE_W0_[X1]

    # flip the toggle
    0F008092                    ; SET_X15_TO_MINUS_1

    2E0000 94                   ; ^~write_byte CALL

    EEFFFF 17                   ; ^~loop RBRANCH

#:hex
    # Purge Comment Lines (#)
    1F8C00F1                    ; CMP_X0_TO_35
    41000054                    ; SKIP_INST_NE
    170000 14                   ; ^~purge_comment BRANCH

    # Purge Comment Lines (;)
    1FEC00F1                    ; CMP_X0_TO_59
    41000054                    ; SKIP_INST_NE
    140000 14                   ; ^~purge_comment BRANCH

    # deal all ascii less than 0
    1FC000F1                    ; CMP_X0_TO_48
    4A000054                    ; SKIP_INST_GE
    1F0000 14                   ; ^~ascii_other BRANCH

    # deal with 0-9
    1FE800F1                    ; CMP_X0_TO_58
    4A000054                    ; SKIP_INST_GE
    160000 14                   ; ^~ascii_num BRANCH

    # deal with all ascii less than A
    1F0401F1                    ; CMP_X0_TO_65
    4A000054                    ; SKIP_INST_GE
    190000 14                   ; ^~ascii_other BRANCH

    # deal with A-F
    1F1C01F1                    ; CMP_X0_TO_71
    4A000054                    ; SKIP_INST_GE
    140000 14                   ; ^~ascii_high BRANCH

    # deal with all ascii less than a
    1F8401F1                    ; CMP_X0_TO_97
    4A000054                    ; SKIP_INST_GE
    130000 14                   ; ^~ascii_other BRANCH

    # deal with a-f
    1F9C01F1                    ; CMP_X0_TO_103
    4A000054                    ; SKIP_INST_GE
    0C0000 14                   ; ^~ascii_low BRANCH

    # The rest that remains needs to be ignored
    0F0000 14                   ; ^~ascii_other BRANCH

#:purge_comment
    FE0F1FF8                    ; PUSH_LR                     # push lr

#:purge_comment_loop
    # Read a byte
    1A0000 94                   ; ^~Read_byte CALL

    # Loop if not LF
    1F2800F1                    ; CMP_X0_TO_10
    40000054                    ; SKIP_INST_EQ
    FDFFFF 17                   ; ^~purge_comment_loop RBRANCH

    # Otherwise return -1
    00008092                    ; SET_X0_TO_MINUS_1

    FE0741F8                    ; POP_LR                      # pop lr
    C0035FD6                    ; RETURN

#:ascii_num
    00C000D1                    ; SUB_X0_48
    C0035FD6                    ; RETURN

#:ascii_low
    005C01D1                    ; SUB_X0_87
    C0035FD6                    ; RETURN

#:ascii_high
    00DC00D1                    ; SUB_X0_55
    C0035FD6                    ; RETURN

#:ascii_other
    00008092                    ; SET_X0_TO_MINUS_1
    C0035FD6                    ; RETURN

#:Done
    # program completed Successfully
    000080D2                    ; SET_X0_TO_0                 # All is well
    A80B80D2                    ; SET_X8_TO_SYS_EXIT          # put the exit syscall number in x8
    010000d4                    ; SYSCALL

#:write_byte
    # Print our Hex
    E0030AAA                    ; SET_X0_FROM_X10             # Where are we writing to
    41000018                    ; LOAD_W1_AHEAD               # What we are writing
    02000014                    ; SKIP_32_DATA
    0C02 6000                   ; $output DATA_OFFSET
    220080D2                    ; SET_X2_TO_1                 # set the size of chars we want
    080880D2                    ; SET_X8_TO_SYS_WRITE         # the syscall number for write
    010000d4                    ; SYSCALL                     # call the Kernel
    C0035FD6                    ; RETURN

#:Read_byte
    # Attempt to read 1 byte from input file
    E00309AA                    ; SET_X0_FROM_X9              # Where are we reading from
    41000018                    ; LOAD_W1_AHEAD               # Where to put it
    02000014                    ; SKIP_32_DATA
    0D02 6000                   ; $input DATA_OFFSET
    220080D2                    ; SET_X2_TO_1                 # set the size of chars we want
    E80780D2                    ; SET_X8_TO_SYS_READ          # the syscall number for read
    010000D4                    ; SYSCALL                     # call the Kernel

    1F0000F1                    ; CMP_X0_TO_0                 # Got EOF call it done
    41000054                    ; SKIP_INST_NE
    ECFFFF 17                   ; ^~Done RBRANCH

    # load byte
    40000098                    ; LOAD_W0_AHEAD
    02000014                    ; SKIP_32_DATA
    0D02 6000                   ; $input DATA_OFFSET
    00004039                    ; LDR_BYTE_W0_[X0]            # load char, unsigned so zero-extended already
    C0035FD6                    ; RETURN

#:ELF_data
# Where we are putting our output
#:output
    # Reserve a byte
    00

# Where we get our input
#:input
    # Reserve a byte
    00

#:ELF_end
