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

	;; Register usage:
	;; EAX, ECX, EBX => Temps
	;; EDI => MALLOC
	;; EBP => HEAD
	;; [Output] => Output_file
	;; [Input] => 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

DEFINE ADDI8_AL 04
DEFINE ADDI8_EAX 83C0
DEFINE ADDI8_EBX 83C3
DEFINE ADDI8_ECX 83C1
DEFINE ADDI8_EDX 83C2
DEFINE ADD_EAX_to_EBX 01C3
DEFINE ADD_ECX_to_EAX 01C8
DEFINE ANDI8_EAX 83E0
DEFINE CALL32 E8
DEFINE CMPI8_AL 3C
DEFINE CMPI8_EAX 83F8
DEFINE CMPI8_EBX 83FB
DEFINE CMPI8_ECX 83F9
DEFINE CMPI8_EDI 83FF
DEFINE CMPI8_EDX 83FA
DEFINE CMP_EAX_EBX 39D8
DEFINE CMP_EAX_ECX 39C8
DEFINE CMP_EBX_ECX 39D9
DEFINE COPY_EAX_to_EBP 89C5
DEFINE COPY_EAX_to_EBX 89C3
DEFINE COPY_EAX_to_ECX 89C1
DEFINE COPY_EAX_to_EDI 89C7
DEFINE COPY_EAX_to_EDX 89C2
DEFINE COPY_EBP_to_EAX 89E8
DEFINE COPY_EBP_to_EBX 89EB
DEFINE COPY_EBX_to_EAX 89D8
DEFINE COPY_EBX_to_ECX 89D9
DEFINE COPY_EBX_to_EDI 89DF
DEFINE COPY_EBX_to_EDX 89DA
DEFINE COPY_ECX_to_EAX 89C8
DEFINE COPY_ECX_to_EBX 89CB
DEFINE COPY_EDI_to_EAX 89F8
DEFINE COPY_EDI_to_EBX 89FB
DEFINE COPY_EDX_to_EAX 89D0
DEFINE COPY_EDX_to_EBP 89D5
DEFINE IMULI8_EAX 6BC0
DEFINE INT_80 CD80
DEFINE JBE32 0F86
DEFINE JE32 0F84
DEFINE JG32 0F8F
DEFINE JL32 0F8C
DEFINE JMP32 E9
DEFINE JNE32 0F85
DEFINE LEA32_ECX_from_esp 8D0C24
DEFINE LOAD32_Absolute32_ebx 8B1D
DEFINE LOAD32_EAX_from_EAX 8B00
DEFINE LOAD32_EAX_from_EAX_Immediate8 8B40
DEFINE LOAD32_EAX_from_EBX_Immediate8 8B43
DEFINE LOAD32_EAX_from_ECX 8B01
DEFINE LOAD32_EAX_from_ECX_Immediate8 8B41
DEFINE LOAD32_EAX_from_EDX_Immediate8 8B42
DEFINE LOAD32_EBX_from_EAX_Immediate8 8B58
DEFINE LOAD32_EBX_from_EBX 8B1B
DEFINE LOAD32_EBX_from_ECX_Immediate8 8B59
DEFINE LOAD32_ECX_from_EAX_Immediate8 8B48
DEFINE LOAD32_ECX_from_EBX 8B0B
DEFINE LOAD32_ECX_from_ECX 8B09
DEFINE LOAD32_EDX_from_EDX 8B12
DEFINE LOAD8_al_from_EBX 8A03
DEFINE LOAD8_al_from_EBX_indexed_ECX 8A040B
DEFINE LOAD8_al_from_ECX 8A01
DEFINE LOAD8_al_from_EDX 8A02
DEFINE LOAD8_bl_from_EAX 8A18
DEFINE LOAD8_bl_from_EDX 8A1A
DEFINE LOAD8_cl_from_EAX 8A08
DEFINE LOAD8_cl_from_EBX 8A0B
DEFINE LOAD8_cl_from_EBX_Immediate8 8A4B
DEFINE LOADI32_EAX B8
DEFINE LOADI32_EBX BB
DEFINE LOADI32_ECX B9
DEFINE LOADI32_EDI BF
DEFINE LOADI32_EDX BA
DEFINE MOVZX_al 0FB6C0
DEFINE MOVZX_bl 0FB6DB
DEFINE MOVZX_cl 0FB6C9
DEFINE NULL 00000000
DEFINE POP_EAX 58
DEFINE POP_EBX 5B
DEFINE POP_ECX 59
DEFINE POP_EDI 5F
DEFINE POP_EDX 5A
DEFINE POP_FLAGS 9D
DEFINE PUSH_EAX 50
DEFINE PUSH_EBX 53
DEFINE PUSH_ECX 51
DEFINE PUSH_EDI 57
DEFINE PUSH_EDX 52
DEFINE PUSH_FLAGS 9C
DEFINE RET C3
DEFINE SHLI8_EAX C1E0
DEFINE SHRI8_EAX C1E8
DEFINE STORE32_Absolute32_eax A3
DEFINE STORE32_EAX_into_Address_EBX 8903
DEFINE STORE32_EAX_into_Address_ECX 8901
DEFINE STORE32_EAX_into_Address_ECX_Immediate8 8941
DEFINE STORE32_EAX_into_Address_EDX_Immediate8 8942
DEFINE STORE32_EBP_into_Address_EDX 892A
DEFINE STORE32_EBX_into_Address_EAX_Immediate8 8958
DEFINE STORE32_EBX_into_Address_ECX_Immediate8 8959
DEFINE STORE32_ECX_into_Address_EDX_Immediate8 894A
DEFINE STORE8_al_into_Address_EBX 8803
DEFINE STORE8_cl_into_Address_EDX 880A
DEFINE SUBI8_ECX 83E9
DEFINE XCHG_EAX_EBX 93

