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

	;; 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
	58                      # POP_RAX                     ; Get the number of arguments
	5F                      # POP_RDI                     ; Get the program name
	5F                      # POP_RDI                     ; Get the actual input name
	BE 00000000             # LOADI32_RSI %0              ; prepare read_only
	48C7C0 02000000         # LOADI32_RAX %2              ; the syscall number for open()
	0F05                    # SYSCALL                     ; Now open that damn file
	4989C7                  # COPY_RAX_to_R15             ; Preserve the file pointer we were given

	5F                      # POP_RDI                     ; Get the actual output name
	BE 41020000             # LOADI32_RSI %577            ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
	BA 80010000             # LOADI32_RDX %384            ; Prepare file as RW for owner only (600 in octal)
	48C7C0 02000000         # LOADI32_RAX %2              ; the syscall number for open()
	0F05                    # SYSCALL                     ; Now open that damn file
	4883F8 00               # CMP_RAX_Immediate8 !0       ; Check for missing output
	0F8F %_start_out        # JG32 %_start_out            ; Have real input
	48C7C0 01000000         # LOADI32_RAX %1              ; Use stdout

:_start_out
	4989C6                  # COPY_RAX_to_R14             ; Preserve the file pointer we were given

	48C7C0 0C000000         # LOADI32_RAX %0xC            ; the Syscall # for SYS_BRK
	BF 00000000             # LOADI32_RDI %0              ; Get current brk
	0F05                    # SYSCALL                     ; Let the kernel do the work
	4989C4                  # COPY_RAX_to_R12             ; Set our malloc pointer

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

:Done
	; program completed Successfully
	BF 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


;; 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
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	52                      # PUSH_RDX                    ; Protect RDX
:restart
	E8 %fgetc               # CALLI32 %fgetc              ; Read a char
	4883F8 FC               # CMP_RAX_Immediate8 !-4      ; Check for EOF
	0F84 %done              # JE32 %done                  ; File is collected

	480FB6C0                # ZERO_EXTEND_AL              ; We have to zero extend it to use it
	4889C1                  # COPY_RAX_to_RCX             ; Protect C

	BB &comments            # LOADI32_RBX &comments       ; Get pointer to "#;"
	E8 %In_Set              # CALLI32 %In_Set             ; Check for comments
	4883F8 01               # CMP_RAX_Immediate8 !1       ; If comments
	0F84 %Purge_LineComment # JE32 %Purge_LineComment     ; try again

	4889C8                  # COPY_RCX_to_RAX             ; put C in place for check
	BB &terminators         # LOADI32_RBX &terminators    ; Get pointer to "\n\t "
	E8 %In_Set              # CALLI32 %In_Set             ; Check for terminators
	4883F8 01               # CMP_RAX_Immediate8 !1       ; If terminator
	0F84 %restart           # JE32 %restart               ; try again

	48C7C0 20000000         # LOADI32_RAX %32             ; Malloc the struct P
	E8 %malloc              # CALLI32 %malloc             ; Get pointer to P
	4889C2                  # COPY_RAX_to_RDX             ; Protect P
	4C892A                  # STORE64_R13_into_Address_RDX ; P->NEXT = HEAD
	4989D5                  # COPY_RDX_to_R13             ; HEAD = P

	4889C8                  # COPY_RCX_to_RAX             ; put C in place for check
	BB &string_char         # LOADI32_RBX &string_char    ; Get pointer to "\"'"
	E8 %In_Set              # CALLI32 %In_Set             ; Check for string chars
	4883F8 01               # CMP_RAX_Immediate8 !1       ; If string char
	0F84 %Store_String      # JE32 %Store_String          ; Get string

	E8 %Store_Atom          # CALLI32 %Store_Atom         ; Get whole token
	E9 %restart             # JMP32 %restart

:done
	5A                      # POP_RDX                     ; Restore RDX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	C3                      # RET


;; fgetc function
;; Receives FILE* in R15
;; Returns -4 (EOF) or char in RAX
:fgetc
	48C7C0 FCFFFFFF         # LOADI32_RAX %-4             ; Put EOF in rax
	50                      # PUSH_RAX                    ; Assume bad (If nothing read, value will remain EOF)
	488D3424                # LEA_RSI                     ; Get stack address
	4C89FF                  # COPY_R15_to_RDI             ; Where are we reading from
	48C7C0 00000000         # LOADI32_RAX %0              ; the syscall number for read
	52                      # PUSH_RDX                    ; Protect RDX
	BA 01000000             # LOADI32_RDX %1              ; set the size of chars we want
	4153                    # PUSH_R11                    ; Protect r11
	0F05                    # SYSCALL                     ; call the Kernel
	415B                    # POP_R11                     ; Restore r11
	5A                      # POP_RDX                     ; Restore RDX
	58                      # POP_RAX                     ; Get either char or EOF
	C3                      # 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
	4C89E7                  # COPY_R12_to_RDI             ; Using the current pointer
	4801C7                  # ADD_RAX_to_RDI              ; Request the number of desired bytes
	48C7C0 0C000000         # LOADI32_RAX %0xC            ; the Syscall # for SYS_BRK
	51                      # PUSH_RCX                    ; Protect rcx
	4153                    # PUSH_R11                    ; Protect r11
	0F05                    # SYSCALL                     ; call the Kernel
	415B                    # POP_R11                     ; Restore r11
	59                      # POP_RCX                     ; Restore rcx
	4C89E0                  # COPY_R12_to_RAX             ; Return pointer
	4989FC                  # COPY_RDI_to_R12             ; Update pointer
	C3                      # RET


