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