:_start
	POP_EAX                     ; Get the number of arguments
	POP_EBX                     ; Get the program name
	POP_EBX                     ; Get the actual input name
	LOADI32_ECX %0              ; prepare read_only
	LOADI32_EAX %5              ; the syscall number for open()
	INT_80                      ; Now open that damn file
	STORE32_Absolute32_eax &Input ; Preserve the file pointer we were given

	POP_EBX                     ; Get the actual output name
	LOADI32_ECX %577            ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
	LOADI32_EDX %384            ; Prepare file as RWX for owner only (600 in octal)
	LOADI32_EAX %5              ; the syscall number for open()
	INT_80                      ; Now open that damn file
	CMPI8_EAX !0                ; Check for missing output
	JG32 %_start_out            ; Have real input
	LOADI32_EAX %1              ; Use stdout

:_start_out
	STORE32_Absolute32_eax &Output ; Preserve the file pointer we were given

	LOADI32_EAX %45             ; the Syscall # for SYS_BRK
	LOADI32_EBX %0              ; Get current brk
	INT_80                      ; Let the kernel do the work
	COPY_EAX_to_EDI             ; Set our malloc pointer

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

:Done
	; program completed Successfully
	LOADI32_EBX %0              ; All is well
	LOADI32_EAX %1              ; put the exit syscall number in eax
	INT_80                      ; Call it a good day


;; Tokenize_Line Function
;; Using input file [Input] and Head EBP
;; Creates a linked list of structs
;; Uses EBX for in_set strings, ECX for Int C and EDX for Struct Token* p
:Tokenize_Line
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	PUSH_EDX                    ; Protect EDX
:restart
	CALL32 %fgetc               ; Read a char
	CMPI8_EAX !-4               ; Check for EOF
	JE32 %done                  ; File is collected

	MOVZX_al                    ; We have to zero extend it to use it
	COPY_EAX_to_ECX             ; Protect C

	LOADI32_EBX &comments       ; Get pointer to "#;"
	CALL32 %In_Set              ; Check for comments
	CMPI8_EAX !1                ; If comments
	JE32 %Purge_LineComment     ; try again

	COPY_ECX_to_EAX             ; put C in place for check
	LOADI32_EBX &terminators    ; Get pointer to "\n\t "
	CALL32 %In_Set              ; Check for terminators
	CMPI8_EAX !1                ; If terminator
	JE32 %restart               ; try again

	LOADI32_EAX %32             ; Malloc the struct P
	CALL32 %malloc              ; Get pointer to P
	COPY_EAX_to_EDX             ; Protect P
	STORE32_EBP_into_Address_EDX ; P->NEXT = HEAD
	COPY_EDX_to_EBP             ; HEAD = P

	COPY_ECX_to_EAX             ; put C in place for check
	LOADI32_EBX &string_char    ; Get pointer to "\"'"
	CALL32 %In_Set              ; Check for string chars
	CMPI8_EAX !1                ; If string char
	JE32 %Store_String          ; Get string

	CALL32 %Store_Atom          ; Get whole token
	JMP32 %restart

