### Copyright (C) 2019 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 ADDI8_to_AL 04
DEFINE ADDI8_to_RAX 4883C0
DEFINE ADDI8_to_RBX 4883C3
DEFINE ADDI8_to_RCX 4883C1
DEFINE ADDI8_to_RDX 4883C2
DEFINE ADD_RAX_to_RDI 4801C7
DEFINE ADD_RCX_to_RAX 4801C8
DEFINE AND_RAX_Immediate8 4883E0
DEFINE CALLI32 E8
DEFINE CMP_AL_Immediate8 3C
DEFINE CMP_RAX_Immediate8 4883F8
DEFINE CMP_RBX_Immediate8 4883FB
DEFINE CMP_RCX_Immediate8 4883F9
DEFINE CMP_RDX_Immediate8 4883FA
DEFINE CMP_RSI_Immediate8 4883FE
DEFINE CMP_RBX_to_RAX 4839D8
DEFINE CMP_RBX_to_RCX 4839D9
DEFINE CMP_RCX_to_RAX 4839C8
DEFINE COPY_R12_to_RAX 4C89E0
DEFINE COPY_R12_to_RDI 4C89E7
DEFINE COPY_R13_to_RAX 4C89E8
DEFINE COPY_R13_to_RBX 4C89EB
DEFINE COPY_R14_to_RDI 4C89F7
DEFINE COPY_R15_to_RDI 4C89FF
DEFINE COPY_RAX_to_R12 4989C4
DEFINE COPY_RAX_to_R13 4989C5
DEFINE COPY_RAX_to_R14 4989C6
DEFINE COPY_RAX_to_R15 4989C7
DEFINE COPY_RAX_to_RBX 4889C3
DEFINE COPY_RAX_to_RCX 4889C1
DEFINE COPY_RAX_to_RDX 4889C2
DEFINE COPY_RBX_to_RAX 4889D8
DEFINE COPY_RBX_to_RCX 4889D9
DEFINE COPY_RBX_to_RDX 4889DA
DEFINE COPY_RCX_to_RAX 4889C8
DEFINE COPY_RCX_to_RBX 4889CB
DEFINE COPY_RDI_to_R12 4989FC
DEFINE COPY_RDX_to_R13 4989D5
DEFINE COPY_RDX_to_RAX 4889D0
DEFINE IMUL_RAX_Immediate8 486BC0
DEFINE JBE8 76
DEFINE JE32 0F84
DEFINE JG32 0F8F
DEFINE JL32 0F8C
DEFINE JMP32 E9
DEFINE JNE32 0F85
DEFINE LEA_RSI 488D3424
DEFINE LOAD8_AL_from_Address_RBX 8A03
DEFINE LOAD8_AL_from_Address_RBX_Index_RCX 8A040B
DEFINE LOAD8_AL_from_Address_RCX 8A01
DEFINE LOAD8_AL_from_Address_RDX 8A02
DEFINE LOAD8_BL_from_Address_RAX 8A18
DEFINE LOAD8_BL_from_Address_RDX 8A1A
DEFINE LOAD8_CL_from_Address_RAX 8A08
DEFINE LOAD8_CL_from_Address_RBX 8A0B
DEFINE LOAD8_CL_from_Address_RBX_Immediate8 8A4B
DEFINE LOAD64_into_RAX_from_Address_RAX 488B00
DEFINE LOAD64_into_RAX_from_Address_RCX 488B01
DEFINE LOAD64_into_RAX_from_Address_RAX_Immediate8 488B40
DEFINE LOAD64_into_RAX_from_Address_RBX_Immediate8 488B43
DEFINE LOAD64_into_RAX_from_Address_RCX_Immediate8 488B41
DEFINE LOAD64_into_RAX_from_Address_RDX_Immediate8 488B42
DEFINE LOAD64_into_RBX_from_Address_RAX_Immediate8 488B58
DEFINE LOAD64_into_RBX_from_Address_RBX 488B1B
DEFINE LOAD64_into_RBX_from_Address_RCX_Immediate8 488B59
DEFINE LOAD64_into_RCX_from_Address_RAX_Immediate8 488B48
DEFINE LOAD64_into_RCX_from_Address_RBX 488B0B
DEFINE LOAD64_into_RCX_from_Address_RCX 488B09
DEFINE LOAD64_into_RDX_from_Address_RDX 488B12
DEFINE LOADI32_RAX 48C7C0
DEFINE LOADI32_RBX BB
DEFINE LOADI32_RCX B9
DEFINE LOADI32_RDI BF
DEFINE LOADI32_RDX BA
DEFINE LOADI32_RSI BE
DEFINE POP_FLAGS 9D
DEFINE POP_R11 415B
DEFINE POP_RAX 58
DEFINE POP_RBX 5B
DEFINE POP_RCX 59
DEFINE POP_RDX 5A
DEFINE POP_RDI 5F
DEFINE POP_RSI 5E
DEFINE PUSH_FLAGS 9C
DEFINE PUSH_R11 4153
DEFINE PUSH_RAX 50
DEFINE PUSH_RBX 53
DEFINE PUSH_RCX 51
DEFINE PUSH_RDX 52
DEFINE PUSH_RSI 56
DEFINE RET C3
DEFINE SHIFT_LEFT_RAX_Immediate8 48C1E0
DEFINE SHIFT_RIGHT_RAX_Immediate8 48C1E8
DEFINE STORE64_R13_into_Address_RDX 4C892A
DEFINE STORE64_RAX_into_Address_RBX 488903
DEFINE STORE64_RAX_into_Address_RCX 488901
DEFINE STORE64_RAX_into_Address_RCX_Immediate8 488941
DEFINE STORE64_RAX_into_Address_RDX_Immediate8 488942
DEFINE STORE64_RBX_into_Address_RAX_Immediate8 488958
DEFINE STORE64_RBX_into_Address_RCX_Immediate8 488959
DEFINE STORE64_RCX_into_Address_RDX_Immediate8 48894A
DEFINE STORE8_AL_into_Address_RBX 8803
DEFINE STORE8_CL_into_Address_RDX 880A
DEFINE SUBI8_RCX 4883E9
DEFINE SWAP_RAX_RBX 4893
DEFINE SYSCALL 0F05
DEFINE ZERO_EXTEND_AL 480FB6C0
DEFINE ZERO_EXTEND_BL 480FB6DB
DEFINE ZERO_EXTEND_CL 480FB6C9

	;; Register usage:
	;; RAX, RSI, RDI => Temps
	;; R12 => MALLOC
	;; R13 => HEAD
	;; R14 => Output_file
	;; R15 => Input_file

	;; Struct format: (size 32)
	;; NEXT => 0
	;; TYPE => 8
	;; TEXT => 16
	;; EXPRESSION => 24

	;; Types
	;; None => 0
	;; MACRO => 1
	;; STRING => 2