;; Purge_LineComment function
;; Reads chars until LF and jumps to restart
:Purge_LineComment
	E8 %fgetc               # CALLI32 %fgetc              ; Get a char
	480FB6C0                # ZERO_EXTEND_AL              ; Zero extend
	4883F8 0A               # CMP_RAX_Immediate8 !10      ; While not LF
	0F85 %Purge_LineComment # JNE32 %Purge_LineComment    ; Keep reading
	E9 %restart             # 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
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	52                      # PUSH_RDX                    ; Protect RDX

	48C7C0 02000000         # LOADI32_RAX %2              ; Using TYPE STRING
	488942 08               # STORE64_RAX_into_Address_RDX_Immediate8 !8 ; HEAD->TYPE = STRING
	48C7C0 00010000         # LOADI32_RAX %256            ; Malloc the string
	E8 %malloc              # CALLI32 %malloc             ; Get pointer to P
	488942 10               # STORE64_RAX_into_Address_RDX_Immediate8 !16 ; HEAD->TEXT = STRING
	4889CB                  # COPY_RCX_to_RBX             ; Protect terminator
	4889C2                  # COPY_RAX_to_RDX             ; Protect string pointer
:Store_String_Loop
	880A                    # STORE8_CL_into_Address_RDX  ; write byte
	E8 %fgetc               # CALLI32 %fgetc              ; read next char
	480FB6C0                # ZERO_EXTEND_AL              ; Zero extend it
	4889C1                  # COPY_RAX_to_RCX             ; Update C
	4883C2 01               # ADDI8_to_RDX !1             ; STRING = STRING + 1
	4839D9                  # CMP_RBX_to_RCX              ; See if we hit terminator
	0F85 %Store_String_Loop # JNE32 %Store_String_Loop    ; Otherwise keep looping

	5A                      # POP_RDX                     ; Restore RDX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	4889D0                  # COPY_RDX_to_RAX             ; return HEAD
	E9 %restart             # 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
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	52                      # PUSH_RDX                    ; Protect RDX

	48C7C0 00010000         # LOADI32_RAX %256            ; Malloc the string
	E8 %malloc              # CALLI32 %malloc             ; Get pointer to P
	488942 10               # STORE64_RAX_into_Address_RDX_Immediate8 !16 ; HEAD->TEXT = STRING
	BB &terminators         # LOADI32_RBX &terminators    ; Get pointer to "\n\t "
	4889C2                  # COPY_RAX_to_RDX             ; Protect string pointer
:Store_Atom_loop
	880A                    # STORE8_CL_into_Address_RDX  ; write byte
	E8 %fgetc               # CALLI32 %fgetc              ; read next char
	480FB6C0                # ZERO_EXTEND_AL              ; Zero extend it
	4889C1                  # COPY_RAX_to_RCX             ; Update C
	4883C2 01               # ADDI8_to_RDX !1             ; STRING = STRING + 1
	E8 %In_Set              # CALLI32 %In_Set             ; Check for terminators
	4883F8 00               # CMP_RAX_Immediate8 !0       ; Check for "\n\t "
	0F84 %Store_Atom_loop   # JE32 %Store_Atom_loop       ; Loop otherwise

	5A                      # POP_RDX                     ; Restore RDX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	4889D0                  # COPY_RDX_to_RAX             ; return HEAD
	C3                      # RET


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

	4839C8                  # CMP_RCX_to_RAX              ; See if they match
	0F84 %In_Set_True       # JE32 %In_Set_True           ; return true

	4883F9 00               # CMP_RCX_Immediate8 !0       ; Check for NULL
	0F84 %In_Set_False      # JE32 %In_Set_False          ; return false

	4883C3 01               # ADDI8_to_RBX !1             ; s = s + 1
	E9 %In_Set_loop         # JMP32 %In_Set_loop          ; Keep looping