:done
	POP_EDX                     ; Restore EDX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	RET


;; fgetc function
;; Receives FILE* in [Input]
;; Returns -4 (EOF) or char in EAX
:fgetc
	PUSH_EDX                    ; Protect EDX
	PUSH_ECX                    ; Protect ECX
	PUSH_EBX                    ; Protect EBX
	LOADI32_EAX %-4             ; Put EOF in eax
	PUSH_EAX                    ; Assume bad (If nothing read, value will remain EOF)
	LEA32_ECX_from_esp          ; Get stack address
	LOAD32_Absolute32_ebx &Input ; Where are we reading from
	LOADI32_EAX %3              ; the syscall number for read
	LOADI32_EDX %1              ; set the size of chars we want
	INT_80                      ; call the Kernel
	POP_EAX                     ; Get either char or EOF
	POP_EBX                     ; Restore EBX
	POP_ECX                     ; Restore ECX
	POP_EDX                     ; Restore EDX
	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 EDI to be initialized and EAX to have the number of desired bytes
:malloc
	PUSH_EDX                    ; Protect EDX
	PUSH_ECX                    ; Protect ECX
	PUSH_EBX                    ; Protect EBX

	COPY_EDI_to_EBX             ; Using the current pointer
	ADD_EAX_to_EBX              ; Request the number of desired bytes
	LOADI32_EAX %45             ; the Syscall # for SYS_BRK
	INT_80                      ; call the Kernel
	COPY_EDI_to_EAX             ; Return pointer
	COPY_EBX_to_EDI             ; Update pointer

	POP_EBX                     ; Restore EBX
	POP_ECX                     ; Restore ECX
	POP_EDX                     ; Restore EDX
	RET


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


;; Store_String Function
;; Receives C in ECX, HEAD in EDX and Input file in [Output]
;; Uses EBX for terminator, ECX for C and EDX for string
:Store_String
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	PUSH_EDX                    ; Protect EDX

	LOADI32_EAX %2              ; Using TYPE STRING
	STORE32_EAX_into_Address_EDX_Immediate8 !8 ; HEAD->TYPE = STRING
	LOADI32_EAX %256            ; Malloc the string
	CALL32 %malloc              ; Get pointer to P
	STORE32_EAX_into_Address_EDX_Immediate8 !16 ; HEAD->TEXT = STRING
	COPY_ECX_to_EBX             ; Protect terminator
	COPY_EAX_to_EDX             ; Protect string pointer
:Store_String_Loop
	STORE8_cl_into_Address_EDX  ; write byte
	CALL32 %fgetc               ; read next char
	MOVZX_al                    ; Zero extend it
	COPY_EAX_to_ECX             ; Update C
	ADDI8_EDX !1                ; STRING = STRING + 1
	CMP_EBX_ECX                 ; See if we hit terminator
	JNE32 %Store_String_Loop    ; Otherwise keep looping

	POP_EDX                     ; Restore EDX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	COPY_EDX_to_EAX             ; return HEAD
	JMP32 %restart


;; Store_Atom Function
;; Receives C in ECX, HEAD in EDX and Input file in [Input]
;; Uses EBX for in_set strings, ECX for C and EDX for string
:Store_Atom
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	PUSH_EDX                    ; Protect EDX

	LOADI32_EAX %256            ; Malloc the string
	CALL32 %malloc              ; Get pointer to P
	STORE32_EAX_into_Address_EDX_Immediate8 !16 ; HEAD->TEXT = STRING
	LOADI32_EBX &terminators    ; Get pointer to "\n\t "
	COPY_EAX_to_EDX             ; Protect string pointer