; 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_RSI %0              ; prepare read_only
	LOADI32_RAX %2              ; the syscall number for open()
	SYSCALL                     ; Now open that damn file
	COPY_RAX_to_R15             ; Preserve the file pointer we were given

	POP_RDI                     ; Get the actual output name
	LOADI32_RSI %577            ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
	LOADI32_RDX %384            ; Prepare file as RW for owner only (600 in octal)
	LOADI32_RAX %2              ; the syscall number for open()
	SYSCALL                     ; Now open that damn file
	CMP_RAX_Immediate8 !0       ; Check for missing output
	JG32 %_start_out            ; Have real input
	LOADI32_RAX %1              ; Use stdout

:_start_out
	COPY_RAX_to_R14             ; Preserve the file pointer we were given

	LOADI32_RAX %0xC            ; the Syscall # for SYS_BRK
	LOADI32_RDI %0              ; Get current brk
	SYSCALL                     ; Let the kernel do the work
	COPY_RAX_to_R12             ; Set our malloc pointer

	CALLI32 %Tokenize_Line      ; Get all lines
	COPY_R13_to_RAX             ; prepare for Reverse_List
	CALLI32 %Reverse_List       ; Correct order
	COPY_RAX_to_R13             ; Update HEAD
	CALLI32 %Identify_Macros    ; Find the DEFINEs
	CALLI32 %Line_Macro         ; Apply the DEFINEs
	CALLI32 %Process_String     ; Handle strings
	CALLI32 %Eval_Immediates    ; Handle Numbers
	CALLI32 %Preserve_Other     ; Collect the remaining
	CALLI32 %Print_Hex          ; Output our results

:Done
	; program completed Successfully
	LOADI32_RDI %0              ; All is well
	LOADI32_RAX %0x3C           ; put the exit syscall number in eax
	SYSCALL                     ; Call it a good day


;; Tokenize_Line Function
;; Using input file R15 and Head R13
;; Creates a linked list of structs
;; Uses RBX for in_set strings, RCX for Int C and RDX for Struct Token* p
:Tokenize_Line
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	PUSH_RDX                    ; Protect RDX
:restart
	CALLI32 %fgetc              ; Read a char
	CMP_RAX_Immediate8 !-4      ; Check for EOF
	JE32 %done                  ; File is collected

	ZERO_EXTEND_AL              ; We have to zero extend it to use it
	COPY_RAX_to_RCX             ; Protect C

	LOADI32_RBX &comments       ; Get pointer to "#;"
	CALLI32 %In_Set             ; Check for comments
	CMP_RAX_Immediate8 !1       ; If comments
	JE32 %Purge_LineComment     ; try again

	COPY_RCX_to_RAX             ; put C in place for check
	LOADI32_RBX &terminators    ; Get pointer to "\n\t "
	CALLI32 %In_Set             ; Check for terminators
	CMP_RAX_Immediate8 !1       ; If terminator
	JE32 %restart               ; try again

	LOADI32_RAX %32             ; Malloc the struct P
	CALLI32 %malloc             ; Get pointer to P
	COPY_RAX_to_RDX             ; Protect P
	STORE64_R13_into_Address_RDX ; P->NEXT = HEAD
	COPY_RDX_to_R13             ; HEAD = P

	COPY_RCX_to_RAX             ; put C in place for check
	LOADI32_RBX &string_char    ; Get pointer to "\"'"
	CALLI32 %In_Set             ; Check for string chars
	CMP_RAX_Immediate8 !1       ; If string char
	JE32 %Store_String          ; Get string

	CALLI32 %Store_Atom         ; Get whole token
	JMP32 %restart

