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