:Store_Atom_loop
	STORE8_cl_into_Address_EDX  ; write byte
	CALL32 %fgetc               ; read next char
	MOVZX_al                    ; Zero extend it
	COPY_EAX_to_ECX             ; Update C
	ADDI8_EDX !1                ; STRING = STRING + 1
	CALL32 %In_Set              ; Check for terminators
	CMPI8_EAX !0                ; Check for "\n\t "
	JE32 %Store_Atom_loop       ; Loop otherwise

	POP_EDX                     ; Restore EDX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	COPY_EDX_to_EAX             ; return HEAD
	RET


;; In_Set function
;; Receives Char C in EAX and CHAR* in EBX
;; Returns 1 if true, zero if false in EAX
:In_Set
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
:In_Set_loop
	LOAD8_cl_from_EBX           ; Read char
	MOVZX_cl                    ; Zero extend it

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

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

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

:In_Set_True
	LOADI32_EAX %1              ; Set True
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	RET

:In_Set_False
	LOADI32_EAX %0              ; Set FALSE
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	RET

;; Char sets
:terminators
"
	 "

:comments
";#"

:string_char
'22 27 00'


;; Reverse_List function
;; Receives List in EAX
;; Returns the list reversed in EAX
:Reverse_List
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	COPY_EAX_to_EBX             ; Set HEAD
	LOADI32_EAX %0              ; ROOT = NULL
:Reverse_List_Loop
	CMPI8_EBX !0                ; WHILE HEAD != NULL
	JE32 %Reverse_List_Done     ; Stop otherwise

	LOAD32_ECX_from_EBX         ; NEXT = HEAD->NEXT
	STORE32_EAX_into_Address_EBX ; HEAD->NEXT = ROOT
	COPY_EBX_to_EAX             ; ROOT = HEAD
	COPY_ECX_to_EBX             ; HEAD = NEXT
	JMP32 %Reverse_List_Loop    ; Keep Going

:Reverse_List_Done
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	RET


;; Identify_Macros function
;; Receives List in EAX
;; Updates the list in place; does not modify registers
;; Uses EBX for DEFINE, ECX for I
:Identify_Macros
	PUSH_EAX                    ; Protect EAX
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	PUSH_EDX                    ; Protect EDX
	LOADI32_EBX &DEFINE_str     ; Setup define string
	COPY_EAX_to_ECX             ; I = HEAD
:Identify_Macros_Loop
	LOAD32_EAX_from_ECX_Immediate8 !16 ; I->TEXT
	CALL32 %match               ; IF "DEFINE" == I->TEXT
	CMPI8_EAX !0                ; Check if match
	JNE32 %Identify_Macros_Next ; Skip the work

	;; Deal with MACRO
	LOADI32_EAX %1              ; Using MACRO
	STORE32_EAX_into_Address_ECX_Immediate8 !8 ; I->TYPE = MACRO

	LOAD32_EAX_from_ECX         ; I->NEXT
	LOAD32_EAX_from_EAX_Immediate8 !16 ; I->NEXT->TEXT
	STORE32_EAX_into_Address_ECX_Immediate8 !16 ; I->TEXT = I->NEXT->TEXT

	LOAD32_EAX_from_ECX         ; I->NEXT
	LOAD32_EAX_from_EAX         ; I->NEXT->NEXT
	LOAD32_EAX_from_EAX_Immediate8 !16 ; I->NEXT->NEXT->TEXT
	STORE32_EAX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = I->NEXT->NEXT->TEXT

	LOAD32_EAX_from_ECX         ; I->NEXT
	LOAD32_EAX_from_EAX         ; I->NEXT->NEXT
	LOAD32_EAX_from_EAX         ; I->NEXT->NEXT->NEXT
	STORE32_EAX_into_Address_ECX ; I->NEXT = I->NEXT->NEXT->NEXT

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

	POP_EDX                     ; Restore EDX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	POP_EAX                     ; Restore EAX
	RET

:DEFINE_str
"DEFINE"


;; match function
;; Receives CHAR* in EAX and CHAR* in EBX
;; Returns 0 (TRUE) or 1 (FALSE) in EAX
:match
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	PUSH_EDX                    ; Protect EDX
	COPY_EAX_to_ECX             ; S1 in place
	COPY_EBX_to_EDX             ; S2 in place