:done
	POP_RDX                     ; Restore RDX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	RET


;; fgetc function
;; Receives FILE* in R15
;; Returns -4 (EOF) or char in RAX
:fgetc
	LOADI32_RAX %-4             ; Put EOF in rax
	PUSH_RAX                    ; Assume bad (If nothing read, value will remain EOF)
	LEA_RSI                     ; Get stack address
	COPY_R15_to_RDI             ; Where are we reading from
	LOADI32_RAX %0              ; the syscall number for read
	PUSH_RDX                    ; Protect RDX
	LOADI32_RDX %1              ; set the size of chars we want
	PUSH_R11                    ; Protect r11
	SYSCALL                     ; call the Kernel
	POP_R11                     ; Restore r11
	POP_RDX                     ; Restore RDX
	POP_RAX                     ; Get either char or EOF
	RET


;; Malloc isn't actually required if the program being built fits in the initial memory
;; However, it doesn't take much to add it.
;; Requires R12 to be initialized and RAX to have the number of desired bytes
:malloc
	COPY_R12_to_RDI             ; Using the current pointer
	ADD_RAX_to_RDI              ; Request the number of desired bytes
	LOADI32_RAX %0xC            ; the Syscall # for SYS_BRK
	PUSH_RCX                    ; Protect rcx
	PUSH_R11                    ; Protect r11
	SYSCALL                     ; call the Kernel
	POP_R11                     ; Restore r11
	POP_RCX                     ; Restore rcx
	COPY_R12_to_RAX             ; Return pointer
	COPY_RDI_to_R12             ; Update pointer
	RET


;; Purge_LineComment function
;; Reads chars until LF and jumps to restart
:Purge_LineComment
	CALLI32 %fgetc              ; Get a char
	ZERO_EXTEND_AL              ; Zero extend
	CMP_RAX_Immediate8 !10      ; While not LF
	JNE32 %Purge_LineComment    ; Keep reading
	JMP32 %restart


;; Store_String Function
;; Receives C in RCX, HEAD in RDX and Input file in R14
;; Uses RBX for terminator, RCX for C and RDX for string
:Store_String
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	PUSH_RDX                    ; Protect RDX

	LOADI32_RAX %2              ; Using TYPE STRING
	STORE64_RAX_into_Address_RDX_Immediate8 !8 ; HEAD->TYPE = STRING
	LOADI32_RAX %256            ; Malloc the string
	CALLI32 %malloc             ; Get pointer to P
	STORE64_RAX_into_Address_RDX_Immediate8 !16 ; HEAD->TEXT = STRING
	COPY_RCX_to_RBX             ; Protect terminator
	COPY_RAX_to_RDX             ; Protect string pointer
:Store_String_Loop
	STORE8_CL_into_Address_RDX  ; write byte
	CALLI32 %fgetc              ; read next char
	ZERO_EXTEND_AL              ; Zero extend it
	COPY_RAX_to_RCX             ; Update C
	ADDI8_to_RDX !1             ; STRING = STRING + 1
	CMP_RBX_to_RCX              ; See if we hit terminator
	JNE32 %Store_String_Loop    ; Otherwise keep looping

	POP_RDX                     ; Restore RDX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	COPY_RDX_to_RAX             ; return HEAD
	JMP32 %restart


;; Store_Atom Function
;; Receives C in RCX, HEAD in RDX and Input file in R15
;; Uses RBX for in_set strings, RCX for C and RDX for string
:Store_Atom
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	PUSH_RDX                    ; Protect RDX

	LOADI32_RAX %256            ; Malloc the string
	CALLI32 %malloc             ; Get pointer to P
	STORE64_RAX_into_Address_RDX_Immediate8 !16 ; HEAD->TEXT = STRING
	LOADI32_RBX &terminators    ; Get pointer to "\n\t "
	COPY_RAX_to_RDX             ; Protect string pointer
:Store_Atom_loop
	STORE8_CL_into_Address_RDX  ; write byte
	CALLI32 %fgetc              ; read next char
	ZERO_EXTEND_AL              ; Zero extend it
	COPY_RAX_to_RCX             ; Update C
	ADDI8_to_RDX !1             ; STRING = STRING + 1
	CALLI32 %In_Set             ; Check for terminators
	CMP_RAX_Immediate8 !0       ; Check for "\n\t "
	JE32 %Store_Atom_loop       ; Loop otherwise

	POP_RDX                     ; Restore RDX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	COPY_RDX_to_RAX             ; return HEAD
	RET


