## Copyright (C) 2017 Jeremiah Orians
## 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
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
00                              # 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
3E 00                           # e_machine Indicating AMD64
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 table
01 00 00 00                     # p_type
06 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 00 00 00 00 00 00         # Undefined
00 00 00 0F 00 00 00 00         # p_filesz
00 00 00 0F 00 00 00 00         # p_memsz
00 00 20 00 00 00 00 00         # Required alignment


:a # :_start
	58                          ; POP_RAX #·Get·the·number·of·arguments
	5F                          ; POP_RDI #·Get·the·program·name$
	5F                          ; POP_RDI #·Get·the·actual·input name
	48C7C6 00000000             ; LOADI32_RSI %0 #·prepare·read_only
	48C7C0 02000000             ; LOADI32_RAX %2 #·the·syscall·number·for·open()
	0F05                        ; SYSCALL # Now open that damn file
	4989C1                      ; COPY_RAX_to_R9 # Preserve the file pointer we were given

	49C7C7 FFFFFFFF             ; LOADI32_R15 %-1 # Our flag for byte processing
	49C7C6 00000000             ; LOADI32_R14 %0 # temp storage for the sum
	49C7C5 00000000             ; LOADI32_R13 %0 # Our starting IP
	E8 %b                       ; CALLI32 %First_pass # Process it

	# rewind input file
	4C89CF                      ; COPY_R9_to_RDI # Using our input file
	48C7C6 00000000             ; LOADI32_RSI %0 # Offset Zero
	48C7C2 00000000             ; LOADI32_RDX %0 # Whence Zero
	48C7C0 08000000             ; LOADI32_RAX %8 # lseek
	0F05                        ; SYSCALL

	49C7C7 FFFFFFFF             ; LOADI32_R15 %-1 # Our flag for byte processing
	49C7C6 00000000             ; LOADI32_R14 %0 # temp storage for the sum
	49C7C5 00000000             ; LOADI32_R13 %0 # Our starting IP
	E8 %h                       ; CALLI32 %Second_pass # Process it

	E9 %t                       ; JMP32 %Done

:b # :First_pass
	E8 %u                       ; CALLI32 %Read_byte

	# Deal with EOF
	483D FCFFFFFF               ; CMPI32_RAX %-4
	0F84 %f                     ; JE32 %First_pass_done

	# Check for :
	483D 3A000000               ; CMPI32_RAX %0x3a
	0F85 %c                     ; JNE32 %First_pass_0

	# Deal with label
	E8 %y                       ; CALLI32 %StoreLabel

:c # :First_pass_0
	# Check for %
	483D 25000000               ; CMPI32_RAX %0x25
	0F84 %e                     ; JE32 %First_pass_pointer

	# Deal with everything else
	E8 %g                       ; CALLI32 %hex # Process our char

	# Deal with EOF
	483D FCFFFFFF               ; CMPI32_RAX %-4
	0F84 %f                     ; JE32 %First_pass_done

	# deal with -1 values
	483D 00000000               ; CMPI32_RAX %0
	0F8C %b                     ; JL32 %First_pass

	# deal with toggle
	4981FF 00000000             ; CMPI32_R15 %0
	0F84 %d                     ; JE32 %First_pass_1
	4981C5 01000000             ; ADDI32_to_R13 %1 # Increment IP

:d # :First_pass_1
	49F7D7                      ; NOT_R15
	E9 %b                       ; JMP32 %First_pass

:e # :First_pass_pointer
	# Deal with Pointer to label
	E8 %u                       ; CALLI32 %Read_byte # Drop the char
	4981C5 04000000             ; ADDI32_to_R13 %4 # Increment IP
	E9 %b                       ; JMP32 %First_pass # Loop again

:f # :First_pass_done
	C3                          ; RET

:g # :hex
	# deal with EOF
	483D FCFFFFFF               ; CMPI32_RAX %-4
	0F84 %l                     ; JE32 %EOF
	# deal with line comments starting with #
	483D 23000000               ; CMPI32_RAX %0x23
	0F84 %q                     ; JE32 %ascii_comment
	# deal with line comments starting with ;
	483D 3B000000               ; CMPI32_RAX %0x3b
	0F84 %q                     ; JE32 %ascii_comment
	# deal all ascii less than 0
	483D 30000000               ; CMPI32_RAX %0x30
	0F8C %p                     ; JL32 %ascii_other
	# deal with 0-9
	483D 3A000000               ; CMPI32_RAX %0x3a
	0F8C %m                     ; JL32 %ascii_num
	# deal with all ascii less than A
	483D 41000000               ; CMPI32_RAX %0x41
	0F8C %p                     ; JL32 %ascii_other
	# deal with A-F
	483D 47000000               ; CMPI32_RAX %0x47
	0F8C %o                     ; JL32 %ascii_high
	#deal with all ascii less than a
	483D 61000000               ; CMPI32_RAX %0x61
	0F8C %p                     ; JL32 %ascii_other
	#deal with a-f
	483D 67000000               ; CMPI32_RAX %0x67
	0F8C %n                     ; JL32 %ascii_low
	# The rest that remains needs to be ignored
	E9 %p                       ; JMP32 %ascii_other

:h # :Second_pass
	E8 %u                       ; CALLI32 %Read_byte

	# Deal with EOF
	483D FCFFFFFF               ; CMPI32_RAX %-4
	0F84 %k                     ; JE32 %Second_pass_done

	# Simply drop the label
	483D 3A000000               ; CMPI32_RAX %0x3a
	0F85 %i                     ; JNE32 %Second_pass_0

	E8 %u                       ; CALLI32 %Read_byte
	E9 %h                       ; JMP32 %Second_pass