:match_Loop
	LOAD8_al_from_ECX           ; S1[0]
	MOVZX_al                    ; Make it useful
	LOAD8_bl_from_EDX           ; S2[0]
	MOVZX_bl                    ; Make it useful
	CMP_EAX_EBX                 ; See if they match
	JNE32 %match_False          ; If not

	ADDI8_ECX !1                ; S1 = S1 + 1
	ADDI8_EDX !1                ; S2 = S2 + 1
	CMPI8_EAX !0                ; If reached end of string
	JE32 %match_Done            ; Perfect match
	JMP32 %match_Loop           ; Otherwise keep looping

:match_False
	LOADI32_EAX %1              ; Return false
:match_Done
	POP_EDX                     ; Restore EDX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	RET


;; Line_Macro function
;; Receives List in EAX
;; Updates the list in place; does not modify registers
;; Uses EAX for I, EBX for I->TEXT, ECX for I->EXPRESSION
:Line_Macro
	PUSH_EAX                    ; Protect EAX
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	PUSH_EDX                    ; Protect EDX
:Line_Macro_Loop
	LOAD32_EBX_from_EAX_Immediate8 !8 ; I->TYPE
	CMPI8_EBX !1                ; IF MACRO == I->TYPE
	JNE32 %Line_Macro_Next      ; Otherwise move on

	;; Is a macro apply
	LOAD32_EBX_from_EAX_Immediate8 !16 ; I->TEXT
	LOAD32_ECX_from_EAX_Immediate8 !24 ; I->EXPRESSION
	LOAD32_EAX_from_EAX         ; I->NEXT
	CALL32 %Set_Expression      ; Apply it
	JMP32 %Line_Macro_Loop      ; Move on to next

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

	POP_EDX                     ; Restore EDX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	POP_EAX                     ; Restore EAX
	RET


;; Set_Expression function
;; Receives List in EAX, CHAR* in EBX and CHAR* in ECX
;; Updates the list in place; does not modify registers
;; Uses EBX for C, ECX for EXP and EDX for I
:Set_Expression
	PUSH_EAX                    ; Protect EAX
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	PUSH_EDX                    ; Protect EDX
	COPY_EAX_to_EDX             ; Set I
:Set_Expression_Loop
	LOAD32_EAX_from_EDX_Immediate8 !8 ; I->TYPE
	CMPI8_EAX !1                ; IF MACRO == I->TYPE
	JE32 %Set_Expression_Next   ; Ignore and move on

	LOAD32_EAX_from_EDX_Immediate8 !16 ; I->TEXT
	CALL32 %match               ; Check for match
	CMPI8_EAX !0                ; If match
	JNE32 %Set_Expression_Next  ; Otherwise next

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

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

	POP_EDX                     ; Restore EDX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	POP_EAX                     ; Restore EAX
	RET


;; Process_String function
;; Receives List in EAX
;; Update the list in place; does not modify registers
;; Uses EBX for I->TEXT, ECX for I and EDX for S
:Process_String
	PUSH_EAX                    ; Protect EAX
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	PUSH_EDX                    ; Protect EDX
	COPY_EAX_to_ECX             ; I = HEAD
:Process_String_loop
	LOAD32_EAX_from_ECX_Immediate8 !8 ; I->TYPE
	CMPI8_EAX !2                ; IF STRING == I->TYPE
	JNE32 %Process_String_Next  ; Skip to next

	LOAD32_EBX_from_ECX_Immediate8 !16 ; I->TEXT
	LOAD8_al_from_EBX           ; I->TEXT[0]
	MOVZX_al                    ; make it useful
	CMPI8_EAX !39               ; IF '\'' == I->TEXT[0]
	JNE32 %Process_String_Raw   ; Deal with '\"'

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