;; In_Set function
;; Receives Char C in RAX and CHAR* in RBX
;; Returns 1 if true, zero if false in RAX
:In_Set
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
:In_Set_loop
	LOAD8_CL_from_Address_RBX   ; Read char
	ZERO_EXTEND_CL              ; Zero extend it

	CMP_RCX_to_RAX              ; See if they match
	JE32 %In_Set_True           ; return true

	CMP_RCX_Immediate8 !0       ; Check for NULL
	JE32 %In_Set_False          ; return false

	ADDI8_to_RBX !1             ; s = s + 1
	JMP32 %In_Set_loop          ; Keep looping

:In_Set_True
	LOADI32_RAX %1              ; Set True
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	RET

:In_Set_False
	LOADI32_RAX %0              ; Set FALSE
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	RET

;; Char sets
:terminators
	"
	 "

:comments
	"#;"

:string_char
	'22 27 00'


;; Reverse_List function
;; Receives List in RAX
;; Returns the list reversed in RAX
:Reverse_List
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	COPY_RAX_to_RBX             ; Set HEAD
	LOADI32_RAX %0              ; ROOT = NULL
:Reverse_List_Loop
	CMP_RBX_Immediate8 !0       ; WHILE HEAD != NULL
	JE32 %Reverse_List_Done     ; Stop otherwise

	LOAD64_into_RCX_from_Address_RBX ; NEXT = HEAD->NEXT
	STORE64_RAX_into_Address_RBX ; HEAD->NEXT = ROOT
	COPY_RBX_to_RAX             ; ROOT = HEAD
	COPY_RCX_to_RBX             ; HEAD = NEXT
	JMP32 %Reverse_List_Loop    ; Keep Going

:Reverse_List_Done
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	RET


;; Identify_Macros function
;; Receives List in RAX
;; Updates the list in place; does not modify registers
;; Uses RBX for DEFINE, RCX for I
:Identify_Macros
	PUSH_RAX                    ; Protect RAX
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	PUSH_RDX                    ; Protect RDX
	LOADI32_RBX &DEFINE_str     ; Setup define string
	COPY_RAX_to_RCX             ; I = HEAD
:Identify_Macros_Loop
	LOAD64_into_RAX_from_Address_RCX_Immediate8 !16 ; I->TEXT
	CALLI32 %match              ; IF "DEFINE" == I->TEXT
	CMP_RAX_Immediate8 !0       ; Check if match
	JNE32 %Identify_Macros_Next ; Skip the work

	;; Deal with MACRO
	LOADI32_RAX %1              ; Using MACRO
	STORE64_RAX_into_Address_RCX_Immediate8 !8 ; I->TYPE = MACRO

	LOAD64_into_RAX_from_Address_RCX ; I->NEXT
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; I->NEXT->TEXT
	STORE64_RAX_into_Address_RCX_Immediate8 !16 ; I->TEXT = I->NEXT->TEXT

	LOAD64_into_RAX_from_Address_RCX ; I->NEXT
	LOAD64_into_RAX_from_Address_RAX ; I->NEXT->NEXT
	LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; I->NEXT->NEXT->TEXT
	STORE64_RAX_into_Address_RCX_Immediate8 !24 ; I->EXPRESSION = I->NEXT->NEXT->TEXT

	LOAD64_into_RAX_from_Address_RCX ; I->NEXT
	LOAD64_into_RAX_from_Address_RAX ; I->NEXT->NEXT
	LOAD64_into_RAX_from_Address_RAX ; I->NEXT->NEXT->NEXT
	STORE64_RAX_into_Address_RCX ; I->NEXT = I->NEXT->NEXT->NEXT

:Identify_Macros_Next
	LOAD64_into_RCX_from_Address_RCX ; I = I->NEXT
	CMP_RCX_Immediate8 !0       ; Check for NULL
	JNE32 %Identify_Macros_Loop ; Keep looping otherwise

	POP_RDX                     ; Restore RDX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	POP_RAX                     ; Restore RAX
	RET

:DEFINE_str
	"DEFINE"


;; match function
;; Receives CHAR* in RAX and CHAR* in RBX
;; Returns 0 (TRUE) or 1 (FALSE) in RAX
:match
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	PUSH_RDX                    ; Protect RDX
	COPY_RAX_to_RCX             ; S1 in place
	COPY_RBX_to_RDX             ; S2 in place
:match_Loop
	LOAD8_AL_from_Address_RCX   ; S1[0]
	ZERO_EXTEND_AL              ; Make it useful
	LOAD8_BL_from_Address_RDX   ; S2[0]
	ZERO_EXTEND_BL              ; Make it useful
	CMP_RBX_to_RAX              ; See if they match
	JNE32 %match_False          ; If not

	ADDI8_to_RCX !1             ; S1 = S1 + 1
	ADDI8_to_RDX !1             ; S2 = S2 + 1
	CMP_RAX_Immediate8 !0       ; If reached end of string
	JE32 %match_Done            ; Perfect match
	JMP32 %match_Loop           ; Otherwise keep looping