:In_Set_True
	48C7C0 01000000         # LOADI32_RAX %1              ; Set True
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	C3                      # RET

:In_Set_False
	48C7C0 00000000         # LOADI32_RAX %0              ; Set FALSE
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	C3                      # RET

;; Char sets
:terminators
	0A092000                # "\n\t \0"

:comments
	233B00                  # "#;\0"

:string_char
	222700                  # "\"'\0"


;; Reverse_List function
;; Receives List in RAX
;; Returns the list reversed in RAX
:Reverse_List
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	4889C3                  # COPY_RAX_to_RBX             ; Set HEAD
	48C7C0 00000000         # LOADI32_RAX %0              ; ROOT = NULL
:Reverse_List_Loop
	4883FB 00                # CMP_RBX_Immediate8 !0       ; WHILE HEAD != NULL
	0F84 %Reverse_List_Done  # JE32 %Reverse_List_Done     ; Stop otherwise

	488B0B                  # LOAD64_into_RCX_from_Address_RBX ; NEXT = HEAD->NEXT
	488903                  # STORE64_RAX_into_Address_RBX ; HEAD->NEXT = ROOT
	4889D8                  # COPY_RBX_to_RAX             ; ROOT = HEAD
	4889CB                  # COPY_RCX_to_RBX             ; HEAD = NEXT
	E9 %Reverse_List_Loop   # JMP32 %Reverse_List_Loop    ; Keep Going

:Reverse_List_Done
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	C3                      # 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
	50                      # PUSH_RAX                    ; Protect RAX
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	52                      # PUSH_RDX                    ; Protect RDX
	BB &DEFINE_str          # LOADI32_RBX &DEFINE_str     ; Setup define string
	4889C1                  # COPY_RAX_to_RCX             ; I = HEAD
:Identify_Macros_Loop
	488B41 10               # LOAD64_into_RAX_from_Address_RCX_Immediate8 !16 ; I->TEXT
	E8 %match               # CALLI32 %match              ; IF "DEFINE" == I->TEXT
	4883F8 00               # CMP_RAX_Immediate8 !0       ; Check if match
	0F85 %Identify_Macros_Next # JNE32 %Identify_Macros_Next ; Skip the work

	;; Deal with MACRO
	48C7C0 01000000         # LOADI32_RAX %1              ; Using MACRO
	488941 08               # STORE64_RAX_into_Address_RCX_Immediate8 !8 ; I->TYPE = MACRO

	488B01                  # LOAD64_into_RAX_from_Address_RCX ; I->NEXT
	488B40 10               # LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; I->NEXT->TEXT
	488941 10               # STORE64_RAX_into_Address_RCX_Immediate8 !16 ; I->TEXT = I->NEXT->TEXT

	488B01                  # LOAD64_into_RAX_from_Address_RCX ; I->NEXT
	488B00                  # LOAD64_into_RAX_from_Address_RAX ; I->NEXT->NEXT
	488B40 10               # LOAD64_into_RAX_from_Address_RAX_Immediate8 !16 ; I->NEXT->NEXT->TEXT
	488941 18               # STORE64_RAX_into_Address_RCX_Immediate8 !24 ; I->EXPRESSION = I->NEXT->NEXT->TEXT

	488B01                  # LOAD64_into_RAX_from_Address_RCX ; I->NEXT
	488B00                  # LOAD64_into_RAX_from_Address_RAX ; I->NEXT->NEXT
	488B00                  # LOAD64_into_RAX_from_Address_RAX ; I->NEXT->NEXT->NEXT
	488901                  # STORE64_RAX_into_Address_RCX ; I->NEXT = I->NEXT->NEXT->NEXT

:Identify_Macros_Next
	488B09                  # LOAD64_into_RCX_from_Address_RCX ; I = I->NEXT
	4883F9 00               # CMP_RCX_Immediate8 !0       ; Check for NULL
	0F85 %Identify_Macros_Loop # JNE32 %Identify_Macros_Loop ; Keep looping otherwise

	5A                      # POP_RDX                     ; Restore RDX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	58                      # POP_RAX                     ; Restore RAX
	C3                      # RET

:DEFINE_str
	444546494E4500          # "DEFINE"


;; match function
;; Receives CHAR* in RAX and CHAR* in RBX
;; Returns 0 (TRUE) or 1 (FALSE) in RAX
:match
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	52                      # PUSH_RDX                    ; Protect RDX
	4889C1                  # COPY_RAX_to_RCX             ; S1 in place
	4889DA                  # COPY_RBX_to_RDX             ; S2 in place