:Process_String_Raw
	COPY_EBX_to_EAX             ; Get length of I->TEXT
	CALL32 %string_length       ; Do it
	SHRI8_EAX !2                ; LENGTH = LENGTH >> 2
	ADDI8_EAX !1                ; LENGTH = LENGTH + 1
	SHLI8_EAX !3                ; LENGTH = LENGTH << 3
	CALL32 %malloc              ; Get string
	COPY_EBX_to_EDX             ; S = I->TEXT
	ADDI8_EDX !1                ; S = S + 1
	STORE32_EAX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = hexify
	COPY_EAX_to_EBX             ; Put hexify buffer in ebx

:Process_String_Raw_Loop
	LOAD8_al_from_EDX           ; Read 1 chars
	MOVZX_al                    ; Make it useful
	ADDI8_EDX !1                ; S = S + 1
	CMPI8_AL !0                 ; Check for NULL
	PUSH_FLAGS                  ; Protect condition
	CALL32 %hex8                ; write them all
	POP_FLAGS                   ; restore condition
	JNE32 %Process_String_Raw_Loop ; Keep looping

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

	POP_EDX                     ; Restore EDX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	POP_EAX                     ; Restore EAX
	RET


;; string_length function
;; Receives CHAR* in EAX
;; Returns INT in EAX
;; Uses EAX for CH, EBX for S and ECX for INDEX
:string_length
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	COPY_EAX_to_EBX             ; Set S
	LOADI32_ECX %0              ; INDEX = 0
:string_length_loop
	LOAD8_al_from_EBX_indexed_ECX ; S[0]
	MOVZX_al                    ; make it useful
	CMPI8_EAX !0                ; IF NULL == S[0]
	JE32 %string_length_done    ; Stop

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

:string_length_done
	COPY_ECX_to_EAX             ; RETURN INDEX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	RET


;; Eval_Immediates function
;; Receives List in EAX
;; Updates the list in place; does not modify registers
;; Uses EBX for I->TEXT[0], ECX for I->TEXT[1] and EDX for I
:Eval_Immediates
	PUSH_EAX                    ; Protect EAX
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	PUSH_EDX                    ; Protect EDX
	COPY_EAX_to_EDX             ; I = HEAD
:Eval_Immediates_Loop
	;; Check for MACRO
	LOAD32_EAX_from_EDX_Immediate8 !8 ; I->TYPE
	CMPI8_EAX !1                ; IF MACRO == I-TYPE
	JE32 %Eval_Immediates_Next  ; Skip to next

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

	;; Check if number
	LOAD32_EAX_from_EDX_Immediate8 !16 ; I->TEXT
	LOAD8_bl_from_EAX           ; I->TEXT[0]
	MOVZX_bl                    ; Extend to use
	ADDI8_EAX !1                ; I->TEXT + 1
	LOAD8_cl_from_EAX           ; I->TEXT[1]
	MOVZX_cl                    ; Extend to use
	CALL32 %numerate_string     ; Convert string to INT
	CMPI8_EAX !0                ; IF 0 == numerate_number(I->TEXT + 1)
	JNE32 %Eval_Immediates_value ; Has a value

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

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

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

	POP_EDX                     ; Restore EDX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	POP_EAX                     ; Restore EAX
	RET


;; numerate_string function
;; Receives CHAR* in EAX
;; Returns value of CHAR* in EAX
;; Uses EAX for VALUE, EBX for S, ECX for CH and EDI for NEGATIVE?
:numerate_string
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	PUSH_EDX                    ; Protect EDX
	PUSH_EDI                    ; Protect EDI
	COPY_EAX_to_EBX             ; put S in correct place
	LOADI32_EAX %0              ; Initialize to Zero
:numerate_string_loop
	LOAD8_cl_from_EBX_Immediate8 !1 ; S[1]
	MOVZX_cl                    ; make it useful
	CMPI8_ECX !120              ; IF 'x' == S[1]
	JE32 %numerate_hex          ; Deal with hex input

	;; Assume decimal input
	LOADI32_ECX %0              ; Assume no negation
	LOAD8_cl_from_EBX           ; S[0]
	MOVZX_cl                    ; make it useful
	CMPI8_ECX !45               ; IF '-' == S[0]
	JNE32 %numerate_decimal     ; Skip negation

	LOADI32_EDI %1              ; Set FLAG
	ADDI8_EBX !1                ; S = S + 1