:match_False
	LOADI32_RAX %1              ; Return false
:match_Done
	POP_RDX                     ; Restore RDX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	RET


;; Line_Macro function
;; Receives List in RAX
;; Updates the list in place; does not modify registers
;; Uses RAX for I, RBX for I->TEXT, RCX for I->EXPRESSION
:Line_Macro
	PUSH_RAX                    ; Protect RAX
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	PUSH_RDX                    ; Protect RDX
:Line_Macro_Loop
	LOAD64_into_RBX_from_Address_RAX_Immediate8 !8 ; I->TYPE
	CMP_RBX_Immediate8 !1       ; IF MACRO == I->TYPE
	JNE32 %Line_Macro_Next      ; Otherwise move on

	;; Is a macro apply
	LOAD64_into_RBX_from_Address_RAX_Immediate8 !16 ; I->TEXT
	LOAD64_into_RCX_from_Address_RAX_Immediate8 !24 ; I->EXPRESSION
	LOAD64_into_RAX_from_Address_RAX ; I->NEXT
	CALLI32 %Set_Expression     ; Apply it
	JMP32 %Line_Macro_Loop      ; Move on to next

:Line_Macro_Next
	LOAD64_into_RAX_from_Address_RAX ; I->NEXT
	CMP_RAX_Immediate8 !0       ; Check for NULL
	JNE32 %Line_Macro_Loop      ; Keep going

	POP_RDX                     ; Restore RDX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	POP_RAX                     ; Restore RAX
	RET


;; Set_Expression function
;; Receives List in RAX, CHAR* in RBX and CHAR* in RCX
;; Updates the list in place; does not modify registers
;; Uses RBX for C, RCX for EXP and RDX for I
:Set_Expression
	PUSH_RAX                    ; Protect RAX
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	PUSH_RDX                    ; Protect RDX
	COPY_RAX_to_RDX             ; Set I
:Set_Expression_Loop
	LOAD64_into_RAX_from_Address_RDX_Immediate8 !8 ; I->TYPE
	CMP_RAX_Immediate8 !1       ; IF MACRO == I->TYPE
	JE32 %Set_Expression_Next   ; Ignore and move on

	LOAD64_into_RAX_from_Address_RDX_Immediate8 !16 ; I->TEXT
	CALLI32 %match              ; Check for match
	CMP_RAX_Immediate8 !0       ; If match
	JNE32 %Set_Expression_Next  ; Otherwise next

	;; We have a non-macro match
	STORE64_RCX_into_Address_RDX_Immediate8 !24 ; I->EXPRESSION = EXP

:Set_Expression_Next
	LOAD64_into_RDX_from_Address_RDX ; I = I->NEXT
	CMP_RDX_Immediate8 !0       ; IF NULL == I
	JNE32 %Set_Expression_Loop  ; Otherwise keep looping

	POP_RDX                     ; Restore RDX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	POP_RAX                     ; Restore RAX
	RET


;; Process_String function
;; Receives List in RAX
;; Update the list in place; does not modify registers
;; Uses RBX for I->TEXT, RCX for I and RDX for S
:Process_String
	PUSH_RAX                    ; Protect RAX
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	PUSH_RDX                    ; Protect RDX
	COPY_RAX_to_RCX             ; I = HEAD
:Process_String_loop
	LOAD64_into_RAX_from_Address_RCX_Immediate8 !8 ; I->TYPE
	CMP_RAX_Immediate8 !2       ; IF STRING == I->TYPE
	JNE32 %Process_String_Next  ; Skip to next

	LOAD64_into_RBX_from_Address_RCX_Immediate8 !16 ; I->TEXT
	LOAD8_AL_from_Address_RBX   ; I->TEXT[0]
	ZERO_EXTEND_AL              ; make it useful
	CMP_RAX_Immediate8 !39      ; IF '\'' == I->TEXT[0]
	JNE32 %Process_String_Raw   ; Deal with "\""

	;; Deal with '\''
	ADDI8_to_RBX !1             ; I->TEXT + 1
	STORE64_RBX_into_Address_RCX_Immediate8 !24 ; I->EXPRESSION = I->TEXT + 1
	JMP32 %Process_String_Next  ; Move on to next

:Process_String_Raw
	COPY_RBX_to_RAX             ; Get length of I->TEXT
	CALLI32 %string_length      ; Do it
	SHIFT_RIGHT_RAX_Immediate8 !2 ; LENGTH = LENGTH >> 2
	ADDI8_to_RAX !1             ; LENGTH = LENGTH + 1
	SHIFT_LEFT_RAX_Immediate8 !3 ; LENGTH = LENGTH << 3
	CALLI32 %malloc             ; Get string
	COPY_RBX_to_RDX             ; S = I->TEXT
	ADDI8_to_RDX !1             ; S = S + 1
	STORE64_RAX_into_Address_RCX_Immediate8 !24 ; I->EXPRESSION = hexify
	COPY_RAX_to_RBX             ; Put hexify buffer in rbx

