## 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/>.

DEFINE ADDI32_to_R13 4981C5
DEFINE ADDI32_to_RAX 4805
DEFINE ADD_R14_to_RAX 4C01F0
DEFINE ADD_RAX_R14 4C01F0
DEFINE CALLI32 E8
DEFINE CMPI32_R15 4981FF
DEFINE CMPI32_RAX 483D
DEFINE CMP_R15_Immediate8 4983FF
DEFINE CMP_RAX_Immediate8 4883F8
DEFINE COPY_R9_to_RDI 4C89CF
DEFINE COPY_R10_to_RDI 4C89D7
DEFINE COPY_RAX_to_R14 4989C6
DEFINE COPY_RAX_to_R9 4989C1
DEFINE COPY_RAX_to_R10 4989C2
DEFINE JE32 0F84
DEFINE JE8 74
DEFINE JGE8 7D
DEFINE JL32 0F8C
DEFINE JL8 7C
DEFINE JMP32 E9
DEFINE JMP8 EB
DEFINE JNE32 0F85
DEFINE JNE8 75
DEFINE LOAD32_Address_in_RAX_into_RAX 678B00
DEFINE LOAD8_al_Absolute32 8A0425
DEFINE LOADI32_R13 49C7C5
DEFINE LOADI32_R14 49C7C6
DEFINE LOADI32_R15 49C7C7
DEFINE LOADI32_RAX 48C7C0
DEFINE LOADI32_EAX B8
DEFINE LOADI32_EDI BF
DEFINE LOADI32_EDX BA
DEFINE LOADI32_ESI BE
DEFINE MOVE_R14_RAX 4989C6
DEFINE MOVZBQ_RAX_AL 480FB6C0
DEFINE NOT_R15 49F7D7
DEFINE NULL 00000000
DEFINE POP_RAX 58
DEFINE POP_RDI 5F
DEFINE RET C3
DEFINE RETQ C3
DEFINE SHL8_R14 49C1E6
DEFINE SHL8_RAX 48C1E0
DEFINE SHL_R14_Immediate8 49C1E6
DEFINE STORE32_R13_to_Address_in_RAX 4C8928
DEFINE STORE32_RAX_Absolute32 890425
DEFINE STORE8_al_Absolute32 880425
DEFINE SUBI8_from_RAX 4883E8
DEFINE SUB_R13_from_RAX 4C29E8
DEFINE SUB_RAX_Immediate8 4883E8
DEFINE SYSCALL 0F05
DEFINE TEST_RAX_RAX 4885C0

# Where the ELF Header is going to hit
# Simply jump to _start
# Our main function
:_start
	POP_RAX                     # Get the number of arguments
	POP_RDI                     # Get the program name
	POP_RDI                     # Get the actual input name
	LOADI32_ESI %0              # prepare read_only
	LOADI32_EAX %2              # the syscall number for open()
	SYSCALL                     # Now open that damn file
	COPY_RAX_to_R9              # Preserve the file pointer we were given

	POP_RDI                     # Get the actual output name
	LOADI32_ESI %577            # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
	LOADI32_EDX %448            # Prepare file as RWX for owner only (700 in octal)
	LOADI32_EAX %2              # the syscall number for open()
	SYSCALL                     # Now open that damn file
	COPY_RAX_to_R10             # Preserve the file pointer we were given

	# Our flag for byte processing
	LOADI32_R15 %-1

	# temp storage for the sum
	LOADI32_R14 %0

:loop
	# Read a byte
	CALLI32 %read_byte

	# process byte
	CALLI32 %hex

	# deal with -1 values
	CMP_RAX_Immediate8 !0
	JL8 !loop

	# deal with toggle
	CMP_R15_Immediate8 !0
	JGE8 !print

	# process first byte of pair
	MOVE_R14_RAX
	LOADI32_R15 %0
	JMP8 !loop

# process second byte of pair
:print
	# update the sum and store in output
	SHL_R14_Immediate8 !4
	ADD_RAX_R14
	STORE8_al_Absolute32 &output

	# flip the toggle
	LOADI32_R15 %-1

	CALLI32 %write_byte

	JMP8 !loop

:hex
	# Purge Comment Lines (#)
	CMP_RAX_Immediate8 !35
	JE8 !purge_comment

	# Purge Comment Lines (;)
	CMP_RAX_Immediate8 !59
	JE8 !purge_comment

	# deal all ascii less than '0'
	CMP_RAX_Immediate8 !48
	JL8 !ascii_other

	# deal with 0-9
	CMP_RAX_Immediate8 !58
	JL8 !ascii_num

	# deal with all ascii less than 'A'
	CMP_RAX_Immediate8 !65
	JL8 !ascii_other

	# deal with 'A'-'F'
	CMP_RAX_Immediate8 !71
	JL8 !ascii_high

	# deal with all ascii less than 'a'
	CMP_RAX_Immediate8 !97
	JL8 !ascii_other

	#deal with 'a'-'f'
	CMP_RAX_Immediate8 !103
	JL8 !ascii_low

	# The rest that remains needs to be ignored
	JMP8 !ascii_other

:purge_comment
	# Read a byte
	CALLI32 %read_byte

	# Loop if not LF
	CMP_RAX_Immediate8 !10
	JNE8 !purge_comment

	# Otherwise return -1
	LOADI32_RAX %-1
	RETQ

:ascii_num
	SUB_RAX_Immediate8 !48
	RETQ

:ascii_low
	SUB_RAX_Immediate8 !87
	RETQ

:ascii_high
	SUB_RAX_Immediate8 !55
	RETQ

:ascii_other
	LOADI32_RAX %-1
	RETQ

:Done
	# program completed Successfully
	LOADI32_EDI %0              # All is well
	LOADI32_EAX %60             # put the exit syscall number in rax
	SYSCALL                     # Call it a good day

:write_byte
	# Print our Hex
	LOADI32_EDX %1              # set the size of chars we want
	LOADI32_ESI &output         # What we are writing
	COPY_R10_to_RDI             # Where are we writing to
	LOADI32_EAX %1              # the syscall number for write
	SYSCALL                     # call the Kernel
	RET

# Where we are putting our output
:output
	# Reserve 4bytes of Zeros
	NULL

:read_byte
	# Attempt to read 1 byte from STDIN
	LOADI32_EDX %1              # set the size of chars we want
	LOADI32_ESI &input          # Where to put it
	COPY_R9_to_RDI              # Where are we reading from
	LOADI32_EAX %0              # the syscall number for read
	SYSCALL                     # call the Kernel

	TEST_RAX_RAX                # check what we got
	JE8 !Done                   # Got EOF call it done

	# load byte
	LOAD8_al_Absolute32 &input  # load char
	MOVZBQ_RAX_AL               # We have to zero extend it to use it
	RET

# Where we get our input
:input
	# Reserve 4bytes of Zeros
	NULL

:ELF_end