:numerate_decimal
	LOAD8_cl_from_EBX           ; S[0]
	MOVZX_cl                    ; make it useful
	CMPI8_ECX !0                ; IF NULL == S[0]
	JE32 %numerate_decimal_done ; We are done

	IMULI8_EAX !10              ; VALUE = VALUE * 10
	SUBI8_ECX !48               ; CH = CH - '0'
	CMPI8_ECX !9                ; Check for illegal
	JG32 %numerate_string_fail  ; If CH > '9'
	CMPI8_ECX !0                ; Check for illegal
	JL32 %numerate_string_fail  ; IF CH < 0
	ADD_ECX_to_EAX              ; VALUE = VALUE + CH
	ADDI8_EBX !1                ; S = S + 1
	JMP32 %numerate_decimal     ; Keep looping

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

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

:numerate_hex
	ADDI8_EBX !2                ; S = S + 2
:numerate_hex_loop
	LOAD8_cl_from_EBX           ; S[0]
	MOVZX_cl                    ; make it useful
	CMPI8_ECX !0                ; IF NULL == S[0]
	JE32 %numerate_string_done  ; We are done

	SHLI8_EAX !4                ; VALUE = VALUE << 4
	SUBI8_ECX !48               ; CH = CH - '0'
	CMPI8_ECX !10               ; IF 10 >= CH
	JL32 %numerate_hex_digit    ; NO
	SUBI8_ECX !7                ; Push A-F into range
:numerate_hex_digit
	CMPI8_ECX !15               ; Check for illegal
	JG32 %numerate_string_fail  ; If CH > 'F'
	CMPI8_ECX !0                ; Check for illegal
	JL32 %numerate_string_fail  ; IF CH < 0
	ADD_ECX_to_EAX              ; VALUE = VALUE + CH
	ADDI8_EBX !1                ; S = S + 1
	JMP32 %numerate_hex_loop    ; Keep looping

:numerate_string_fail
	LOADI32_EAX %0              ; return ZERO

:numerate_string_done
	POP_EDI                     ; Restore EDI
	POP_EDX                     ; Restore EDX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	RET


;; express_number function
;; Receives INT in EAX and CHAR in EBX
;; Allocates a string and expresses the value in hex
;; Returns string in EAX
;; Uses EAX for VALUE, EBX for S and ECX for CH
:express_number
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	PUSH_EDX                    ; Protect EDX
	COPY_EBX_to_ECX             ; Put CH in right place
	COPY_EAX_to_EBX             ; Protect VALUE
	CMPI8_ECX !37               ; IF '%' == CH
	JNE32 %express_number2      ; Otherwise try @

	LOADI32_EAX %9              ; We need 3bytes
	CALL32 %malloc              ; Get S pointer
	XCHG_EAX_EBX                ; Put S and VALUE in place
	PUSH_EBX                    ; Protect S
	CALL32 %hex32l              ; Store 32bits
	JMP32 %express_number_done  ; done

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

	LOADI32_EAX %5              ; We need 3bytes
	CALL32 %malloc              ; Get S pointer
	XCHG_EAX_EBX                ; Put S and VALUE in place
	PUSH_EBX                    ; Protect S
	CALL32 %hex16l              ; Store 16bits
	JMP32 %express_number_done  ; done

:express_number1
	LOADI32_EAX %3              ; We need 3bytes
	CALL32 %malloc              ; Get S pointer
	XCHG_EAX_EBX                ; Put S and VALUE in place
	PUSH_EBX                    ; Protect S
	CALL32 %hex8                ; Store 8bit

:express_number_done
	POP_EAX                     ; Restore S
	POP_EDX                     ; Restore EDX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	RET


;; HEX to ascii routine
;; Receives INT in EAX and CHAR* in EBX
;; Stores ascii of INT in CHAR*
;; Returns only modifying EAX
:hex64l
	PUSH_EAX                    ; Protect top 32
	CALL32 %hex32l              ; Store it
	POP_EAX                     ; do top 32
	SHRI8_EAX !32               ; do bottom 32 first
:hex32l
	PUSH_EAX                    ; Protect top 16
	CALL32 %hex16l              ; Store it
	POP_EAX                     ; do top 16
	SHRI8_EAX !16               ; do bottom 16 first