:Process_String_Raw_Loop
	LOAD8_AL_from_Address_RDX   ; Read 1 chars
	ZERO_EXTEND_AL              ; Make it useful
	ADDI8_to_RDX !1             ; S = S + 1
	CMP_AL_Immediate8 !0        ; Check for NULL
	PUSH_FLAGS                  ; Protect condition
	CALLI32 %hex8               ; write them all
	POP_FLAGS                   ; restore condition
	JNE32 %Process_String_Raw_Loop ; Keep looping

:Process_String_Next
	LOAD64_into_RCX_from_Address_RCX ; I = I->NEXT
	CMP_RCX_Immediate8 !0       ; IF NULL == I
	JNE32 %Process_String_loop  ; Otherwise keep looping

	POP_RDX                     ; Restore RDX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	POP_RAX                     ; Restore RAX
	RET


;; string_length function
;; Receives CHAR* in RAX
;; Returns INT in RAX
;; Uses RAX for CH, RBX for S and RCX for INDEX
:string_length
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	COPY_RAX_to_RBX             ; Set S
	LOADI32_RCX %0              ; INDEX = 0
:string_length_loop
	LOAD8_AL_from_Address_RBX_Index_RCX ; S[0]
	ZERO_EXTEND_AL              ; make it useful
	CMP_RAX_Immediate8 !0       ; IF NULL == S[0]
	JE32 %string_length_done    ; Stop

	ADDI8_to_RCX !1             ; INDEX = INDEX + 1
	JMP32 %string_length_loop   ; Keep going

:string_length_done
	COPY_RCX_to_RAX             ; RETURN INDEX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	RET


;; Eval_Immediates function
;; Receives List in RAX
;; Updates the list in place; does not modify registers
;; Uses RBX for I->TEXT[0], RCX for I->TEXT[1] and RDX for I
:Eval_Immediates
	PUSH_RAX                    ; Protect RAX
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	PUSH_RDX                    ; Protect RDX
	COPY_RAX_to_RDX             ; I = HEAD
:Eval_Immediates_Loop
	;; Check for MACRO
	LOAD64_into_RAX_from_Address_RDX_Immediate8 !8 ; I->TYPE
	CMP_RAX_Immediate8 !1       ; IF MACRO == I-TYPE
	JE32 %Eval_Immediates_Next  ; Skip to next

	;; Check for NULL EXPRESSION
	LOAD64_into_RAX_from_Address_RDX_Immediate8 !24 ; I->EXPRESSION
	CMP_RAX_Immediate8 !0       ; IF NULL == I->EXPRESSION
	JNE32 %Eval_Immediates_Next ; Skip to next

	;; Check if number
	LOAD64_into_RAX_from_Address_RDX_Immediate8 !16 ; I->TEXT
	LOAD8_BL_from_Address_RAX   ; I->TEXT[0]
	ZERO_EXTEND_BL              ; Extend to use
	ADDI8_to_RAX !1             ; I->TEXT + 1
	LOAD8_CL_from_Address_RAX   ; I->TEXT[1]
	ZERO_EXTEND_CL              ; Extend to use
	CALLI32 %numerate_string    ; Convert string to INT
	CMP_RAX_Immediate8 !0       ; IF 0 == numerate_number(I->TEXT + 1)
	JNE32 %Eval_Immediates_value ; Has a value

	;; Last chance for Immediate
	CMP_RCX_Immediate8 !48      ; If '0' == I->TEXT[1]
	JNE32 %Eval_Immediates_Next ; Skip to next

:Eval_Immediates_value
	CALLI32 %express_number     ; Convert value to hex string
	STORE64_RAX_into_Address_RDX_Immediate8 !24 ; I->EXPRESSION = express_number(value, I-TEXT[0])

:Eval_Immediates_Next
	LOAD64_into_RDX_from_Address_RDX ; I = I->NEXT
	CMP_RDX_Immediate8 !0       ; IF NULL == I
	JNE32 %Eval_Immediates_Loop ; Otherwise keep looping

	POP_RDX                     ; Restore RDX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	POP_RAX                     ; Restore RAX
	RET


;; numerate_string function
;; Receives CHAR* in RAX
;; Returns value of CHAR* in RAX
;; Only supports negative decimals and Uppercase Hex (eg 5, -3 and 0xCC)
;; Uses RAX for VALUE, RBX for S, RCX for CH and RSI for NEGATIVE?
:numerate_string
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	PUSH_RDX                    ; Protect RDX
	PUSH_RSI                    ; Protect RSI
	COPY_RAX_to_RBX             ; put S in correct place
	LOADI32_RAX %0              ; Initialize to Zero
:numerate_string_loop
	LOAD8_CL_from_Address_RBX_Immediate8 !1 ; S[1]
	ZERO_EXTEND_CL              ; make it useful
	CMP_RCX_Immediate8 !120     ; IF 'x' == S[1]
	JE32 %numerate_hex          ; Deal with hex input

	;; Assume decimal input
	LOADI32_RSI %0              ; Assume no negation
	LOAD8_CL_from_Address_RBX   ; S[0]
	ZERO_EXTEND_CL              ; make it useful
	CMP_RCX_Immediate8 !45      ; IF '-' == S[0]
	JNE32 %numerate_decimal     ; Skip negation

	LOADI32_RSI %1              ; Set FLAG
	ADDI8_to_RBX !1             ; S = S + 1