:i # :Second_pass_0
	# Deal with % pointer
	483D 25000000               ; CMPI32_RAX %0x25
	0F85 %j                     ; JNE32 %Second_pass_1

	E8 %z                       ; CALLI32 %StorePointer
	E9 %h                       ; JMP32 %Second_pass

:j # :Second_pass_1
	# Deal with everything else
	E8 %g                       ; CALLI32 %hex # Process our char

	# Deal with EOF
	483D FCFFFFFF               ; CMPI32_RAX %-4
	0F84 %k                     ; JE32 %Second_pass_done

	# deal with -1 values
	483D 00000000               ; CMPI32_RAX %0
	0F8C %h                     ; JL32 %Second_pass

	# deal with toggle
	4981FF 00000000             ; CMPI32_R15 %0
	0F84 %s                     ; JE32 %print

	# process first byte of pair
	4989C6	; COPY_RAX_to_R14
	49C7C7 00000000             ; LOADI32_R15 %0
	E9 %h                       ; JMP32 %Second_pass

:k # :Second_pass_done
	C3                          ; RET

:l # :EOF
	C3                          ; RET
:m # :ascii_num
	4883E8 30                   ; SUBI8_from_RAX !0x30
	C3                          ; RET
:n # :ascii_low
	4883E8 57                   ; SUBI8_from_RAX !0x57
	C3                          ; RET
:o # :ascii_high
	4883E8 37                   ; SUBI8_from_RAX !0x37
	C3                          ; RET
:p # :ascii_other
	48C7C0 FFFFFFFF             ; LOADI32_RAX %-1
	C3                          ; RET
:q # :ascii_comment
	E8 %u                       ; CALLI32 %Read_byte
	483D 0D000000               ; CMPI32_RAX %0xd
	0F84 %r                     ; JE32 %ascii_comment_cr
	483D 0A000000               ; CMPI32_RAX %0xa
	0F85 %q                     ; JNE32 %ascii_comment
:r # :ascii_comment_cr
	48C7C0 FFFFFFFF             ; LOADI32_RAX %-1
	C3                          ; RET

# process second byte of pair
:s # :print
	# update the sum and store in output
	49C1E6 04                   ; SHL8_R14 !4
	4C01F0                      ; ADD_R14_to_RAX
	880425 46036000             ; STORE8_al_Absolute32 &table

	# flip the toggle
	49F7D7                      ; NOT_R15

	# Print our first Hex
	48C7C2 01000000             ; LOADI32_RDX %1 # set the size of chars we want
	E8 %w                       ; CALLI32 %print_chars

	4981C5 01000000             ; ADDI32_to_R13 %1 # Increment IP
	E9 %h                       ; JMP32 %Second_pass

:t # :Done
	# program completed Successfully
	48C7C7 00000000             ; LOADI32_RDI %0 # All is well
	48C7C0 3C000000             ; LOADI32_RAX %0x3c # put the exit syscall number in eax
	0F05                        ; SYSCALL # Call it a good day

:u # :Read_byte
	# Attempt to read 1 byte from STDIN
	48C7C2 01000000             ; LOADI32_RDX %1 # set the size of chars we want
	48C7C6 46036000             ; LOADI32_RSI &table # Where to put it
	4C89CF                      ; COPY_R9_to_RDI # Where are we reading from
	48C7C0 00000000             ; LOADI32_RAX %0 # the syscall number for read
	0F05                        ; SYSCALL # call the Kernel

	4885C0                      ; TEST_RAX_RAX # check what we got
	0F84 %v                     ; JE32 %Read_byte_1 # Got EOF call it done

	# load byte
	8A0425 46036000             ; LOAD8_al_Absolute32 &table # load char
	480FB6C0                    ; MOVZBQ_RAX_AL # We have to zero extend it to use it
	C3                          ; RET

# Deal with EOF
:v # :Read_byte_1
	48C7C0 FCFFFFFF             ; LOADI32_RAX %-4 # Put EOF in rax
	C3                          ; RET

:w # :print_chars
	48C7C6 46036000             ; LOADI32_RSI &table # What we are writing
	48C7C7 01000000             ; LOADI32_RDI %1 # Stdout File Descriptor
	48C7C0 01000000             ; LOADI32_RAX %1 # the syscall number for write
	0F05                        ; SYSCALL # call the Kernel
	C3                          ; RET

:x # :Get_table_target
	E8 %u                       ; CALLI32 %Read_byte # Get single char label
	48C1E0 02                   ; SHL8_RAX !2 # Each label in table takes 4 bytes to store
	4805 46036000               ; ADDI32_to_RAX &table # Calculate offset
	C3                          ; RET

:y # :StoreLabel
	E8 %x                       ; CALLI32 %Get_table_target
	4C8928                      ; STORE32_R13_to_Address_in_RAX # Write out pointer to table
	C3                          ; RET

:z # :StorePointer
	4981C5 04000000             ; ADDI32_to_R13 %4 # Increment IP
	E8 %x                       ; CALLI32 %Get_table_target # Get address of pointer
	678B00                      ; LOAD32_Address_in_RAX_into_RAX # Get pointer
	4C29E8                      ; SUB_R13_from_RAX # target - ip
	890425 46036000             ; STORE32_RAX_Absolute32 &table # put value in output
	48C7C2 04000000             ; LOADI32_RDX %4 # set the size of chars we want
	E8 %w                       ; CALLI32 %print_chars
	C3                          ; RET

# :table