:hex16l
	PUSH_EAX                    ; Protect top byte
	CALL32 %hex8                ; Store it
	POP_EAX                     ; do high byte
	SHRI8_EAX !8                ; do bottom byte first
:hex8
	PUSH_EAX                    ; Protect bottom nibble
	SHRI8_EAX !4                ; do high nibble first
	CALL32 %hex4                ; Store it
	POP_EAX                     ; do low nibble
:hex4
	ANDI8_EAX !0xF              ; isolate nibble
	ADDI8_AL !48                ; convert to ascii
	CMPI8_AL !57                ; valid digit?
	JBE32 %hex1                 ; yes
	ADDI8_AL !7                 ; use alpha range
:hex1
	STORE8_al_into_Address_EBX  ; store result
	ADDI8_EBX !1                ; next position
	RET


;; Preserve_Other function
;; Receives List in EAX
;; Updates the list in place; does not modify registers
;; Uses EAX for I, EBX for I->TEXT
:Preserve_Other
	PUSH_EAX                    ; Protect EAX
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	PUSH_EDX                    ; Protect EDX
:Preserve_Other_Loop
	LOAD32_EBX_from_EAX_Immediate8 !24 ; I->EXPRESSION
	CMPI8_EBX !0                ; IF NULL == I->EXPRESSION
	JNE32 %Preserve_Other_Next  ; Otherwise next

	;; Needs preserving
	LOAD32_EBX_from_EAX_Immediate8 !16 ; I->TEXT
	STORE32_EBX_into_Address_EAX_Immediate8 !24 ; I->EXPRESSION = I->TEXT

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

	POP_EDX                     ; Restore EDX
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	POP_EAX                     ; Restore EAX
	RET


;; Print_Hex function
;; Receives list in EAX
;; walks the list and prints the I->EXPRESSION for all nodes followed by newline
;; Uses EBX for I
:Print_Hex
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	COPY_EBP_to_EBX             ; I = Head
:Print_Hex_Loop
	LOAD32_EAX_from_EBX_Immediate8 !8 ; I->TYPE
	CMPI8_EAX !1                ; IF MACRO == I->TYPE
	JE32 %Print_Hex_Next        ; Skip

	LOAD32_EAX_from_EBX_Immediate8 !24 ; Using EXPRESSION
	CALL32 %File_Print          ; Print it
	LOADI32_EAX %10             ; NEWLINE
	CALL32 %fputc               ; Append it

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

	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	RET


;; File_Print function
;; Receives CHAR* in EAX
;; calls fputc for every non-null char
:File_Print
	PUSH_EBX                    ; Protect EBX
	PUSH_ECX                    ; Protect ECX
	COPY_EAX_to_EBX             ; Protect S
	CMPI8_EAX !0                ; Protect against nulls
	JE32 %File_Print_Done       ; Simply don't try to print them
:File_Print_Loop
	LOAD8_al_from_EBX           ; Read byte
	MOVZX_al                    ; zero extend
	CMPI8_EAX !0                ; Check for NULL
	JE32 %File_Print_Done       ; Stop at NULL

	CALL32 %fputc               ; write it
	ADDI8_EBX !1                ; S = S + 1
	JMP32 %File_Print_Loop      ; Keep going

:File_Print_Done
	POP_ECX                     ; Restore ECX
	POP_EBX                     ; Restore EBX
	RET


;; fputc function
;; receives CHAR in EAX and FILE* in [Output]
;; writes char and returns
:fputc
	PUSH_EDX                    ; Protect EDX
	PUSH_ECX                    ; protect ECX
	PUSH_EBX                    ; protect EBX
	PUSH_EAX                    ; We are writing eax
	LEA32_ECX_from_esp          ; Get stack address
	LOAD32_Absolute32_ebx &Output ; Write to target file
	LOADI32_EAX %4              ; the syscall number for write
	LOADI32_EDX %1              ; set the size of chars we want
	INT_80                      ; call the Kernel
	POP_EAX                     ; Restore stack
	POP_EBX                     ; Restore EBX
	POP_ECX                     ; Restore ECX
	POP_EDX                     ; Restore EDX
	RET

:Output
	NULL
:Input
	NULL
:ELF_end