:numerate_decimal
	LOAD8_CL_from_Address_RBX   ; S[0]
	ZERO_EXTEND_CL              ; make it useful
	CMP_RCX_Immediate8 !0       ; IF NULL == S[0]
	JE32 %numerate_decimal_done ; We are done

	IMUL_RAX_Immediate8 !10     ; VALUE = VALUE * 10
	SUBI8_RCX !48               ; CH = CH - '0'
	CMP_RCX_Immediate8 !9       ; Check for illegal
	JG32 %numerate_string_fail  ; If CH > '9'
	CMP_RCX_Immediate8 !0       ; Check for illegal
	JL32 %numerate_string_fail  ; IF CH < 0
	ADD_RCX_to_RAX              ; VALUE = VALUE + CH
	ADDI8_to_RBX !1             ; S = S + 1
	JMP32 %numerate_decimal     ; Keep looping

:numerate_decimal_done
	CMP_RSI_Immediate8 !1       ; Check if need to negate
	JNE32 %numerate_string_done ; Nope

	IMUL_RAX_Immediate8 !-1     ; VALUE = VALUE * -1
	JMP32 %numerate_string_done ; Done

:numerate_hex
	ADDI8_to_RBX !2             ; S = S + 2
:numerate_hex_loop
	LOAD8_CL_from_Address_RBX   ; S[0]
	ZERO_EXTEND_CL              ; make it useful
	CMP_RCX_Immediate8 !0       ; IF NULL == S[0]
	JE32 %numerate_string_done  ; We are done

	SHIFT_LEFT_RAX_Immediate8 !4 ; VALUE = VALUE << 4
	SUBI8_RCX !48               ; CH = CH - '0'
	CMP_RCX_Immediate8 !10      ; IF 10 >= CH
	JL32 %numerate_hex_digit    ; NO
	SUBI8_RCX !7                ; Push A-F into range
:numerate_hex_digit
	CMP_RCX_Immediate8 !15      ; Check for illegal
	JG32 %numerate_string_fail  ; If CH > 'F'
	CMP_RCX_Immediate8 !0       ; Check for illegal
	JL32 %numerate_string_fail  ; IF CH < 0
	ADD_RCX_to_RAX              ; VALUE = VALUE + CH
	ADDI8_to_RBX !1             ; S = S + 1
	JMP32 %numerate_hex_loop    ; Keep looping

:numerate_string_fail
	LOADI32_RAX %0              ; return ZERO

:numerate_string_done
	POP_RSI                     ; Restore RSI
	POP_RDX                     ; Restore RDX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	RET


;; express_number function
;; Receives INT in RAX and CHAR in RBX
;; Allocates a string and expresses the value in hex
;; Returns string in RAX
;; Uses RAX for VALUE, RBX for S and RCX for CH
:express_number
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	PUSH_RDX                    ; Protect RDX
	COPY_RBX_to_RCX             ; Put CH in right place
	COPY_RAX_to_RBX             ; Protect VALUE
	CMP_RCX_Immediate8 !37      ; IF '%' == CH
	JNE32 %express_number2      ; Otherwise try @

	LOADI32_RAX %9              ; We need 3bytes
	CALLI32 %malloc             ; Get S pointer
	SWAP_RAX_RBX                ; Put S and VALUE in place
	PUSH_RBX                    ; Protect S
	CALLI32 %hex32l             ; Store 32bits
	JMP32 %express_number_done  ; done

:express_number2
	CMP_RCX_Immediate8 !64      ; IF '@' == CH
	JNE32 %express_number1      ; Othrewise try !

	LOADI32_RAX %5              ; We need 3bytes
	CALLI32 %malloc             ; Get S pointer
	SWAP_RAX_RBX                ; Put S and VALUE in place
	PUSH_RBX                    ; Protect S
	CALLI32 %hex16l             ; Store 16bits
	JMP32 %express_number_done  ; done

:express_number1
	LOADI32_RAX %3              ; We need 3bytes
	CALLI32 %malloc             ; Get S pointer
	SWAP_RAX_RBX                ; Put S and VALUE in place
	PUSH_RBX                    ; Protect S
	CALLI32 %hex8               ; Store 8bit

:express_number_done
	POP_RAX                     ; Restore S
	POP_RDX                     ; Restore RDX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	RET


;; HEX to ascii routine
;; Receives INT in RAX and CHAR* in RBX
;; Stores ascii of INT in CHAR*
;; Returns only modifying RAX
:hex64l
	PUSH_RAX                    ; Protect top 32
	CALLI32 %hex32l             ; Store it
	POP_RAX                     ; do top 32
	SHIFT_RIGHT_RAX_Immediate8 !32 ; do bottom 32 first
