;; 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 . ;; 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 :_start 58 ; POP_EAX ;·Get·the·number·of·arguments 5B ; POP_EBX ;·Get·the·program·name 5B ; POP_EBX ;·Get·the·actual·input name B9 00000000 ; LOADI32_ECX %0 ;·prepare·read_only B8 05000000 ; LOADI32_EAX %5 ;·the·syscall·number·for·open() CD80 ; INT_80 ; Now open that damn file A3 &Input ; STORE32_Absolute32_eax &Input ; Preserve the file pointer we were given 5B ; POP_EBX ;·Get·the·actual·output name B9 41020000 ; LOADI32_ECX %577 ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC BA 80010000 ; LOADI32_EDX %384 ; Prepare file as RW for owner only (600 in octal) B8 05000000 ; LOADI32_EAX %5 ; the·syscall·number·for·open() CD80 ; INT_80 ; Now open that damn file 83F8 00 ; CMPI8_EAX !0 ; Check for missing output 0F8F %_start_out ; JG32 %_start_out ; Have real input B8 01000000 ; LOADI32_EAX %1 ; Use stdout :_start_out A3 &Output ; STORE32_Absolute32_eax &Output ; Preserve the file pointer we were given B8 2D000000 ; LOADI32_EAX %45 ; the Syscall # for SYS_BRK BB 00000000 ; LOADI32_EBX %0 ; Get current brk CD80 ; INT_80 ; Let the kernel do the work 89C7 ; COPY_EAX_to_EDI ; Set our malloc pointer E8 %Tokenize_Line ; CALL32 %Tokenize_Line ; Get all lines 89E8 ; COPY_EBP_to_EAX ; prepare for Reverse_List E8 %Reverse_List ; CALL32 %Reverse_List ; Correct order 89C5 ; COPY_EAX_to_EBP ; Update HEAD E8 %Identify_Macros ; CALL32 %Identify_Macros ; Find the DEFINEs E8 %Line_Macro ; CALL32 %Line_Macro ; Apply the DEFINEs E8 %Process_String ; CALL32 %Process_String ; Handle strings E8 %Eval_Immediates ; CALL32 %Eval_Immediates ; Handle Numbers E8 %Preserve_Other ; CALL32 %Preserve_Other ; Collect the remaining E8 %Print_Hex ; CALL32 %Print_Hex ; Output our results :Done ; program completed Successfully BB 00000000 ; LOADI32_EBX %0 ; All is well B8 01000000 ; LOADI32_EAX %1 ; put the exit syscall number in eax CD80 ; 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 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 52 ; PUSH_EDX ; Protect EDX :restart E8 %fgetc ; CALL32 %fgetc ; Read a char 83F8 FC ; CMPI8_EAX !-4 ; Check for EOF 0F84 %done ; JE32 %done ; File is collected 0FB6C0 ; MOVZX_al ; We have to zero extend it to use it 89C1 ; COPY_EAX_to_ECX ; Protect C BB &comments ; LOADI32_EBX &comments ; Get pointer to "#;" E8 %In_Set ; CALL32 %In_Set ; Check for comments 83F8 01 ; CMPI8_EAX !1 ; If comments 0F84 %Purge_LineComment ; JE32 %Purge_LineComment ; try again 89C8 ; COPY_ECX_to_EAX ; put C in place for check BB &terminators ; LOADI32_EBX &terminators ; Get pointer to "\n\t " E8 %In_Set ; CALL32 %In_Set ; Check for terminators 83F8 01 ; CMPI8_EAX !1 ; If terminator 0F84 %restart ; JE32 %restart ; try again B8 20000000 ; LOADI32_EAX %32 ; Malloc the struct P E8 %malloc ; CALL32 %malloc ; Get pointer to P 89C2 ; COPY_EAX_to_EDX ; Protect P 892A ; STORE32_EBP_into_Address_EDX ; P->NEXT = HEAD 89D5 ; COPY_EDX_to_EBP ; HEAD = P 89C8 ; COPY_ECX_to_EAX ; put C in place for check BB &string_char ; LOADI32_EBX &string_char ; Get pointer to "\"'" E8 %In_Set ; CALL32 %In_Set ; Check for string chars 83F8 01 ; CMPI8_EAX !1 ; If string char 0F84 %Store_String ; JE32 %Store_String ; Get string E8 %Store_Atom ; CALL32 %Store_Atom ; Get whole token E9 %restart ; JMP32 %restart :done 5A ; POP_EDX ; Restore EDX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX C3 ; RET ;; fgetc function ;; Receives FILE* in [Input] ;; Returns -4 (EOF) or char in EAX :fgetc 52 ; PUSH_EDX ; Protect EDX 51 ; PUSH_ECX ; Protect ECX 53 ; PUSH_EBX ; Protect EBX B8 FCFFFFFF ; LOADI32_EAX %-4 ; Put EOF in eax 50 ; PUSH_EAX ; Assume bad (If nothing read, value will remain EOF) 8D0C24 ; LEA32_ECX_from_esp ; Get stack address 8B1D &Input ; LOAD32_Absolute32_ebx &Input ; Where are we reading from B8 03000000 ; LOADI32_EAX %3 ; the syscall number for read BA 01000000 ; LOADI32_EDX %1 ; set the size of chars we want CD80 ; INT_80 ; call the Kernel 58 ; POP_EAX ; Get either char or EOF 5B ; POP_EBX ; Restore EBX 59 ; POP_ECX ; Restore ECX 5A ; POP_EDX ; Restore EDX 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 EDI to be initialized and EAX to have the number of desired bytes :malloc 52 ; PUSH_EDX ; Protect EDX 51 ; PUSH_ECX ; Protect ECX 53 ; PUSH_EBX ; Protect EBX 89FB ; COPY_EDI_to_EBX ; Using the current pointer 01C3 ; ADD_EAX_to_EBX ; Request the number of desired bytes B8 2D000000 ; LOADI32_EAX %45 ; the Syscall # for SYS_BRK CD80 ; INT_80 ; call the Kernel 89F8 ; COPY_EDI_to_EAX ; Return pointer 89DF ; COPY_EBX_to_EDI ; Update pointer 5B ; POP_EBX ; Restore EBX 59 ; POP_ECX ; Restore ECX 5A ; POP_EDX ; Restore EDX C3 ; RET ;; Purge_LineComment function ;; Reads chars until LF and jumps to restart :Purge_LineComment E8 %fgetc ; CALL32 %fgetc ; Get a char 0FB6C0 ; MOVZX_al ; Zero extend 83F8 0A ; CMPI8_EAX !10 ; While not LF 0F85 %Purge_LineComment ; JNE32 %Purge_LineComment ; Keep reading E9 %restart ; 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 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 52 ; PUSH_EDX ; Protect EDX B8 02000000 ; LOADI32_EAX %2 ; Using TYPE STRING 8942 08 ; STORE32_EAX_into_Address_EDX_Immediate8 !8 ; HEAD->TYPE = STRING B8 00010000 ; LOADI32_EAX %256 ; Malloc the string E8 %malloc ; CALL32 %malloc ; Get pointer to P 8942 10 ; STORE32_EAX_into_Address_EDX_Immediate8 !16 ; HEAD->TEXT = STRING 89CB ; COPY_ECX_to_EBX ; Protect terminator 89C2 ; COPY_EAX_to_EDX ; Protect string pointer :Store_String_Loop 880A ; STORE8_cl_into_Address_EDX ; write byte E8 %fgetc ; CALL32 %fgetc ; read next char 0FB6C0 ; MOVZX_al ; Zero extend it 89C1 ; COPY_EAX_to_ECX ; Update C 83C2 01 ; ADDI8_EDX !1 ; STRING = STRING + 1 39D9 ; CMP_EBX_ECX ; See if we hit terminator 0F85 %Store_String_Loop ; JNE32 %Store_String_Loop ; Otherwise keep looping 5A ; POP_EDX ; Restore EDX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX 89D0 ; COPY_EDX_to_EAX ; return HEAD E9 %restart ; 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 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 52 ; PUSH_EDX ; Protect EDX B8 00010000 ; LOADI32_EAX %256 ; Malloc the string E8 %malloc ; CALL32 %malloc ; Get pointer to P 8942 10 ; STORE32_EAX_into_Address_EDX_Immediate8 !16 ; HEAD->TEXT = STRING BB &terminators ; LOADI32_EBX &terminators ; Get pointer to "\n\t " 89C2 ; COPY_EAX_to_EDX ; Protect string pointer :Store_Atom_loop 880A ; STORE8_cl_into_Address_EDX ; write byte E8 %fgetc ; CALL32 %fgetc ; read next char 0FB6C0 ; MOVZX_al ; Zero extend it 89C1 ; COPY_EAX_to_ECX ; Update C 83C2 01 ; ADDI8_EDX !1 ; STRING = STRING + 1 E8 %In_Set ; CALL32 %In_Set ; Check for terminators 83F8 00 ; CMPI8_EAX !0 ; Check for "\n\t " 0F84 %Store_Atom_loop ; JE32 %Store_Atom_loop ; Loop otherwise 5A ; POP_EDX ; Restore EDX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX 89D0 ; COPY_EDX_to_EAX ; return HEAD C3 ; RET ;; In_Set function ;; Receives Char C in EAX and CHAR* in EBX ;; Returns 1 if true, zero if false in EAX :In_Set 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX :In_Set_loop 8A0B ; LOAD8_cl_from_EBX ; Read char 0FB6C9 ; MOVZX_cl ; Zero extend it 39C8 ; CMP_EAX_ECX ; See if they match 0F84 %In_Set_True ; JE32 %In_Set_True ; return true 83F9 00 ; CMPI8_ECX !0 ; Check for NULL 0F84 %In_Set_False ; JE32 %In_Set_False ; return false 83C3 01 ; ADDI8_EBX !1 ; s = s + 1 E9 %In_Set_loop ; JMP32 %In_Set_loop ; Keep looping :In_Set_True B8 01000000 ; LOADI32_EAX %1 ; Set True 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX C3 ; RET :In_Set_False B8 00000000 ; LOADI32_EAX %0 ; Set FALSE 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX C3 ; RET ;; Char sets :terminators 0A092000 ; "\n\t " :comments 3B2300 ; ";#" :string_char 22 27 00 ; "\"\'" ;; Reverse_List function ;; Receives List in EAX ;; Returns the list reversed in EAX :Reverse_List 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 89C3 ; COPY_EAX_to_EBX ; Set HEAD B8 00000000 ; LOADI32_EAX %0 ; ROOT = NULL :Reverse_List_Loop 83FB 00 ; CMPI8_EBX !0 ; WHILE HEAD != NULL 0F84 %Reverse_List_Done ; JE32 %Reverse_List_Done ; Stop otherwise 8B0B ; LOAD32_ECX_from_EBX ; NEXT = HEAD->NEXT 8903 ; STORE32_EAX_into_Address_EBX ; HEAD->NEXT = ROOT 89D8 ; COPY_EBX_to_EAX ; ROOT = HEAD 89CB ; COPY_ECX_to_EBX ; HEAD = NEXT E9 %Reverse_List_Loop ; JMP32 %Reverse_List_Loop ; Keep Going :Reverse_List_Done 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX C3 ; 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 50 ; PUSH_EAX ; Protect EAX 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 52 ; PUSH_EDX ; Protect EDX BB &DEFINE_str ; LOADI32_EBX &DEFINE_str ; Setup define string 89C1 ; COPY_EAX_to_ECX ; I = HEAD :Identify_Macros_Loop 8B41 10 ; LOAD32_EAX_from_ECX_Immediate8 !16 ; I->TEXT E8 %match ; CALL32 %match ; IF "DEFINE" == I->TEXT 83F8 00 ; CMPI8_EAX !0 ; Check if match 0F85 %Identify_Macros_Next ; JNE32 %Identify_Macros_Next ; Skip the work ;; Deal with MACRO B8 01000000 ; LOADI32_EAX %1 ; Using MACRO 8941 08 ; STORE32_EAX_into_Address_ECX_Immediate8 !8 ; I->TYPE = MACRO 8B01 ; LOAD32_EAX_from_ECX ; I->NEXT 8B40 10 ; LOAD32_EAX_from_EAX_Immediate8 !16 ; I->NEXT->TEXT 8941 10 ; STORE32_EAX_into_Address_ECX_Immediate8 !16 ; I->TEXT = I->NEXT->TEXT 8B01 ; LOAD32_EAX_from_ECX ; I->NEXT 8B00 ; LOAD32_EAX_from_EAX ; I->NEXT->NEXT 8B40 10 ; LOAD32_EAX_from_EAX_Immediate8 !16 ; I->NEXT->NEXT->TEXT 8941 18 ; STORE32_EAX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = I->NEXT->NEXT->TEXT 8B01 ; LOAD32_EAX_from_ECX ; I->NEXT 8B00 ; LOAD32_EAX_from_EAX ; I->NEXT->NEXT 8B00 ; LOAD32_EAX_from_EAX ; I->NEXT->NEXT->NEXT 8901 ; STORE32_EAX_into_Address_ECX ; I->NEXT = I->NEXT->NEXT->NEXT :Identify_Macros_Next 8B09 ; LOAD32_ECX_from_ECX ; I = I->NEXT 83F9 00 ; CMPI8_ECX !0 ; Check for NULL 0F85 %Identify_Macros_Loop ; JNE32 %Identify_Macros_Loop ; Keep looping otherwise 5A ; POP_EDX ; Restore EDX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX 58 ; POP_EAX ; Restore EAX C3 ; RET :DEFINE_str 444546494E4500 ; "DEFINE" ;; match function ;; Receives CHAR* in EAX and CHAR* in EBX ;; Returns 0 (TRUE) or 1 (FALSE) in EAX :match 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 52 ; PUSH_EDX ; Protect EDX 89C1 ; COPY_EAX_to_ECX ; S1 in place 89DA ; COPY_EBX_to_EDX ; S2 in place :match_Loop 8A01 ; LOAD8_al_from_ECX ; S1[0] 0FB6C0 ; MOVZX_al ; Make it useful 8A1A ; LOAD8_bl_from_EDX ; S2[0] 0FB6DB ; MOVZX_bl ; Make it useful 39D8 ; CMP_EAX_EBX ; See if they match 0F85 %match_False ; JNE32 %match_False ; If not 83C1 01 ; ADDI8_ECX !1 ; S1 = S1 + 1 83C2 01 ; ADDI8_EDX !1 ; S2 = S2 + 1 83F8 00 ; CMPI8_EAX !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 B8 01000000 ; LOADI32_EAX %1 ; Return false :match_Done 5A ; POP_EDX ; Restore EDX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX C3 ; 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 50 ; PUSH_EAX ; Protect EAX 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 52 ; PUSH_EDX ; Protect EDX :Line_Macro_Loop 8B58 08 ; LOAD32_EBX_from_EAX_Immediate8 !8 ; I->TYPE 83FB 01 ; CMPI8_EBX !1 ; IF MACRO == I->TYPE 0F85 %Line_Macro_Next ; JNE32 %Line_Macro_Next ; Otherwise move on ;; Is a macro apply 8B58 10 ; LOAD32_EBX_from_EAX_Immediate8 !16 ; I->TEXT 8B48 18 ; LOAD32_ECX_from_EAX_Immediate8 !24 ; I->EXPRESSION 8B00 ; LOAD32_EAX_from_EAX ; I->NEXT E8 %Set_Expression ; CALL32 %Set_Expression ; Apply it E9 %Line_Macro_Loop ; JMP32 %Line_Macro_Loop ; Move on to next :Line_Macro_Next 8B00 ; LOAD32_EAX_from_EAX ; I->NEXT 83F8 00 ; CMPI8_EAX !0 ; Check for NULL 0F85 %Line_Macro_Loop ; JNE32 %Line_Macro_Loop ; Keep going 5A ; POP_EDX ; Restore EDX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX 58 ; POP_EAX ; Restore EAX C3 ; 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 50 ; PUSH_EAX ; Protect EAX 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 52 ; PUSH_EDX ; Protect EDX 89C2 ; COPY_EAX_to_EDX ; Set I :Set_Expression_Loop 8B42 08 ; LOAD32_EAX_from_EDX_Immediate8 !8 ; I->TYPE 83F8 01 ; CMPI8_EAX !1 ; IF MACRO == I->TYPE 0F84 %Set_Expression_Next ; JE32 %Set_Expression_Next ; Ignore and move on 8B42 10 ; LOAD32_EAX_from_EDX_Immediate8 !16 ; I->TEXT E8 %match ; CALL32 %match ; Check for match 83F8 00 ; CMPI8_EAX !0 ; If match 0F85 %Set_Expression_Next ; JNE32 %Set_Expression_Next ; Otherwise next ;; We have a non-macro match 894A 18 ; STORE32_ECX_into_Address_EDX_Immediate8 !24 ; I->EXPRESSION = EXP :Set_Expression_Next 8B12 ; LOAD32_EDX_from_EDX ; I = I->NEXT 83FA 00 ; CMPI8_EDX !0 ; IF NULL == I 0F85 %Set_Expression_Loop ; JNE32 %Set_Expression_Loop ; Otherwise keep looping 5A ; POP_EDX ; Restore EDX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX 58 ; POP_EAX ; Restore EAX C3 ; 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 50 ; PUSH_EAX ; Protect EAX 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 52 ; PUSH_EDX ; Protect EDX 89C1 ; COPY_EAX_to_ECX ; I = HEAD :Process_String_loop 8B41 08 ; LOAD32_EAX_from_ECX_Immediate8 !8 ; I->TYPE 83F8 02 ; CMPI8_EAX !2 ; IF STRING == I->TYPE 0F85 %Process_String_Next ; JNE32 %Process_String_Next ; Skip to next 8B59 10 ; LOAD32_EBX_from_ECX_Immediate8 !16 ; I->TEXT 8A03 ; LOAD8_al_from_EBX ; I->TEXT[0] 0FB6C0 ; MOVZX_al ; make it useful 83F8 27 ; CMPI8_EAX !39 ; IF '\'' == I->TEXT[0] 0F85 %Process_String_Raw ; JNE32 %Process_String_Raw ; Deal with '\"' ;; Deal with '\'' 83C3 01 ; ADDI8_EBX !1 ; I->TEXT + 1 8959 18 ; STORE32_EBX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = I->TEXT + 1 E9 %Process_String_Next ; JMP32 %Process_String_Next ; Move on to next :Process_String_Raw 89D8 ; COPY_EBX_to_EAX ; Get length of I->TEXT E8 %string_length ; CALL32 %string_length ; Do it C1E8 02 ; SHRI8_EAX !2 ; LENGTH = LENGTH >> 2 83C0 01 ; ADDI8_EAX !1 ; LENGTH = LENGTH + 1 C1E0 03 ; SHLI8_EAX !3 ; LENGTH = LENGTH << 3 E8 %malloc ; CALL32 %malloc ; Get string 89DA ; COPY_EBX_to_EDX ; S = I->TEXT 83C2 01 ; ADDI8_EDX !1 ; S = S + 1 8941 18 ; STORE32_EAX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = hexify 89C3 ; COPY_EAX_to_EBX ; Put hexify buffer in ebx :Process_String_Raw_Loop 8A02 ; LOAD8_al_from_EDX ; Read 1 chars 0FB6C0 ; MOVZX_al ; Make it useful 83C2 01 ; ADDI8_EDX !1 ; S = S + 1 3C 00 ; CMPI8_AL !0 ; Check for NULL 9C ; PUSH_FLAGS ; Protect condition E8 %hex8 ; CALL32 %hex8 ; write them all 9D ; POP_FLAGS ; restore condition 0F85 %Process_String_Raw_Loop ; JNE32 %Process_String_Raw_Loop ; Keep looping :Process_String_Next 8B09 ; LOAD32_ECX_from_ECX ; I = I->NEXT 83F9 00 ; CMPI8_ECX !0 ; IF NULL == I 0F85 %Process_String_loop ; JNE32 %Process_String_loop ; Otherwise keep looping 5A ; POP_EDX ; Restore EDX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX 58 ; POP_EAX ; Restore EAX C3 ; 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 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 89C3 ; COPY_EAX_to_EBX ; Set S B9 00000000 ; LOADI32_ECX %0 ; INDEX = 0 :string_length_loop 8A040B ; LOAD8_al_from_EBX_indexed_ECX ; S[0] 0FB6C0 ; MOVZX_al ; make it useful 83F8 00 ; CMPI8_EAX !0 ; IF NULL == S[0] 0F84 %string_length_done ; JE32 %string_length_done ; Stop 83C1 01 ; ADDI8_ECX !1 ; INDEX = INDEX + 1 E9 %string_length_loop ; JMP32 %string_length_loop ; Keep going :string_length_done 89C8 ; COPY_ECX_to_EAX ; RETURN INDEX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX C3 ; 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 50 ; PUSH_EAX ; Protect EAX 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 52 ; PUSH_EDX ; Protect EDX 89C2 ; COPY_EAX_to_EDX ; I = HEAD :Eval_Immediates_Loop ;; Check for MACRO 8B42 08 ; LOAD32_EAX_from_EDX_Immediate8 !8 ; I->TYPE 83F8 01 ; CMPI8_EAX !1 ; IF MACRO == I-TYPE 0F84 %Eval_Immediates_Next ; JE32 %Eval_Immediates_Next ; Skip to next ;; Check for NULL EXPRESSION 8B42 18 ; LOAD32_EAX_from_EDX_Immediate8 !24 ; I->EXPRESSION 83F8 00 ; CMPI8_EAX !0 ; IF NULL == I->EXPRESSION 0F85 %Eval_Immediates_Next ; JNE32 %Eval_Immediates_Next ; Skip to next ;; Check if number 8B42 10 ; LOAD32_EAX_from_EDX_Immediate8 !16 ; I->TEXT 8A18 ; LOAD8_bl_from_EAX ; I->TEXT[0] 0FB6DB ; MOVZX_bl ; Extend to use 83C0 01 ; ADDI8_EAX !1 ; I->TEXT + 1 8A08 ; LOAD8_cl_from_EAX ; I->TEXT[1] 0FB6C9 ; MOVZX_cl ; Extend to use E8 %numerate_string ; CALL32 %numerate_string ; Convert string to INT 83F8 00 ; CMPI8_EAX !0 ; IF 0 == numerate_number(I->TEXT + 1) 0F85 %Eval_Immediates_value ; JNE32 %Eval_Immediates_value ; Has a value ;; Last chance for Immediate 83F9 30 ; CMPI8_ECX !48 ; If '0' == I->TEXT[1] 0F85 %Eval_Immediates_Next ; JNE32 %Eval_Immediates_Next ; Skip to next :Eval_Immediates_value E8 %express_number ; CALL32 %express_number ; Convert value to hex string 8942 18 ; STORE32_EAX_into_Address_EDX_Immediate8 !24 ; I->EXPRESSION = express_number(value, I-TEXT[0]) :Eval_Immediates_Next 8B12 ; LOAD32_EDX_from_EDX ; I = I->NEXT 83FA 00 ; CMPI8_EDX !0 ; IF NULL == I 0F85 %Eval_Immediates_Loop ; JNE32 %Eval_Immediates_Loop ; Otherwise keep looping 5A ; POP_EDX ; Restore EDX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX 58 ; POP_EAX ; Restore EAX C3 ; 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 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 52 ; PUSH_EDX ; Protect EDX 57 ; PUSH_EDI ; Protect EDI 89C3 ; COPY_EAX_to_EBX ; put S in correct place B8 00000000 ; LOADI32_EAX %0 ; Initialize to Zero :numerate_string_loop 8A4B 01 ; LOAD8_cl_from_EBX_Immediate8 !1 ; S[1] 0FB6C9 ; MOVZX_cl ; make it useful 83F9 78 ; CMPI8_ECX !120 ; IF 'x' == S[1] 0F84 %numerate_hex ; JE32 %numerate_hex ; Deal with hex input ;; Assume decimal input B9 00000000 ; LOADI32_ECX %0 ; Assume no negation 8A0B ; LOAD8_cl_from_EBX ; S[0] 0FB6C9 ; MOVZX_cl ; make it useful 83F9 2D ; CMPI8_ECX !45 ; IF '-' == S[0] 0F85 %numerate_decimal ; JNE32 %numerate_decimal ; Skip negation BF 01000000 ; LOADI32_EDI %1 ; Set FLAG 83C3 01 ; ADDI8_EBX !1 ; S = S + 1 :numerate_decimal 8A0B ; LOAD8_cl_from_EBX ; S[0] 0FB6C9 ; MOVZX_cl ; make it useful 83F9 00 ; CMPI8_ECX !0 ; IF NULL == S[0] 0F84 %numerate_decimal_done ; JE32 %numerate_decimal_done ; We are done 6BC0 0A ; IMULI8_EAX !10 ; VALUE = VALUE * 10 83E9 30 ; SUBI8_ECX !48 ; CH = CH - '0' 83F9 09 ; CMPI8_ECX !9 ; Check for illegal 0F8F %numerate_string_fail ; JG32 %numerate_string_fail ; If CH > '9' 83F9 00 ; CMPI8_ECX !0 ; Check for illegal 0F8C %numerate_string_fail ; JL32 %numerate_string_fail ; IF CH < 0 01C8 ; ADD_ECX_to_EAX ; VALUE = VALUE + CH 83C3 01 ; ADDI8_EBX !1 ; S = S + 1 E9 %numerate_decimal ; JMP32 %numerate_decimal ; Keep looping :numerate_decimal_done 83FF 01 ; CMPI8_EDI !1 ; Check if need to negate 0F85 %numerate_string_done ; JNE32 %numerate_string_done ; Nope 6BC0 FF ; IMULI8_EAX !-1 ; VALUE = VALUE * -1 E9 %numerate_string_done ; JMP32 %numerate_string_done ; Done :numerate_hex 83C3 02 ; ADDI8_EBX !2 ; S = S + 2 :numerate_hex_loop 8A0B ; LOAD8_cl_from_EBX ; S[0] 0FB6C9 ; MOVZX_cl ; make it useful 83F9 00 ; CMPI8_ECX !0 ; IF NULL == S[0] 0F84 %numerate_string_done ; JE32 %numerate_string_done ; We are done C1E0 04 ; SHLI8_EAX !4 ; VALUE = VALUE << 4 83E9 30 ; SUBI8_ECX !48 ; CH = CH - '0' 83F9 0A ; CMPI8_ECX !10 ; IF 10 >= CH 0F8C %numerate_hex_digit ; JL32 %numerate_hex_digit ; NO 83E9 07 ; SUBI8_ECX !7 ; Push A-F into range :numerate_hex_digit 83F9 0F ; CMPI8_ECX !15 ; Check for illegal 0F8F %numerate_string_fail ; JG32 %numerate_string_fail ; If CH > 'F' 83F9 00 ; CMPI8_ECX !0 ; Check for illegal 0F8C %numerate_string_fail ; JL32 %numerate_string_fail ; IF CH < 0 01C8 ; ADD_ECX_to_EAX ; VALUE = VALUE + CH 83C3 01 ; ADDI8_EBX !1 ; S = S + 1 E9 %numerate_hex_loop ; JMP32 %numerate_hex_loop ; Keep looping :numerate_string_fail B8 00000000 ; LOADI32_EAX %0 ; return ZERO :numerate_string_done 5F ; POP_EDI ; Restore EDI 5A ; POP_EDX ; Restore EDX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX C3 ; 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 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 52 ; PUSH_EDX ; Protect EDX 89D9 ; COPY_EBX_to_ECX ; Put CH in right place 89C3 ; COPY_EAX_to_EBX ; Protect VALUE 83F9 25 ; CMPI8_ECX !37 ; IF '%' == CH 0F85 %express_number2 ; JNE32 %express_number2 ; Otherwise try @ B8 09000000 ; LOADI32_EAX %9 ; We need 3bytes E8 %malloc ; CALL32 %malloc ; Get S pointer 93 ; XCHG_EAX_EBX ; Put S and VALUE in place 53 ; PUSH_EBX ; Protect S E8 %hex32l ; CALL32 %hex32l ; Store 32bits E9 %express_number_done ; JMP32 %express_number_done ; done :express_number2 83F9 40 ; CMPI8_ECX !64 ; IF '@' == CH 0F85 %express_number1 ; JNE32 %express_number1 ; Othrewise try ! B8 05000000 ; LOADI32_EAX %5 ; We need 3bytes E8 %malloc ; CALL32 %malloc ; Get S pointer 93 ; XCHG_EAX_EBX ; Put S and VALUE in place 53 ; PUSH_EBX ; Protect S E8 %hex16l ; CALL32 %hex16l ; Store 16bits E9 %express_number_done ; JMP32 %express_number_done ; done :express_number1 B8 03000000 ; LOADI32_EAX %3 ; We need 3bytes E8 %malloc ; CALL32 %malloc ; Get S pointer 93 ; XCHG_EAX_EBX ; Put S and VALUE in place 53 ; PUSH_EBX ; Protect S E8 %hex8 ; CALL32 %hex8 ; Store 8bit :express_number_done 58 ; POP_EAX ; Restore S 5A ; POP_EDX ; Restore EDX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX C3 ; RET ;; HEX to ascii routine ;; Receives INT in EAX and CHAR* in EBX ;; Stores ascii of INT in CHAR* ;; Returns only modifying EAX :hex64l 50 ; PUSH_EAX ; Protect top 32 E8 %hex32l ; CALL32 %hex32l ; Store it 58 ; POP_EAX ; do top 32 C1E8 20 ; SHRI8_EAX !32 ; do bottom 32 first :hex32l 50 ; PUSH_EAX ; Protect top 16 E8 %hex16l ; CALL32 %hex16l ; Store it 58 ; POP_EAX ; do top 16 C1E8 10 ; SHRI8_EAX !16 ; do bottom 16 first :hex16l 50 ; PUSH_EAX ; Protect top byte E8 %hex8 ; CALL32 %hex8 ; Store it 58 ; POP_EAX ; do high byte C1E8 08 ; SHRI8_EAX !8 ; do bottom byte first :hex8 50 ; PUSH_EAX ; Protect bottom nibble C1E8 04 ; SHRI8_EAX !4 ; do high nibble first E8 %hex4 ; CALL32 %hex4 ; Store it 58 ; POP_EAX ; do low nibble :hex4 83E0 0F ; ANDI8_EAX !0xF ; isolate nibble 04 30 ; ADDI8_AL !48 ; convert to ascii 3C 39 ; CMPI8_AL !57 ; valid digit? 0F86 %hex1 ; JBE32 %hex1 ; yes 04 07 ; ADDI8_AL !7 ; use alpha range :hex1 8803 ; STORE8_al_into_Address_EBX ; store result 83C3 01 ; ADDI8_EBX !1 ; next position C3 ; 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 50 ; PUSH_EAX ; Protect EAX 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 52 ; PUSH_EDX ; Protect EDX :Preserve_Other_Loop 8B58 18 ; LOAD32_EBX_from_EAX_Immediate8 !24 ; I->EXPRESSION 83FB 00 ; CMPI8_EBX !0 ; IF NULL == I->EXPRESSION 0F85 %Preserve_Other_Next ; JNE32 %Preserve_Other_Next ; Otherwise next ;; Needs preserving 8B58 10 ; LOAD32_EBX_from_EAX_Immediate8 !16 ; I->TEXT 8958 18 ; STORE32_EBX_into_Address_EAX_Immediate8 !24 ; I->EXPRESSION = I->TEXT :Preserve_Other_Next 8B00 ; LOAD32_EAX_from_EAX ; I = I->NEXT 83F8 00 ; CMPI8_EAX !0 ; IF NULL == I 0F85 %Preserve_Other_Loop ; JNE32 %Preserve_Other_Loop ; Otherwise keep looping 5A ; POP_EDX ; Restore EDX 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX 58 ; POP_EAX ; Restore EAX C3 ; 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 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 89EB ; COPY_EBP_to_EBX ; I = Head :Print_Hex_Loop 8B43 08 ; LOAD32_EAX_from_EBX_Immediate8 !8 ; I->TYPE 83F8 01 ; CMPI8_EAX !1 ; IF MACRO == I->TYPE 0F84 %Print_Hex_Next ; JE32 %Print_Hex_Next ; Skip 8B43 18 ; LOAD32_EAX_from_EBX_Immediate8 !24 ; Using EXPRESSION E8 %File_Print ; CALL32 %File_Print ; Print it B8 0A000000 ; LOADI32_EAX %10 ; NEWLINE E8 %fputc ; CALL32 %fputc ; Append it :Print_Hex_Next 8B1B ; LOAD32_EBX_from_EBX ; Iterate to next Token 83FB 00 ; CMPI8_EBX !0 ; Check for NULL 0F85 %Print_Hex_Loop ; JNE32 %Print_Hex_Loop ; Otherwise keep looping 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX C3 ; RET ;; File_Print function ;; Receives CHAR* in EAX ;; calls fputc for every non-null char :File_Print 53 ; PUSH_EBX ; Protect EBX 51 ; PUSH_ECX ; Protect ECX 89C3 ; COPY_EAX_to_EBX ; Protect S 83F8 00 ; CMPI8_EAX !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_EBX ; Read byte 0FB6C0 ; MOVZX_al ; zero extend 83F8 00 ; CMPI8_EAX !0 ; Check for NULL 0F84 %File_Print_Done ; JE32 %File_Print_Done ; Stop at NULL E8 %fputc ; CALL32 %fputc ; write it 83C3 01 ; ADDI8_EBX !1 ; S = S + 1 E9 %File_Print_Loop ; JMP32 %File_Print_Loop ; Keep going :File_Print_Done 59 ; POP_ECX ; Restore ECX 5B ; POP_EBX ; Restore EBX C3 ; RET ;; fputc function ;; receives CHAR in EAX and FILE* in [Output] ;; writes char and returns :fputc 52 ; PUSH_EDX ; Protect EDX 51 ; PUSH_ECX ; protect ECX 53 ; PUSH_EBX ; protect EBX 50 ; PUSH_EAX ; We are writing eax 8D0C24 ; LEA32_ECX_from_esp ; Get stack address 8B1D &Output ; LOAD32_Absolute32_ebx &Output ; Write to target file B8 04000000 ; LOADI32_EAX %4 ; the syscall number for write BA 01000000 ; LOADI32_EDX %1 ; set the size of chars we want CD80 ; INT_80 ; call the Kernel 58 ; POP_EAX ; Restore stack 5B ; POP_EBX ; Restore EBX 59 ; POP_ECX ; Restore ECX 5A ; POP_EDX ; Restore EDX C3 ; RET :Output 00000000 ; NULL :Input 00000000 ; NULL :ELF_end