:match_Loop
	8A01                    # LOAD8_AL_from_Address_RCX   ; S1[0]
	480FB6C0                # ZERO_EXTEND_AL              ; Make it useful
	8A1A                    # LOAD8_BL_from_Address_RDX   ; S2[0]
	480FB6DB                # ZERO_EXTEND_BL              ; Make it useful
	4839D8                  # CMP_RBX_to_RAX              ; See if they match
	0F85 %match_False       # JNE32 %match_False          ; If not

	4883C1 01               # ADDI8_to_RCX !1             ; S1 = S1 + 1
	4883C2 01               # ADDI8_to_RDX !1             ; S2 = S2 + 1
	4883F8 00               # CMP_RAX_Immediate8 !0       ; If reached end of string
	0F84 %match_Done        # JE32 %match_Done            ; Perfect match
	E9 %match_Loop          # JMP32 %match_Loop           ; Otherwise keep looping

:match_False
	48C7C0 01000000         # LOADI32_RAX %1              ; Return false
:match_Done
	5A                      # POP_RDX                     ; Restore RDX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	C3                      # 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
	50                      # PUSH_RAX                    ; Protect RAX
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	52                      # PUSH_RDX                    ; Protect RDX
:Line_Macro_Loop
	488B58 08               # LOAD64_into_RBX_from_Address_RAX_Immediate8 !8 ; I->TYPE
	4883FB 01               # CMP_RBX_Immediate8 !1       ; IF MACRO == I->TYPE
	0F85 %Line_Macro_Next   # JNE32 %Line_Macro_Next      ; Otherwise move on

	;; Is a macro apply
	488B58 10               # LOAD64_into_RBX_from_Address_RAX_Immediate8 !16 ; I->TEXT
	488B48 18               # LOAD64_into_RCX_from_Address_RAX_Immediate8 !24 ; I->EXPRESSION
	488B00                  # LOAD64_into_RAX_from_Address_RAX ; I->NEXT
	E8 %Set_Expression      # CALLI32 %Set_Expression     ; Apply it
	E9 %Line_Macro_Loop     # JMP32 %Line_Macro_Loop      ; Move on to next

:Line_Macro_Next
	488B00                  # LOAD64_into_RAX_from_Address_RAX ; I->NEXT
	4883F8 00               # CMP_RAX_Immediate8 !0       ; Check for NULL
	0F85 %Line_Macro_Loop   # JNE32 %Line_Macro_Loop      ; Keep going

	5A                      # POP_RDX                     ; Restore RDX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	58                      # POP_RAX                     ; Restore RAX
	C3                      # 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
	50                      # PUSH_RAX                    ; Protect RAX
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	52                      # PUSH_RDX                    ; Protect RDX
	4889C2                  # COPY_RAX_to_RDX             ; Set I
:Set_Expression_Loop
	488B42 08               # LOAD64_into_RAX_from_Address_RDX_Immediate8 !8 ; I->TYPE
	4883F8 01               # CMP_RAX_Immediate8 !1       ; IF MACRO == I->TYPE
	0F84 %Set_Expression_Next # JE32 %Set_Expression_Next   ; Ignore and move on

	488B42 10               # LOAD64_into_RAX_from_Address_RDX_Immediate8 !16 ; I->TEXT
	E8 %match               # CALLI32 %match              ; Check for match
	4883F8 00               # CMP_RAX_Immediate8 !0       ; If match
	0F85 %Set_Expression_Next # JNE32 %Set_Expression_Next  ; Otherwise next

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

:Set_Expression_Next
	488B12                  # LOAD64_into_RDX_from_Address_RDX ; I = I->NEXT
	4883FA 00               # CMP_RDX_Immediate8 !0       ; IF NULL == I
	0F85 %Set_Expression_Loop # JNE32 %Set_Expression_Loop  ; Otherwise keep looping

	5A                      # POP_RDX                     ; Restore RDX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	58                      # POP_RAX                     ; Restore RAX
	C3                      # 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
	50                      # PUSH_RAX                    ; Protect RAX
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	52                      # PUSH_RDX                    ; Protect RDX
	4889C1                  # COPY_RAX_to_RCX             ; I = HEAD
:Process_String_loop
	488B41 08               # LOAD64_into_RAX_from_Address_RCX_Immediate8 !8 ; I->TYPE
	4883F8 02               # CMP_RAX_Immediate8 !2       ; IF STRING == I->TYPE
	0F85 %Process_String_Next # JNE32 %Process_String_Next  ; Skip to next

	488B59 10               # LOAD64_into_RBX_from_Address_RCX_Immediate8 !16 ; I->TEXT
	8A03                    # LOAD8_AL_from_Address_RBX   ; I->TEXT[0]
	480FB6C0                # ZERO_EXTEND_AL              ; make it useful
	4883F8 27               # CMP_RAX_Immediate8 !39      ; IF '\'' == I->TEXT[0]
	0F85 %Process_String_Raw # JNE32 %Process_String_Raw   ; Deal with "\""

	;; Deal with '\''
	4883C3 01               # ADDI8_to_RBX !1             ; I->TEXT + 1
	488959 18               # STORE64_RBX_into_Address_RCX_Immediate8 !24 ; I->EXPRESSION = I->TEXT + 1
	E9 %Process_String_Next # JMP32 %Process_String_Next  ; Move on to next