:hex32l
	PUSH_RAX                    ; Protect top 16
	CALLI32 %hex16l             ; Store it
	POP_RAX                     ; do top 16
	SHIFT_RIGHT_RAX_Immediate8 !16 ; do bottom 16 first
:hex16l
	PUSH_RAX                    ; Protect top byte
	CALLI32 %hex8               ; Store it
	POP_RAX                     ; do high byte
	SHIFT_RIGHT_RAX_Immediate8 !8 ; do bottom byte first
:hex8
	PUSH_RAX                    ; Protect bottom nibble
	SHIFT_RIGHT_RAX_Immediate8 !4 ; do high nibble first
	CALLI32 %hex4               ; Store it
	POP_RAX                     ; do low nibble
:hex4
	AND_RAX_Immediate8 !0xF     ; isolate nibble
	ADDI8_to_AL !48             ; convert to ascii
	CMP_AL_Immediate8 !57       ; valid digit?
	JBE8 !hex1                  ; yes
	ADDI8_to_AL !7              ; use alpha range
:hex1
	STORE8_AL_into_Address_RBX  ; store result
	ADDI8_to_RBX !1             ; next position
RET


;; Preserve_Other function
;; Receives List in RAX
;; Updates the list in place; does not modify registers
;; Uses RAX for I, RBX for I->TEXT
:Preserve_Other
	PUSH_RAX                    ; Protect RAX
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	PUSH_RDX                    ; Protect RDX
:Preserve_Other_Loop
	LOAD64_into_RBX_from_Address_RAX_Immediate8 !24 ; I->EXPRESSION
	CMP_RBX_Immediate8 !0       ; IF NULL == I->EXPRESSION
	JNE32 %Preserve_Other_Next  ; Otherwise next

	;; Needs preserving
	LOAD64_into_RBX_from_Address_RAX_Immediate8 !16 ; I->TEXT
	STORE64_RBX_into_Address_RAX_Immediate8 !24 ; I->EXPRESSION = I->TEXT

:Preserve_Other_Next
	LOAD64_into_RAX_from_Address_RAX ; I = I->NEXT
	CMP_RAX_Immediate8 !0       ; IF NULL == I
	JNE32 %Preserve_Other_Loop  ; Otherwise keep looping

	POP_RDX                     ; Restore RDX
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	POP_RAX                     ; Restore RAX
	RET


;; Print_Hex function
;; Receives list in RAX
;; walks the list and prints the I->EXPRESSION for all nodes followed by newline
;; Uses RBX for I
:Print_Hex
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	COPY_R13_to_RBX             ; I = Head
:Print_Hex_Loop
	LOAD64_into_RAX_from_Address_RBX_Immediate8 !8 ; I->TYPE
	CMP_RAX_Immediate8 !1       ; IF MACRO == I->TYPE
	JE32 %Print_Hex_Next        ; Skip

	LOAD64_into_RAX_from_Address_RBX_Immediate8 !24 ; Using EXPRESSION
	CALLI32 %File_Print         ; Print it
	LOADI32_RAX %10             ; NEWLINE
	CALLI32 %fputc              ; Append it

:Print_Hex_Next
	LOAD64_into_RBX_from_Address_RBX ; Iterate to next Token
	CMP_RBX_Immediate8 !0       ; Check for NULL
	JNE32 %Print_Hex_Loop       ; Otherwise keep looping

	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	RET


;; File_Print function
;; Receives CHAR* in RAX
;; calls fputc for every non-null char
:File_Print
	PUSH_RBX                    ; Protect RBX
	PUSH_RCX                    ; Protect RCX
	COPY_RAX_to_RBX             ; Protect S
	CMP_RAX_Immediate8 !0       ; Protect against nulls
	JE32 %File_Print_Done       ; Simply don't try to print them
:File_Print_Loop
	LOAD8_AL_from_Address_RBX   ; Read byte
	ZERO_EXTEND_AL              ; zero extend
	CMP_RAX_Immediate8 !0       ; Check for NULL
	JE32 %File_Print_Done       ; Stop at NULL

	CALLI32 %fputc              ; write it
	ADDI8_to_RBX !1             ; S = S + 1
	JMP32 %File_Print_Loop      ; Keep going

:File_Print_Done
	POP_RCX                     ; Restore RCX
	POP_RBX                     ; Restore RBX
	RET


;; fputc function
;; receives CHAR in RAX and FILE* in R14
;; writes char and returns
:fputc
	PUSH_RAX                    ; We are writing rax
	LEA_RSI                     ; Get stack address
	COPY_R14_to_RDI             ; Write to target file
	LOADI32_RAX %1              ; the syscall number for write
	PUSH_RDX                    ; Protect RDX
	LOADI32_RDX %1              ; set the size of chars we want
	PUSH_R11                    ; Protect HEAD
	SYSCALL                     ; call the Kernel
	POP_R11                     ; Restore HEAD
	POP_RDX                     ; Restore RDX
	POP_RAX                     ; Restore stack
	RET

:ELF_end