:Process_String_Raw
	4889D8                  # COPY_RBX_to_RAX             ; Get length of I->TEXT
	E8 %string_length       # CALLI32 %string_length      ; Do it
	48C1E8 02               # SHIFT_RIGHT_RAX_Immediate8 !2 ; LENGTH = LENGTH >> 2
	4883C0 01               # ADDI8_to_RAX !1             ; LENGTH = LENGTH + 1
	48C1E0 03               # SHIFT_LEFT_RAX_Immediate8 !3 ; LENGTH = LENGTH << 3
	E8 %malloc              # CALLI32 %malloc             ; Get string
	4889DA                  # COPY_RBX_to_RDX             ; S = I->TEXT
	4883C2 01               # ADDI8_to_RDX !1             ; S = S + 1
	488941 18               # STORE64_RAX_into_Address_RCX_Immediate8 !24 ; I->EXPRESSION = hexify
	4889C3                  # COPY_RAX_to_RBX             ; Put hexify buffer in rbx

:Process_String_Raw_Loop
	8A02                    # LOAD8_AL_from_Address_RDX   ; Read 1 chars
	480FB6C0                # ZERO_EXTEND_AL              ; Make it useful
	4883C2 01               # ADDI8_to_RDX !1             ; S = S + 1
	3C 00                   # CMP_AL_Immediate8 !0        ; Check for NULL
	9C                      # PUSH_FLAGS                  ; Protect condition
	E8 %hex8                # CALLI32 %hex8               ; write them all
	9D                      # POP_FLAGS                   ; restore condition
	0F85 %Process_String_Raw_Loop # JNE32 %Process_String_Raw_Loop ; Keep looping

:Process_String_Next
	488B09                  # LOAD64_into_RCX_from_Address_RCX ; I = I->NEXT
	4883F9 00               # CMP_RCX_Immediate8 !0       ; IF NULL == I
	0F85 %Process_String_loop # JNE32 %Process_String_loop  ; Otherwise keep looping

	5A                      # POP_RDX                     ; Restore RDX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	58                      # POP_RAX                     ; Restore RAX
	C3                      # 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
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	4889C3                  # COPY_RAX_to_RBX             ; Set S
	B9 00000000             # LOADI32_RCX %0              ; INDEX = 0
:string_length_loop
	8A040B                  # LOAD8_AL_from_Address_RBX_Index_RCX ; S[0]
	480FB6C0                # ZERO_EXTEND_AL              ; make it useful
	4883F8 00               # CMP_RAX_Immediate8 !0       ; IF NULL == S[0]
	0F84 %string_length_done # JE32 %string_length_done    ; Stop

	4883C1 01               # ADDI8_to_RCX !1             ; INDEX = INDEX + 1
	E9 %string_length_loop  # JMP32 %string_length_loop   ; Keep going

:string_length_done
	4889C8                  # COPY_RCX_to_RAX             ; RETURN INDEX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	C3                      # 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
	50                      # PUSH_RAX                    ; Protect RAX
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	52                      # PUSH_RDX                    ; Protect RDX
	4889C2                  # COPY_RAX_to_RDX             ; I = HEAD
:Eval_Immediates_Loop
	;; Check for MACRO
	488B42 08               # LOAD64_into_RAX_from_Address_RDX_Immediate8 !8 ; I->TYPE
	4883F8 01               # CMP_RAX_Immediate8 !1       ; IF MACRO == I-TYPE
	0F84 %Eval_Immediates_Next # JE32 %Eval_Immediates_Next  ; Skip to next

	;; Check for NULL EXPRESSION
	488B42 18               # LOAD64_into_RAX_from_Address_RDX_Immediate8 !24 ; I->EXPRESSION
	4883F8 00               # CMP_RAX_Immediate8 !0       ; IF NULL == I->EXPRESSION
	0F85 %Eval_Immediates_Next # JNE32 %Eval_Immediates_Next ; Skip to next

	;; Check if number
	488B42 10               # LOAD64_into_RAX_from_Address_RDX_Immediate8 !16 ; I->TEXT
	8A18                    # LOAD8_BL_from_Address_RAX   ; I->TEXT[0]
	480FB6DB                # ZERO_EXTEND_BL              ; Extend to use
	4883C0 01               # ADDI8_to_RAX !1             ; I->TEXT + 1
	8A08                    # LOAD8_CL_from_Address_RAX   ; I->TEXT[1]
	480FB6C9                # ZERO_EXTEND_CL              ; Extend to use
	E8 %numerate_string     # CALLI32 %numerate_string    ; Convert string to INT
	4883F8 00               # CMP_RAX_Immediate8 !0       ; IF 0 == numerate_number(I->TEXT + 1)
	0F85 %Eval_Immediates_value # JNE32 %Eval_Immediates_value ; Has a value

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

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

:Eval_Immediates_Next
	488B12                  # LOAD64_into_RDX_from_Address_RDX ; I = I->NEXT
	4883FA 00               # CMP_RDX_Immediate8 !0       ; IF NULL == I
	0F85 %Eval_Immediates_Loop # JNE32 %Eval_Immediates_Loop ; Otherwise keep looping

	5A                      # POP_RDX                     ; Restore RDX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	58                      # POP_RAX                     ; Restore RAX
	C3                      # 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
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	52                      # PUSH_RDX                    ; Protect RDX
	56                      # PUSH_RSI                    ; Protect RSI
	4889C3                  # COPY_RAX_to_RBX             ; put S in correct place
	48C7C0 00000000         # LOADI32_RAX %0              ; Initialize to Zero
:numerate_string_loop
	8A4B 01                 # LOAD8_CL_from_Address_RBX_Immediate8 !1 ; S[1]
	480FB6C9                # ZERO_EXTEND_CL              ; make it useful
	4883F9 78               # CMP_RCX_Immediate8 !120     ; IF 'x' == S[1]
	0F84 %numerate_hex      # JE32 %numerate_hex          ; Deal with hex input

	;; Assume decimal input
	BE 00000000             # LOADI32_RSI %0              ; Assume no negation
	8A0B                    # LOAD8_CL_from_Address_RBX   ; S[0]
	480FB6C9                # ZERO_EXTEND_CL              ; make it useful
	4883F9 2D               # CMP_RCX_Immediate8 !45      ; IF '-' == S[0]
	0F85 %numerate_decimal  # JNE32 %numerate_decimal     ; Skip negation

	BE 01000000             # LOADI32_RSI %1              ; Set FLAG
	4883C3 01               # ADDI8_to_RBX !1             ; S = S + 1

:numerate_decimal
	8A0B                    # LOAD8_CL_from_Address_RBX   ; S[0]
	480FB6C9                # ZERO_EXTEND_CL              ; make it useful
	4883F9 00               # CMP_RCX_Immediate8 !0       ; IF NULL == S[0]
	0F84 %numerate_decimal_done # JE32 %numerate_decimal_done ; We are done

	486BC0 0A               # IMUL_RAX_Immediate8 !10     ; VALUE = VALUE * 10
	4883E9 30               # SUBI8_RCX !48               ; CH = CH - '0'
	4883F9 09               # CMP_RCX_Immediate8 !9       ; Check for illegal
	0F8F %numerate_string_fail # JG32 %numerate_string_fail  ; If CH > '9'
	4883F9 00               # CMP_RCX_Immediate8 !0       ; Check for illegal
	0F8C %numerate_string_fail # JL32 %numerate_string_fail  ; IF CH < 0
	4801C8                  # ADD_RCX_to_RAX              ; VALUE = VALUE + CH
	4883C3 01               # ADDI8_to_RBX !1             ; S = S + 1
	E9 %numerate_decimal    # JMP32 %numerate_decimal     ; Keep looping

:numerate_decimal_done
	4883FE 01               # CMP_RSI_Immediate8 !1       ; Check if need to negate
	0F85 %numerate_string_done # JNE32 %numerate_string_done ; Nope

	486BC0 FF               # IMUL_RAX_Immediate8 !-1     ; VALUE = VALUE * -1
	E9 %numerate_string_done # JMP32 %numerate_string_done ; Done

:numerate_hex
	4883C3 02               # ADDI8_to_RBX !2             ; S = S + 2
:numerate_hex_loop
	8A0B                    # LOAD8_CL_from_Address_RBX   ; S[0]
	480FB6C9                # ZERO_EXTEND_CL              ; make it useful
	4883F9 00               # CMP_RCX_Immediate8 !0       ; IF NULL == S[0]
	0F84 %numerate_string_done # JE32 %numerate_string_done  ; We are done

	48C1E0 04               # SHIFT_LEFT_RAX_Immediate8 !4 ; VALUE = VALUE << 4
	4883E9 30               # SUBI8_RCX !48               ; CH = CH - '0'
	4883F9 0A               # CMP_RCX_Immediate8 !10      ; IF 10 >= CH
	0F8C %numerate_hex_digit # JL32 %numerate_hex_digit    ; NO
	4883E9 07               # SUBI8_RCX !7                ; Push A-F into range
:numerate_hex_digit
	4883F9 0F               # CMP_RCX_Immediate8 !15      ; Check for illegal
	0F8F %numerate_string_fail # JG32 %numerate_string_fail  ; If CH > 'F'
	4883F9 00               # CMP_RCX_Immediate8 !0       ; Check for illegal
	0F8C %numerate_string_fail # JL32 %numerate_string_fail  ; IF CH < 0
	4801C8                  # ADD_RCX_to_RAX              ; VALUE = VALUE + CH
	4883C3 01               # ADDI8_to_RBX !1             ; S = S + 1
	E9 %numerate_hex_loop   # JMP32 %numerate_hex_loop    ; Keep looping

:numerate_string_fail
	48C7C0 00000000         # LOADI32_RAX %0              ; return ZERO

:numerate_string_done
	5E                      # POP_RSI                     ; Restore RSI
	5A                      # POP_RDX                     ; Restore RDX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	C3                      # 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
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	52                      # PUSH_RDX                    ; Protect RDX
	4889D9                  # COPY_RBX_to_RCX             ; Put CH in right place
	4889C3                  # COPY_RAX_to_RBX             ; Protect VALUE
	4883F9 25               # CMP_RCX_Immediate8 !37      ; IF '%' == CH
	0F85 %express_number2   # JNE32 %express_number2      ; Otherwise try @

	48C7C0 09000000         # LOADI32_RAX %9              ; We need 3bytes
	E8 %malloc              # CALLI32 %malloc             ; Get S pointer
	4893                    # SWAP_RAX_RBX                ; Put S and VALUE in place
	53                      # PUSH_RBX                    ; Protect S
	E8 %hex32l              # CALLI32 %hex32l             ; Store 32bits
	E9 %express_number_done # JMP32 %express_number_done  ; done

:express_number2
	4883F9 40               # CMP_RCX_Immediate8 !64      ; IF '@' == CH
	0F85 %express_number1   # JNE32 %express_number1      ; Othrewise try !

	48C7C0 05000000         # LOADI32_RAX %5              ; We need 3bytes
	E8 %malloc              # CALLI32 %malloc             ; Get S pointer
	4893                    # SWAP_RAX_RBX                ; Put S and VALUE in place
	53                      # PUSH_RBX                    ; Protect S
	E8 %hex16l              # CALLI32 %hex16l             ; Store 16bits
	E9 %express_number_done # JMP32 %express_number_done  ; done

:express_number1
	48C7C0 03000000         # LOADI32_RAX %3              ; We need 3bytes
	E8 %malloc              # CALLI32 %malloc             ; Get S pointer
	4893                    # SWAP_RAX_RBX                ; Put S and VALUE in place
	53                      # PUSH_RBX                    ; Protect S
	E8 %hex8                # CALLI32 %hex8               ; Store 8bit

:express_number_done
	58                      # POP_RAX                     ; Restore S
	5A                      # POP_RDX                     ; Restore RDX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	C3                      # RET


;; HEX to ascii routine
;; Receives INT in RAX and CHAR* in RBX
;; Stores ascii of INT in CHAR*
;; Returns only modifying RAX
:hex64l
	50                      # PUSH_RAX                    ; Protect top 32
	E8 %hex32l              # CALLI32 %hex32l             ; Store it
	58                      # POP_RAX                     ; do top 32
	48C1E8 20               # SHIFT_RIGHT_RAX_Immediate8 !32 ; do bottom 32 first
:hex32l
	50                      # PUSH_RAX                    ; Protect top 16
	E8 %hex16l              # CALLI32 %hex16l             ; Store it
	58                      # POP_RAX                     ; do top 16
	48C1E8 10               # SHIFT_RIGHT_RAX_Immediate8 !16 ; do bottom 16 first
:hex16l
	50                      # PUSH_RAX                    ; Protect top byte
	E8 %hex8                # CALLI32 %hex8               ; Store it
	58                      # POP_RAX                     ; do high byte
	48C1E8 08               # SHIFT_RIGHT_RAX_Immediate8 !8 ; do bottom byte first
:hex8
	50                      # PUSH_RAX                    ; Protect bottom nibble
	48C1E8 04               # SHIFT_RIGHT_RAX_Immediate8 !4 ; do high nibble first
	E8 %hex4                # CALLI32 %hex4               ; Store it
	58                      # POP_RAX                     ; do low nibble
:hex4
	4883E0 0F               # AND_RAX_Immediate8 !0xF     ; isolate nibble
	04 30                   # ADDI8_to_AL !48             ; convert to ascii
	3C 39                   # CMP_AL_Immediate8 !57       ; valid digit?
	76 !hex1                # JBE8 !hex1                  ; yes
	04 07                   # ADDI8_to_AL !7              ; use alpha range
:hex1
	8803                    # STORE8_AL_into_Address_RBX  ; store result
	4883C3 01               # ADDI8_to_RBX !1             ; next position
	C3                      # 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
	50                      # PUSH_RAX                    ; Protect RAX
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	52                      # PUSH_RDX                    ; Protect RDX
:Preserve_Other_Loop
	488B58 18               # LOAD64_into_RBX_from_Address_RAX_Immediate8 !24 ; I->EXPRESSION
	4883FB 00               # CMP_RBX_Immediate8 !0       ; IF NULL == I->EXPRESSION
	0F85 %Preserve_Other_Next # JNE32 %Preserve_Other_Next  ; Otherwise next

	;; Needs preserving
	488B58 10               # LOAD64_into_RBX_from_Address_RAX_Immediate8 !16 ; I->TEXT
	488958 18               # STORE64_RBX_into_Address_RAX_Immediate8 !24 ; I->EXPRESSION = I->TEXT

:Preserve_Other_Next
	488B00                  # LOAD64_into_RAX_from_Address_RAX ; I = I->NEXT
	4883F8 00               # CMP_RAX_Immediate8 !0       ; IF NULL == I
	0F85 %Preserve_Other_Loop # JNE32 %Preserve_Other_Loop  ; Otherwise keep looping

	5A                      # POP_RDX                     ; Restore RDX
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	58                      # POP_RAX                     ; Restore RAX
	C3                      # 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
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	4C89EB                  # COPY_R13_to_RBX             ; I = Head
:Print_Hex_Loop
	488B43 08               # LOAD64_into_RAX_from_Address_RBX_Immediate8 !8 ; I->TYPE
	4883F8 01               # CMP_RAX_Immediate8 !1       ; IF MACRO == I->TYPE
	0F84 %Print_Hex_Next    # JE32 %Print_Hex_Next        ; Skip

	488B43 18               # LOAD64_into_RAX_from_Address_RBX_Immediate8 !24 ; Using EXPRESSION
	E8 %File_Print          # CALLI32 %File_Print         ; Print it
	48C7C0 0A000000         # LOADI32_RAX %10             ; NEWLINE
	E8 %fputc               # CALLI32 %fputc              ; Append it

:Print_Hex_Next
	488B1B                  # LOAD64_into_RBX_from_Address_RBX ; Iterate to next Token
	4883FB 00               # CMP_RBX_Immediate8 !0       ; Check for NULL
	0F85 %Print_Hex_Loop    # JNE32 %Print_Hex_Loop       ; Otherwise keep looping

	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	C3                      # RET


;; File_Print function
;; Receives CHAR* in RAX
;; calls fputc for every non-null char
:File_Print
	53                      # PUSH_RBX                    ; Protect RBX
	51                      # PUSH_RCX                    ; Protect RCX
	4889C3                  # COPY_RAX_to_RBX             ; Protect S
	4883F8 00               # CMP_RAX_Immediate8 !0       ; Protect against nulls
	0F84 %File_Print_Done   # JE32 %File_Print_Done       ; Simply don't try to print them
:File_Print_Loop
	8A03                    # LOAD8_AL_from_Address_RBX   ; Read byte
	480FB6C0                # ZERO_EXTEND_AL              ; zero extend
	4883F8 00               # CMP_RAX_Immediate8 !0       ; Check for NULL
	0F84 %File_Print_Done   # JE32 %File_Print_Done       ; Stop at NULL

	E8 %fputc               # CALLI32 %fputc              ; write it
	4883C3 01               # ADDI8_to_RBX !1             ; S = S + 1
	E9 %File_Print_Loop     # JMP32 %File_Print_Loop      ; Keep going

:File_Print_Done
	59                      # POP_RCX                     ; Restore RCX
	5B                      # POP_RBX                     ; Restore RBX
	C3                      # RET


;; fputc function
;; receives CHAR in RAX and FILE* in R14
;; writes char and returns
:fputc
	50                      # PUSH_RAX                    ; We are writing rax
	488D3424                # LEA_RSI                     ; Get stack address
	4C89F7                  # COPY_R14_to_RDI             ; Write to target file
	48C7C0 01000000         # LOADI32_RAX %1              ; the syscall number for write
	52                      # PUSH_RDX                    ; Protect RDX
	BA 01000000             # LOADI32_RDX %1              ; set the size of chars we want
	4153                    # PUSH_R11                    ; Protect HEAD
	0F05                    # SYSCALL                     ; call the Kernel
	415B                    # POP_R11                     ; Restore HEAD
	5A                      # POP_RDX                     ; Restore RDX
	58                      # POP_RAX                     ; Restore stack
	C3                      # RET

:ELF_end
