;; 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 . DEFINE ADDI8_EAX 83C0 DEFINE ADDI8_EBX 83C3 DEFINE ADDI8_ECX 83C1 DEFINE ADDI8_EDX 83C2 DEFINE ADDI8_ESI 83C6 DEFINE ADD_eax_into_ebx 01C3 DEFINE ADD_ebx_into_eax 01D8 DEFINE ADD_ecx_into_eax 01C8 DEFINE ADD_edi_into_ecx 01F9 DEFINE AND_EAX_EBX 21D8 DEFINE ANDI32_EAX 25 DEFINE CALL_EAX FFD0 DEFINE CALL32 E8 DEFINE CMPI8_EAX 83F8 DEFINE CMPI8_EBP 83fd DEFINE CMPI8_EBX 83FB DEFINE CMPI8_ECX 83F9 DEFINE CMPI8_EDX 83FA DEFINE CMPI8_ESI 83FE DEFINE CMP_EAX_EBX 39D8 DEFINE CMP_EAX_ECX 39C8 DEFINE CMP_EBX_ECX 39D9 DEFINE CMP_ECX_EBX 39CB DEFINE CMP_EDI_ESI 39FE DEFINE CMP_EBX_EDX 39D3 DEFINE COPY_EAX_to_EBP 89C5 DEFINE COPY_EAX_to_EBX 89C3 DEFINE COPY_EAX_to_ECX 89C1 DEFINE COPY_EAX_to_EDX 89C2 DEFINE COPY_EAX_to_ESI 89C6 DEFINE COPY_EBP_to_EAX 89E8 DEFINE COPY_EBX_to_EAX 89D8 DEFINE COPY_EBX_to_ECX 89D9 DEFINE COPY_EBX_to_EDX 89DA DEFINE COPY_ECX_to_EAX 89C8 DEFINE COPY_ECX_to_EBX 89CB DEFINE COPY_EDI_to_ESI 89FE DEFINE COPY_EDX_to_EAX 89D0 DEFINE COPY_EDX_to_EBP 89D5 DEFINE COPY_EDX_to_EBX 89D3 DEFINE COPY_ESI_to_EAX 89F0 DEFINE COPY_ESI_to_EDI 89F7 DEFINE IDIV_EBX F7FB DEFINE IMUL_EAX_by_EBX 0FAFC3 DEFINE IMULI8_EAX 6BC0 DEFINE IMULI8_EBP 6BED DEFINE INT_80 CD80 DEFINE JBE8 76 DEFINE JE32 0F84 DEFINE JG32 0F8F DEFINE JG8 7F DEFINE JL32 0F8C DEFINE JLE32 0F8E DEFINE JMP32 E9 DEFINE JNE32 0F85 DEFINE LEA32_ECX_from_esp 8D0C24 DEFINE LOAD32_Absolute32_eax A1 DEFINE LOAD32_Absolute32_ebx 8B1D DEFINE LOAD32_Absolute32_ecx 8B0D DEFINE LOAD32_EAX_from_EAX 8B00 DEFINE LOAD32_EAX_from_EAX_Immediate8 8B40 DEFINE LOAD32_EAX_from_EBP_Immediate8 8B45 DEFINE LOAD32_EAX_from_EBX 8B03 DEFINE LOAD32_EAX_from_EBX_Immediate8 8B43 DEFINE LOAD32_EAX_from_ECX_Immediate8 8B41 DEFINE LOAD32_EAX_from_EDX_Immediate8 8B42 DEFINE LOAD32_EBP_from_EBP 8B6D00 DEFINE LOAD32_EBX_from_EAX_Immediate8 8B58 DEFINE LOAD32_EBX_from_EBX 8B1B DEFINE LOAD32_EBX_from_EBX_Immediate8 8B5B 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_ECX_from_ECX_Immediate8 8B49 DEFINE LOAD32_ECX_from_EDX_Immediate8 8B4A DEFINE LOAD32_EDI_from_EDX_Immediate8 8B7A DEFINE LOAD32_EDX_from_EDX_Immediate8 8B52 DEFINE LOAD8_al_from_EAX 8A00 DEFINE LOAD8_al_from_EBX 8A03 DEFINE LOAD8_al_from_ECX 8A01 DEFINE LOAD8_al_from_EDX 8A02 DEFINE LOAD8_bl_from_EBX 8A1B DEFINE LOAD8_bl_from_ECX 8A19 DEFINE LOAD8_bl_from_EDX 8A1A 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 LOADI32_ESI BE DEFINE MOVZX_al 0FB6C0 DEFINE MOVZX_bl 0FB6DB DEFINE MOVZX_cl 0FB6C9 DEFINE NULL 00000000 DEFINE POP_EAX 58 DEFINE POP_EBP 5D DEFINE POP_EBX 5B DEFINE POP_ECX 59 DEFINE POP_EDI 5F DEFINE POP_EDX 5A DEFINE POP_ESI 5E DEFINE PUSH_EAX 50 DEFINE PUSH_EBP 55 DEFINE PUSH_EBX 53 DEFINE PUSH_ECX 51 DEFINE PUSH_EDI 57 DEFINE PUSH_EDX 52 DEFINE PUSH_ESI 56 DEFINE RETURN C3 DEFINE SALI8_EAX C1E0 DEFINE SHRI8_EAX C1E8 DEFINE SHRI8_EBX C1EB DEFINE STORE32_Absolute32_eax A3 DEFINE STORE32_Absolute32_ebx 891D DEFINE STORE32_Absolute32_ecx 890D DEFINE STORE32_Absolute32_edx 8915 DEFINE STORE32_EAX_into_EBP_Immediate8 8945 DEFINE STORE32_EAX_into_EBX 8903 DEFINE STORE32_EAX_into_ECX_Immediate8 8941 DEFINE STORE32_EAX_into_EDX 8902 DEFINE STORE32_EAX_into_EDX_Immediate8 8942 DEFINE STORE32_EBP_into_EDX_Immediate8 896A DEFINE STORE32_EBX_into_EAX 8918 DEFINE STORE32_EBX_into_EAX_Immediate8 8958 DEFINE STORE32_EBX_into_EDX_Immediate8 895A DEFINE STORE32_ECX_into_EAX 8908 DEFINE STORE32_ECX_into_EAX_Immediate8 8948 DEFINE STORE32_ECX_into_EDX_Immediate8 894A DEFINE STORE32_EDX_into_EAX_Immediate8 8950 DEFINE STORE32_EDX_into_EBP_Immediate8 8955 DEFINE STORE32_ESI_into_EBP_Immedate8 8975 DEFINE STORE32_ESI_into_EDX_Immedate8 8972 DEFINE STORE8_al_into_Address_EBX 8803 DEFINE STORE8_al_into_Address_ECX 8801 DEFINE STORE8_al_into_Address_ESI 8806 DEFINE STORE8_bl_into_Address_ECX 8819 DEFINE SUBI8_EAX 83E8 DEFINE SUBI8_ECX 83E9 DEFINE SUBI8_ESI 83EE DEFINE SWAP_EAX_EBX 93 ;; Register usage: ;; EAX => Temps ;; Struct TYPE format: (size 28) ;; NEXT => 0 ;; SIZE => 4 ;; OFFSET => 8 ;; INDIRECT => 12 ;; MEMBERS => 16 ;; TYPE => 20 ;; NAME => 24 ;; Struct TOKEN_LIST format: (size 20) ;; NEXT => 0 ;; LOCALS/PREV => 4 ;; S => 8 ;; TYPE => 12 ;; ARGS/DEPTH => 16 ; Where the ELF Header is going to hit ; Simply jump to _start ; Our main function :_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_file ; 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 RW 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_file ; 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 STORE32_Absolute32_eax &MALLOC ; Set our malloc pointer LOADI32_EAX %0 ; HEAD = NULL CALL32 %read_all_tokens ; Read all tokens CALL32 %Reverse_List ; Reverse order ; CALL32 %debug_list ; Try to figure out what is wrong STORE32_Absolute32_eax &global_token ; Set global_token CALL32 %program ; Convert into program LOADI32_EAX &header_string1 ; Our header string CALL32 %File_Print ; Print it LOAD32_Absolute32_eax &output_list ; Our output_list CALL32 %recursive_output ; Print core program ; LOADI32_EAX &header_string2 ; Our Enable debug ; CALL32 %File_Print ; Print it LOADI32_EAX &header_string3 ; Our second label CALL32 %File_Print ; Print it LOAD32_Absolute32_eax &globals_list ; Our globals CALL32 %recursive_output ; Get them LOADI32_EAX &header_string4 ; Our final header CALL32 %File_Print ; Print it LOAD32_Absolute32_eax &strings_list ; Our strings CALL32 %recursive_output ; Get them LOADI32_EAX &header_string5 ; Make this a bare assembly CALL32 %File_Print ; Print it :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 :header_string1 " # Core program " :header_string2 " :ELF_data " :header_string3 " # Program global variables " :header_string4 " # Program strings " :header_string5 " :ELF_end " ;; read_all_tokens function ;; Receives Token_List* in EAX ;; Tokenizes all input and returns updated list in EAX ;; Returns TOKEN in EAX ;; Uses EAX for C :read_all_tokens STORE32_Absolute32_eax &Token CALL32 %fgetc :read_all_tokens_loop CMPI8_EAX !-4 ; Check for EOF JE32 %read_all_tokens_done ; Stop if found CALL32 %get_token ; Read all tokens JMP32 %read_all_tokens_loop ; Loop :read_all_tokens_done LOAD32_Absolute32_eax &Token RETURN ;; get_token function ;; Receives INT in EAX ;; Makes a list of TOKEN_LIST ;; C and STRING_INDEX are stored in memory, ECX is used for S and EDX is used for current ;; Returns C in EAX :get_token PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX STORE32_Absolute32_eax &C ; Set C LOADI32_EAX %20 ; Malloc CURRENT CALL32 %malloc ; Get Pointer COPY_EAX_to_EDX ; Set CURRENT LOADI32_EAX %256 ; Malloc the string CALL32 %malloc ; Get pointer to S COPY_EAX_to_ECX ; Set S STORE32_ECX_into_EDX_Immediate8 !8 ; CURRENT->S = S :reset STORE32_Absolute32_ecx &string_index ; S[0] LOAD32_Absolute32_eax &C ; Using C CALL32 %clear_white_space ; Clear WhiteSpace STORE32_Absolute32_eax &C ; Set C CMPI8_EAX !-4 ; Check for EOF JE32 %get_token_abort ; if EOF abort CMPI8_EAX !35 ; Check for '#' JNE32 %get_token_alpha ; Nope ;; Deal with # line comments CALL32 %purge_macro ; Let it handle it STORE32_Absolute32_eax &C ; Set C JMP32 %reset ; Try again :get_token_alpha LOAD32_Absolute32_eax &C ; Send C LOADI32_EBX &alphas ; Get alphanumerics CALL32 %In_Set ; See if in set CMPI8_EAX !1 ; IF TRUE JNE32 %get_token_symbol ; Otherwise ;; Store keywords LOAD32_Absolute32_eax &C ; Send C CALL32 %preserve_keyword ; Store STORE32_Absolute32_eax &C ; Set C JMP32 %get_token_done ; Be done with this token :get_token_symbol LOAD32_Absolute32_eax &C ; Send C LOADI32_EBX &symbols ; Get symbols CALL32 %In_Set ; See if in set CMPI8_EAX !1 ; IF TRUE JNE32 %get_token_strings ; Otherwise ;; Store symbols LOAD32_Absolute32_eax &C ; Send C CALL32 %preserve_symbol ; Store STORE32_Absolute32_eax &C ; Set C JMP32 %get_token_done ; Be done with this token :get_token_strings LOAD32_Absolute32_eax &C ; Send C LOADI32_EBX &strings ; Get symbols CALL32 %In_Set ; See if in set CMPI8_EAX !1 ; IF TRUE JNE32 %get_token_comment ; Otherwise ;; Store String LOAD32_Absolute32_eax &C ; Send C CALL32 %consume_word ; Store STORE32_Absolute32_eax &C ; Set C JMP32 %get_token_done ; Be done with this token :get_token_comment LOAD32_Absolute32_eax &C ; Send C CMPI8_EAX !47 ; IF '/' == C JNE32 %get_token_else ; Otherwise CALL32 %consume_byte ; Hope it just is '/' STORE32_Absolute32_eax &C ; Set C CMPI8_EAX !42 ; IF '*' we have '/*' JNE32 %get_token_comment_line ; Check for '//' ;; Deal with /* block comments */ CALL32 %fgetc ; get next C STORE32_Absolute32_eax &C ; Set C :get_token_comment_block_outer LOAD32_Absolute32_eax &C ; Using C CMPI8_EAX !47 ; IF '/' != C JE32 %get_token_comment_block_done ; be done :get_token_comment_block_inner LOAD32_Absolute32_eax &C ; Using C CMPI8_EAX !42 ; IF '*' != C JE32 %get_token_comment_block_iter ; jump over ;; Deal with inner loop CALL32 %fgetc ; get next C STORE32_Absolute32_eax &C ; Set C JMP32 %get_token_comment_block_inner ; keep going :get_token_comment_block_iter CALL32 %fgetc ; get next C STORE32_Absolute32_eax &C ; Set C JMP32 %get_token_comment_block_outer :get_token_comment_block_done CALL32 %fgetc ; get next C STORE32_Absolute32_eax &C ; Set C JMP32 %reset ; throw away, try again :get_token_comment_line CMPI8_EAX !47 ; IF '/' we have // JNE32 %get_token_done ; keep if just '/' ;; Deal with // line comment CALL32 %fgetc ; drop to match STORE32_Absolute32_eax &C ; Set C JMP32 %reset ; throw away, try again :get_token_else LOAD32_Absolute32_eax &C ; Send C CALL32 %consume_byte STORE32_Absolute32_eax &C ; Set C :get_token_done LOAD32_Absolute32_eax &Token ; TOKEN STORE32_EAX_into_EDX_Immediate8 !4 ; CURRENT->PREV = TOKEN STORE32_EAX_into_EDX ; CURRENT->NEXT = TOKEN STORE32_Absolute32_edx &Token ; TOKEN = CURRENT :get_token_abort POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX LOAD32_Absolute32_eax &C ; Return C RETURN ;; 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 [MALLOC] to be initialized and EAX to have the number of desired bytes :malloc PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX LOAD32_Absolute32_ebx &MALLOC ; Using the current pointer ADD_eax_into_ebx ; Request the number of desired bytes LOADI32_EAX %45 ; the Syscall # for SYS_BRK INT_80 ; call the Kernel LOAD32_Absolute32_eax &MALLOC ; Return pointer STORE32_Absolute32_ebx &MALLOC ; Update pointer POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; clear_white_space function ;; Receives INT C in EAX ;; Returns first non-whitespace char in EAX :clear_white_space CMPI8_EAX !32 ; Check for ' ' JE32 %clear_white_space_wipe ; wipe it out CMPI8_EAX !10 ; Check for '\n' JE32 %clear_white_space_wipe ; wipe it output CMPI8_EAX !9 ; Check for '\t' JNE32 %clear_white_space_done ; looks like non-whitespace :clear_white_space_wipe CALL32 %fgetc ; Read a new byte CMPI8_EAX !-4 ; Check for EOF JE32 %clear_white_space_done ; Short circuit JMP32 %clear_white_space ; iterate :clear_white_space_done RETURN ;; 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 RETURN :In_Set_False LOADI32_EAX %0 ; Set FALSE POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :alphas "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" :symbols "<=>|&!-" :strings '22 27 00' ;; purge_macro function ;; Receives CH in EAX ;; Reads chars until Line feed is read ;; returns line feed :purge_macro CALL32 %fgetc ; read next char CMPI8_EAX !10 ; Check for '\n' JNE32 %purge_macro ; Keep going RETURN ;; preserve_keyword function ;; Receives INT C in EAX ;; collects all chars in keyword ;; Returns C in EAX ;; Uses ECX for INT C :preserve_keyword PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX COPY_EAX_to_ECX ; Setup C LOADI32_EBX &alphas ; Concerning ourselves with "abc.." :preserve_keyword_loop CALL32 %In_Set ; Check if alphanumerics CMPI8_EAX !1 ; IF TRUE JNE32 %preserve_keyword_label ; Otherwise check for label COPY_ECX_to_EAX ; Pass C CALL32 %consume_byte ; consume that byte COPY_EAX_to_ECX ; Update C JMP32 %preserve_keyword_loop ; keep looping :preserve_keyword_label COPY_ECX_to_EAX ; Fix return CMPI8_EAX !58 ; Check for ':' JNE32 %preserve_keyword_done ; be done ;; Fix our goto label CALL32 %fixup_label ; Fix the label LOADI32_EAX %32 ; Return Whitespace :preserve_keyword_done POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; preserve_symbol function ;; Receives INT C in EAX ;; collects all chars in symbol ;; Returns C in EAX ;; Uses ECX for INT C :preserve_symbol PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX COPY_EAX_to_ECX ; Setup C LOADI32_EBX &symbols ; Concerning ourselves with "<=>.." :preserve_symbol_loop CALL32 %In_Set ; Check if alphanumerics CMPI8_EAX !1 ; IF TRUE JNE32 %preserve_symbol_done ; Otherwise be done COPY_ECX_to_EAX ; Pass C CALL32 %consume_byte ; consume that byte COPY_EAX_to_ECX ; Update C JMP32 %preserve_symbol_loop ; keep looping :preserve_symbol_done COPY_ECX_to_EAX ; Fix return POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; consume_word function ;; receives INT C in EAX ;; returns INT C in EAX ;; Uses EAX for C, EBX for FREQ and ECX for ESCAPE :consume_word PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX COPY_EAX_to_EBX ; FREQ = C LOADI32_ECX %0 ; ESCAPE = FALSE :consume_word_loop CMPI8_ECX !0 ; IF !ESCAPE JNE32 %consume_word_escape ; Enable escape CMPI8_EAX !92 ; if '\\' JNE32 %consume_word_iter ; keep state LOADI32_ECX %1 ; ESCAPE = TRUE JMP32 %consume_word_iter ; keep going :consume_word_escape LOADI32_ECX %0 ; ESCAPE = FALSE :consume_word_iter CALL32 %consume_byte ; read next char CMPI8_ECX !0 ; IF ESCAPE JNE32 %consume_word_loop ; keep looping CMP_EAX_EBX ; IF C != FREQ JNE32 %consume_word_loop ; keep going CALL32 %fgetc ; return next char POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; consume_byte function ;; Receives INT C in EAX ;; Inserts C into string S, updates String S ;; Returns Next char in EAX :consume_byte PUSH_EBX ; Protect EBX LOAD32_Absolute32_ebx &string_index ; S[0] STORE8_al_into_Address_EBX ; S[0] = C ADDI8_EBX !1 ; S = S + 1 STORE32_Absolute32_ebx &string_index ; Update S CALL32 %fgetc POP_EBX ; Restore EBX RETURN ;; fixup_label function ;; Receives S in ECX ;; prepends ':' to string and returns registers un changed ;; Uses EAX for HOLD, EBX for PREV and ECX for S[0] :fixup_label PUSH_EAX ; Protect EAX PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOADI32_EAX %58 ; HOLD = ':' LOAD32_ECX_from_EDX_Immediate8 !8 ; HOLD_STRING[0] :fixup_label_loop COPY_EAX_to_EBX ; PREV = HOLD LOAD8_al_from_ECX ; HOLD = HOLD_STRING[I] MOVZX_al ; make useful STORE8_bl_into_Address_ECX ; HOLD_STRING[I] = PREV ADDI8_ECX !1 ; I = I + 1 CMPI8_EAX !0 ; IF NULL == HOLD JNE32 %fixup_label_loop ; Keep looping POP_ECX ; Restore ECX POP_EBX ; Restore EBX POP_EAX ; Restore EAX RETURN ;; fgetc function ;; Loads FILE* from [INPUT_FILE] ;; Returns -4 (EOF) or char in EAX :fgetc PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX 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_file ; 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 CMPI8_EAX !-4 ; Check for EOF JE32 %fgetc_done ; Return as is MOVZX_al ; Make it useful :fgetc_done POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; 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_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 RETURN ;; recursive_output function ;; Receives list in EAX ;; walks the list and prints the I->S for all nodes backwards ;; Uses EBX for I :recursive_output PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX CMPI8_EAX !0 ; Check for NULL JE32 %recursive_output_done ; Skip the work COPY_EAX_to_EBX ; I = Head LOAD32_EAX_from_EBX ; Iterate to next Token CALL32 %recursive_output ; Recurse LOAD32_EAX_from_EBX_Immediate8 !8 ; Using S CALL32 %File_Print ; Print it :recursive_output_done POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; 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 RETURN ;; fputc function ;; receives CHAR in EAX and load FILE* from [OUTPUT_FILE] ;; writes char and returns :fputc PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX PUSH_EAX ; We are writing eax LEA32_ECX_from_esp ; Get stack address LOAD32_Absolute32_ebx &Output_file ; 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_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; program function ;; receives nothing, returns nothing ;; Uses EAX for type_size :program ;; The binary initialized the globals to null, so we can skip those steps PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX :new_type LOAD32_Absolute32_eax &global_token ; Using global_token CMPI8_EAX !0 ; Check if NULL JE32 %program_done ; Be done if null LOAD32_EBX_from_EAX_Immediate8 !8 ; GLOBAL_TOKEN->S LOADI32_EAX &constant ; "CONSTANT" CALL32 %match ; IF GLOBAL_TOKEN->S == "CONSTANT" CMPI8_EAX !0 ; If true JNE32 %program_else ; Looks like not a constant ;; Deal with minimal constant case LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOADI32_EBX %0 ; NULL LOAD32_Absolute32_ecx &global_constant_list ; global_constant_list CALL32 %sym_declare ; Declare that constant STORE32_Absolute32_eax &global_constant_list ; global_constant_list = sym_declare(global_token->s, NULL, global_constant_list); LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX ; global_token->next STORE32_EBX_into_EAX_Immediate8 !16 ; global_constant_list->arguments = global_token->next LOAD32_EBX_from_EBX ; global_token->next->next STORE32_Absolute32_ebx &global_token ; global_token = global_token->next->next JMP32 %new_type ; go around again :program_else CALL32 %type_name ; Figure out the type_size CMPI8_EAX !0 ; IF NULL == type_size JE32 %new_type ; it was a new type ;; Add to global symbol table COPY_EAX_to_EBX ; put type_size in the right spot LOAD32_Absolute32_eax &global_token ; Using global token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD32_Absolute32_ecx &global_symbol_list ; Using global_symbol_list CALL32 %sym_declare ; Declare symbol STORE32_Absolute32_eax &global_symbol_list ; global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list); LOAD32_Absolute32_ebx &global_token ; Using global token LOAD32_EBX_from_EBX ; global_token->next STORE32_Absolute32_ebx &global_token ; global_token = global_token->next LOAD32_Absolute32_ebx &global_token ; Using global token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &semicolon ; ";" CALL32 %match ; if(match(";", global_token->s)) CMPI8_EAX !0 ; If true JNE32 %program_function ; looks like not a match ;; Deal with the global variable LOAD32_Absolute32_ebx &globals_list ; Using globals_list LOADI32_EAX &program_string_0 ; ":GLOBAL_" CALL32 %emit ; Emit it COPY_EAX_to_EBX ; update globals_list LOAD32_Absolute32_eax &global_token ; Using global token LOAD32_EAX_from_EAX_Immediate8 !4 ; global token->prev LOAD32_EAX_from_EAX_Immediate8 !8 ; global token->prev->s CALL32 %emit ; Emit it COPY_EAX_to_EBX ; update globals_list LOADI32_EAX &program_string_1 ; "\nNOP\n" CALL32 %emit ; Emit it STORE32_Absolute32_eax &globals_list ; update globals_list LOAD32_Absolute32_eax &global_token ; Using global token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next JMP32 %new_type ; go around again :program_function LOAD32_Absolute32_ebx &global_token ; Using global token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &open_paren ; "(" CALL32 %match ; if(match(";", global_token->s)) CMPI8_EAX !0 ; If true JNE32 %program_error ; Otherwise deal with error case ;; Deal with function definition CALL32 %declare_function ; Lets get the parsing rolling JMP32 %new_type ; Keep looping through functions :program_error ;; Deal with the case of something we don't support ;; NOT IMPLEMENTED :program_done POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; Strings needed by the program function :program_string_0 ":GLOBAL_" :program_string_1 " NOP " ;; declare_function function ;; Receives nothing and returns nothing ;; Sets current function and adds it to the global function list :declare_function PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOADI32_EAX %0 ; Using NULL STORE32_Absolute32_eax ¤t_count ; current_count = 0 LOAD32_Absolute32_eax &global_token ; Using global token LOAD32_EAX_from_EAX_Immediate8 !4 ; global token->prev LOAD32_EAX_from_EAX_Immediate8 !8 ; global token->prev->s LOADI32_EBX %0 ; NULL LOAD32_Absolute32_ecx &global_function_list ; global_function_list CALL32 %sym_declare ; sym_declare(global_token->prev->s, NULL, global_function_list); STORE32_Absolute32_eax &function ; function = sym_declare(global_token->prev->s, NULL, global_function_list); STORE32_Absolute32_eax &global_function_list ; global_function_list = function CALL32 %collect_arguments ; collect all of the function arguments LOAD32_Absolute32_eax &global_token ; Using global token LOAD32_EAX_from_EAX_Immediate8 !8 ; global token->s LOADI32_EBX &semicolon ; ";" CALL32 %match ; IF global token->s == ";" CMPI8_EAX !0 ; If true JNE32 %declare_function_full ; It was a prototype ;; Deal with prototypes LOAD32_Absolute32_eax &global_token ; Using global token LOAD32_EAX_from_EAX ; global token->next STORE32_Absolute32_eax &global_token ; global token = global token->next JMP32 %declare_function_done ; Move on :declare_function_full ;; Deal will full function definitions LOADI32_EAX &declare_function_string_0 ; "# Defining function " CALL32 %emit_out ; emit it LOAD32_Absolute32_eax &function ; function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->s CALL32 %emit_out ; emit it LOADI32_EAX &declare_function_string_1 ; "\n:FUNCTION_" CALL32 %emit_out ; emit it LOAD32_Absolute32_eax &function ; function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->s CALL32 %emit_out ; emit it LOADI32_EAX &declare_function_string_3 ; "\n" CALL32 %emit_out ; emit it CALL32 %statement ; Recursively get the function pieces LOAD32_Absolute32_eax &output_list ; output LOAD32_EAX_from_EAX_Immediate8 !8 ; output->s LOADI32_EBX &declare_function_string_2 ; "RETURN\n" CALL32 %match ; IF output->s == "RETURN\n" CMPI8_EAX !0 ; If true we can skip adding it JE32 %declare_function_done ; otherwise we need to add it ;; Add the return to the end of a function lacking a return; LOADI32_EAX &declare_function_string_2 ; "RETURN\n" CALL32 %emit_out ; emit it :declare_function_done POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :declare_function_string_0 "# Defining function " :declare_function_string_1 " :FUNCTION_" :declare_function_string_2 "RETURN " :declare_function_string_3 " " ;; collect_arguments function ;; Receives nothing ;; Returns Nothing ;; Adds arguments to the function definition ;; holds struct type* type_size in ECX, then replace with struct token_list* a in ECX when type_size is used :collect_arguments PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next :collect_arguments_loop LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &close_paren ; ")" CALL32 %match ; IF global_token->S == ")" CMPI8_EAX !0 ; we reached the end JE32 %collect_arguments_done ; be done ;; deal with the case of there are arguments CALL32 %type_name ; Get the type COPY_EAX_to_ECX ; put type_size safely out of the way LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &close_paren ; ")" CALL32 %match ; IF global_token->S == ")" CMPI8_EAX !0 ; is a foo(int, char,void) case JE32 %collect_arguments_common ; deal with commas ;; Trying second else LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &comma ; "," CALL32 %match ; IF global_token->S == "," CMPI8_EAX !0 ; then deal with the common JE32 %collect_arguments_common ; case of commas between arguments ;; deal with foo(int a, char b) LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S COPY_ECX_to_EBX ; put type_size in the right place LOAD32_Absolute32_ecx &function ; Using function LOAD32_ECX_from_ECX_Immediate8 !16 ; function->args CALL32 %sym_declare ; sym_declare(global_token->s, type_size, function->arguments); COPY_EAX_to_ECX ; put a in a safe place LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !16 ; function->args CMPI8_EAX !0 ; IF function->args == NULL JNE32 %collect_arguments_another ; otherwise it isn't the first ;; Deal with the case of first argument in the function LOADI32_EAX %-4 ; -4 STORE32_EAX_into_ECX_Immediate8 !16 ; a->depth = -4 JMP32 %collect_arguments_next ; get to next :collect_arguments_another ;; deal with the case of non-first arguments LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !16 ; function->args LOAD32_EAX_from_EAX_Immediate8 !16 ; function->args->depth SUBI8_EAX !4 ; function->args->depth - 4 STORE32_EAX_into_ECX_Immediate8 !16 ; a->depth = function->args->depth - 4 :collect_arguments_next LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next LOAD32_Absolute32_eax &function ; Using function STORE32_ECX_into_EAX_Immediate8 !16 ; function->args = a :collect_arguments_common LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &comma ; "," CALL32 %match ; IF global_token->S == "," CMPI8_EAX !0 ; then deal with the comma JNE32 %collect_arguments_loop ; otherwise loop ;; keep foo(bar(), 1) expressions working LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next JMP32 %collect_arguments_loop ; keep going :collect_arguments_done LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; statement function ;; Receives nothing ;; Returns nothing ;; Walks down global_token recursively to collect the contents of the function :statement PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &open_curly_brace ; "{" CALL32 %match ; IF global_token->S == "{" JNE32 %statement_label ; otherwise try label ;; deal with { statement } CALL32 %recursive_statement ; Statements inside of statements for days JMP32 %statement_done ; Be done :statement_label LOAD8_al_from_EBX ; global_token->S[0] MOVZX_al ; make it useful CMPI8_EAX !58 ; IF global_token->S == ':' JNE32 %statement_local ; otherwise try locals ;; deal with labels COPY_EBX_to_EAX ; put global_token->S in the right spot CALL32 %emit_out ; emit it LOADI32_EAX &statement_string_0 ; Using "\t#C goto label\n" CALL32 %emit_out ; emit it LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next JMP32 %statement_done ; be done :statement_local COPY_EBX_to_EAX ; put global_token->S in the right place LOADI32_EBX &prim_types ; pointer to primative types CALL32 %lookup_type ; See if found CMPI8_EAX !0 ; IF NULL == lookup_type(global_token->S, prim_types) JNE32 %statement_local_success ; Sweet a new local ;; Second chance LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &struct ; "struct" CALL32 %match ; IF global_token->S == "struct" CMPI8_EAX !0 ; then we are a local JNE32 %statement_if ; otherwise try IF :statement_local_success CALL32 %collect_local ; Grab those locals JMP32 %statement_done ; be done :statement_if LOADI32_EAX &if_string ; Using "if" CALL32 %match ; IF global_token->S == "if" CMPI8_EAX !0 ; then we have an if statement JNE32 %statement_do ; otherwise try DO ;; Deal with IF statement CALL32 %process_if ; DO IT JMP32 %statement_done ; be done :statement_do LOADI32_EAX &do_string ; Using "do" CALL32 %match ; IF global_token->S == "do" CMPI8_EAX !0 ; then we have a do statement JNE32 %statement_while ; otherwise try WHILE ;; Deal with DO statement CALL32 %process_do ; DO IT JMP32 %statement_done ; be done :statement_while LOADI32_EAX &while_string ; Using "while" CALL32 %match ; IF global_token->S == "while" CMPI8_EAX !0 ; then we have a while statement JNE32 %statement_for ; otherwise try FOR ;; Deal with WHILE statement CALL32 %process_while ; DO IT JMP32 %statement_done ; be done :statement_for LOADI32_EAX &for_string ; Using "for" CALL32 %match ; IF global_token->S == "for" CMPI8_EAX !0 ; then we have a for statement JNE32 %statement_asm ; otherwise try ASM ;; Deal with FOR statement CALL32 %process_for ; DO IT JMP32 %statement_done ; be done :statement_asm LOADI32_EAX &asm_string ; Using "asm" CALL32 %match ; IF global_token->S == "asm" CMPI8_EAX !0 ; then we have an asm statement JNE32 %statement_goto ; otherwise try GOTO ;; Deal with ASM statement CALL32 %process_asm ; Hit it JMP32 %statement_done ; be done :statement_goto LOADI32_EAX &goto_string ; Using "goto" CALL32 %match ; IF global_token->S == "goto" CMPI8_EAX !0 ; then we have a goto statement JNE32 %statement_return ; Otherwise try RETURN ;; Deal with GOTO statement LOADI32_EAX &statement_string_1 ; Using "JUMP %" CALL32 %emit_out ; emit it LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S CALL32 %emit_out ; emit it LOADI32_EAX &statement_string_2 ; Using "\n" CALL32 %emit_out ; emit it LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next LOADI32_EAX &statement_string_4 ; Using "ERROR in statement\nMissing ;\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Make sure it has the required JMP32 %statement_done ; Be done :statement_return LOADI32_EAX &return_string ; Using "return" CALL32 %match ; IF global_token->S == "return" CMPI8_EAX !0 ; then we have a return statement JNE32 %statement_break ; Otherwise try BREAK ;; Deal with RETURN Statement CALL32 %return_result ; Return anything they want JMP32 %statement_done ; be done :statement_break LOADI32_EAX &break_string ; Using "break" CALL32 %match ; IF global_token->S == "break" CMPI8_EAX !0 ; then we have a break statement JNE32 %statement_continue ; Otherwise try CONTINUE ;; Deal with BREAK statement CALL32 %process_break ; Lets do some damage JMP32 %statement_done ; be done :statement_continue LOADI32_EAX &continue_string ; Using "continue" CALL32 %match ; IF global_token->S == "continue" CMPI8_EAX !0 ; then we have a continue statement JNE32 %statement_else ; Otherwise we are punting to an expression ;; Deal with CONTINUE statement LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next LOADI32_EAX &statement_string_3 ; Using "\n#continue statement\n" CALL32 %emit_out ; emit it LOADI32_EAX &statement_string_4 ; Using "ERROR in statement\nMissing ;\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Don't forget the ";" JMP32 %statement_done ; Be done :statement_else CALL32 %expression ; Collect expression LOADI32_EAX &statement_string_4 ; Using "ERROR in statement\nMissing ;\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; make sure we have it :statement_done POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :statement_string_0 " #C goto label " :statement_string_1 "JUMP %" :statement_string_2 " " :statement_string_3 " #continue statement " :statement_string_4 "ERROR in statement Missing ; " ;; recursive_statement function ;; Receives nothing ;; Returns nothing ;; Walks the global_token list to build the contents of statements ;; Uses struct token_list* frame in ECX :recursive_statement PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next LOAD32_Absolute32_ecx &function ; Using function LOAD32_ECX_from_ECX_Immediate8 !4 ; frame = function->locals :recursive_statement_loop LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &close_curly_brace ; Using "}" CALL32 %match ; IF global_token->S == "}" CMPI8_EAX !0 ; Then we are done recuring JE32 %recursive_statement_cleanup ; and then we clean up ;; Deal with the recursive calls CALL32 %statement ; Deal with another statement JMP32 %recursive_statement_loop ; loop some more :recursive_statement_cleanup LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next LOADI32_EAX &recursive_statement_string_0 ; Using "RETURN\n" LOAD32_Absolute32_ebx &output_list ; Using output LOAD32_EBX_from_EBX_Immediate8 !8 ; output->S CALL32 %match ; IF output->S == "RETURN\n" CMPI8_EAX !0 ; Then we can skip the clean up JE32 %recursive_statement_done ; and be done ;; Deal with cleanup LOAD32_Absolute32_ebx &function ; Using function LOAD32_EBX_from_EBX_Immediate8 !4 ; i = function->locals LOADI32_EAX &recursive_statement_string_1 ; Using "POP_ebx\t# _recursive_statement_locals\n" :recursive_statement_locals CMP_ECX_EBX ; IF frame != i JE32 %recursive_statement_done ; Otherwise be done ;; Lets emit CALL32 %emit_out ; emit it LOAD32_EBX_from_EBX ; i = i->next JMP32 %recursive_statement_locals ; keep going :recursive_statement_done LOAD32_Absolute32_eax &function ; Using function STORE32_ECX_into_EAX_Immediate8 !4 ; function->locals = frame POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :recursive_statement_string_0 "RETURN " :recursive_statement_string_1 "POP_ebx # _recursive_statement_locals " ;; return_result function ;; Receives nothing ;; Returns nothing ;; Cleans up function and generates return ;; Also handles returing expressions :return_result PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; make it useful CMPI8_EAX !59 ; If global_token->S[0] == ';' JE32 %return_result_cleanup ; Go straight to cleanup CALL32 %expression ; get the expression we are returning :return_result_cleanup LOADI32_EAX &return_result_string_0 ; Using "ERROR in return_result\nMISSING ;\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Make sure we have it LOAD32_Absolute32_ebx &function ; Using function LOAD32_EBX_from_EBX_Immediate8 !4 ; function->locals LOADI32_EAX &return_result_string_1 ; Using "POP_ebx\t# _return_result_locals\n" :return_result_locals CMPI8_EBX !0 ; IF NULL == i JE32 %return_result_done ; Be done CALL32 %emit_out ; Emit out pop LOAD32_EBX_from_EBX ; i = i->NEXT JMP32 %return_result_locals ; Keep going :return_result_done LOADI32_EAX &return_result_string_2 ; Using "RETURN\n" CALL32 %emit_out ; Emit it POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :return_result_string_0 "ERROR in return_result MISSING ; " :return_result_string_1 "POP_ebx # _return_result_locals " :return_result_string_2 "RETURN " ;; collect_local function ;; Receives nothing ;; Returns nothing ;; Walks global_token list to create function locals ;; Uses ECX for struct token_list* A :collect_local PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX CALL32 %type_name ; Get the local's type COPY_EAX_to_EBX ; Put struct type* type_size in the right place LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD32_Absolute32_ecx &function ; Using function LOAD32_ECX_from_ECX_Immediate8 !4 ; function->locals CALL32 %sym_declare ; Declare it COPY_EAX_to_ECX ; put it away safely ;; Try for main LOADI32_EAX &main_string ; Using "main" LOAD32_Absolute32_ebx &function ; Using function LOAD32_EBX_from_EBX_Immediate8 !8 ; function->S CALL32 %match ; IF match("main", function->s) CMPI8_EAX !0 ; possible JNE32 %collect_local_fresh ; try to see if fresh function ;; Ok we are in main, now to see if main is fresh LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !4 ; function->locals CMPI8_EAX !0 ; IF NULL == function->locals JNE32 %collect_local_fresh ; try to see if fresh function ;; Sweet we are in a fresh main LOADI32_EAX %-20 ; We start at -20 STORE32_EAX_into_ECX_Immediate8 !16 ; a->DEPTH = -20 JMP32 %collect_local_common ; Go to the commons :collect_local_fresh LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !16 ; function->args CMPI8_EAX !0 ; IF NULL == function->locals JNE32 %collect_local_first ; Otherwise see if first LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !4 ; function->locals CMPI8_EAX !0 ; IF NULL == function->locals JNE32 %collect_local_first ; Otherwise try first ;; Sweet we are in a fresh function LOADI32_EAX %-8 ; We start at -8 STORE32_EAX_into_ECX_Immediate8 !16 ; a->DEPTH = -8 JMP32 %collect_local_common ; Go to the commons :collect_local_first LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !4 ; function->locals CMPI8_EAX !0 ; IF NULL == function->locals JNE32 %collect_local_else ; Looks like we are just another local ;; Ok we are the first local LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !16 ; function->args LOAD32_EAX_from_EAX_Immediate8 !16 ; function->args->depth SUBI8_EAX !8 ; function->arguments->depth - 8 STORE32_EAX_into_ECX_Immediate8 !16 ; a->DEPTH = function->arguments->depth - 8 JMP32 %collect_local_common ; Go to the commons :collect_local_else ;; Always the last to know LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !4 ; function->locals LOAD32_EAX_from_EAX_Immediate8 !16 ; function->locals->depth SUBI8_EAX !4 ; function->locals->depth - 4 STORE32_EAX_into_ECX_Immediate8 !16 ; a->DEPTH = function->locals->depth - 4 :collect_local_common LOAD32_Absolute32_eax &function ; Using function STORE32_ECX_into_EAX_Immediate8 !4 ; function->locals = a LOAD32_ECX_from_ECX_Immediate8 !8 ; a->S LOADI32_EAX &collect_local_string_0 ; Using "# Defining local " CALL32 %emit_out ; emit it LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S CALL32 %emit_out ; emit it LOADI32_EAX &collect_local_string_1 ; Using "\n" CALL32 %emit_out ; emit it LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOAD32_EBX_from_EAX_Immediate8 !8 ; global_token->S LOADI32_EAX &equal ; Using "=" CALL32 %match ; IF match("=", global_token->s) CMPI8_EAX !0 ; Deal with assignment JNE32 %collect_local_done ; Otherwise finish it ;; Deal with assignment LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT CALL32 %expression ; Recurse :collect_local_done LOADI32_EAX &collect_local_string_2 ; Using "ERROR in collect_local\nMissing ;\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Make sure we have it LOADI32_EAX &collect_local_string_3 ; Using "PUSH_eax\t#" CALL32 %emit_out ; emit it COPY_ECX_to_EAX ; put A->S where it belongs CALL32 %emit_out ; emit it LOADI32_EAX &collect_local_string_1 ; Using "\n" CALL32 %emit_out ; emit it POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :collect_local_string_0 "# Defining local " :collect_local_string_1 " " :collect_local_string_2 "ERROR in collect_local Missing ; " :collect_local_string_3 "PUSH_eax #" ;; process_asm function ;; Receives nothing ;; Returns nothing ;; Simply inlines the asm statements ;; Uses EBX for global_token temp storage :process_asm PUSH_EBX ; Protect EBX LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOADI32_EAX &process_asm_string_0 ; Using "ERROR in process_asm\nMISSING (\n" LOADI32_EBX &open_paren ; Using "(" CALL32 %require_match ; Make sure we have it LOAD32_Absolute32_ebx &global_token ; Using global_token :process_asm_iter LOAD32_EAX_from_EBX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; Make it useful CMPI8_EAX !34 ; IF global_token->S[0] == '\"' JNE32 %process_asm_done ; Otherwise be done LOAD32_EAX_from_EBX_Immediate8 !8 ; global_token->S ADDI8_EAX !1 ; global_token->S + 1 CALL32 %emit_out ; Emit it LOADI32_EAX &process_asm_string_1 ; Using "\n" CALL32 %emit_out ; Emit it LOAD32_EBX_from_EBX ; global_token->NEXT STORE32_Absolute32_ebx &global_token ; global_token = global_token->NEXT JMP32 %process_asm_iter ; keep going :process_asm_done LOADI32_EAX &process_asm_string_2 ; Using "ERROR in process_asm\nMISSING )\n" LOADI32_EBX &close_paren ; Using ")" CALL32 %require_match ; Make sure we have it LOADI32_EAX &process_asm_string_3 ; Using "ERROR in process_asm\nMISSING ;\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Make sure we have it POP_EBX ; Restore EBX RETURN :process_asm_string_0 "ERROR in process_asm MISSING ( " :process_asm_string_1 " " :process_asm_string_2 "ERROR in process_asm MISSING ) " :process_asm_string_3 "ERROR in process_asm MISSING ; " ;; process_if function ;; Receives nothing ;; Returns Nothing ;; Increments current_count recurses into expression + statement ;; Uses ECX for char* NUMBER_STRING :process_if PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOAD32_Absolute32_eax ¤t_count ; Using current count COPY_EAX_to_EBX ; Preparing for update ADDI8_EBX !1 ; current_count + 1 STORE32_Absolute32_ebx ¤t_count ; current_count = current_count + 1 CALL32 %numerate_number ; convert to string COPY_EAX_to_ECX ; put NUMBER_STRING in place LOADI32_EAX &process_if_string_0 ; Using "# IF_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOADI32_EAX &process_if_string_1 ; Using "ERROR in process_if\nMISSING (\n" LOADI32_EBX &open_paren ; Using "(" CALL32 %require_match ; Make sure we have it CALL32 %expression ; Recurse to get the IF(...) part LOADI32_EAX &process_if_string_2 ; Using "TEST\nJUMP_EQ %ELSE_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_if_string_3 ; Using "ERROR in process_if\nMISSING )\n" LOADI32_EBX &close_paren ; Using ")" CALL32 %require_match ; Make sure we have it CALL32 %statement ; Recursive to get the IF(){...} part LOADI32_EAX &process_if_string_4 ; Using "JUMP %_END_IF_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_if_string_5 ; Using ":ELSE_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &else_string ; Using "else" CALL32 %match ; IF global_token->S == "else" CMPI8_EAX !0 ; Then we need to collect the else too JNE32 %process_if_done ; Otherwise finish up ;; deal with else statement LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT CALL32 %statement ; Recurse to get the ELSE {...} part :process_if_done LOADI32_EAX &process_if_string_6 ; Using ":_END_IF_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :process_if_string_0 "# IF_" :process_if_string_1 "ERROR in process_if MISSING ( " :process_if_string_2 "TEST JUMP_EQ %ELSE_" :process_if_string_3 "ERROR in process_if MISSING ) " :process_if_string_4 "JUMP %_END_IF_" :process_if_string_5 ":ELSE_" :process_if_string_6 ":_END_IF_" ;; save_break_frame microfunction ;; Overwrites EAX abd EBX ;; Saves break frame on stack ;; Returns to caller :save_break_frame POP_EBX ; Save return Address LOAD32_Absolute32_eax &break_frame ; Get break_frame PUSH_EAX ; Store as nested_locals LOAD32_Absolute32_eax &break_target_head ; Get break_target_head PUSH_EAX ; Store as nested_break_head LOAD32_Absolute32_eax &break_target_func ; Get break_target_func PUSH_EAX ; Store as nested_break_func LOAD32_Absolute32_eax &break_target_num ; Get break_target_num PUSH_EAX ; Store as nested_break_num PUSH_EBX ; Put return back in place RETURN ; Return to caller ;; restore_break_frame microfunction ;; Overwrites EAX and EBX ;; Restores break frame from stack ;; Returns to caller :restore_break_frame POP_EBX ; Save return Address POP_EAX ; Get nested_break_num STORE32_Absolute32_eax &break_target_num ; Restore break_target_num POP_EAX ; Get nested_break_func STORE32_Absolute32_eax &break_target_func ; Restore break_target_func POP_EAX ; Get nested_break_head STORE32_Absolute32_eax &break_target_head ; Restore break_target_head POP_EAX ; Get nested_locals STORE32_Absolute32_eax &break_frame ; Restore break_frame PUSH_EBX ; Put return back in place RETURN ; Return to caller ;; set_break_frame microfunction ;; Receives char* head in EAX and char* num in EBX ;; Overwrites EAX and EBX ;; Returns to calling function :set_break_frame STORE32_Absolute32_eax &break_target_head ; update break_target_head STORE32_Absolute32_ebx &break_target_num ; update break_target_num LOAD32_Absolute32_ebx &function ; Using function LOAD32_EAX_from_EBX_Immediate8 !4 ; function->LOCALS STORE32_Absolute32_eax &break_frame ; break_frame = function->LOCALS LOAD32_EAX_from_EBX_Immediate8 !8 ; function->S STORE32_Absolute32_eax &break_target_func ; break_target_func = function->S RETURN ; Return to sender ;; process_do function ;; Receives Nothing ;; Returns Nothing ;; Increments current_count and leverages save/restore_break_frame pieces ;; Uses ECX for char* NUMBER_STRING :process_do PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX CALL32 %save_break_frame ; Save the frame LOAD32_Absolute32_eax ¤t_count ; Using current count COPY_EAX_to_EBX ; Preparing for update ADDI8_EBX !1 ; current_count + 1 STORE32_Absolute32_ebx ¤t_count ; current_count = current_count + 1 CALL32 %numerate_number ; convert to string COPY_EAX_to_ECX ; put NUMBER_STRING in place LOADI32_EAX &process_do_string_0 ; Using "DO_END_" COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %set_break_frame ; Set the frame LOADI32_EAX &process_do_string_1 ; Using ":DO_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT CALL32 %statement ; Do the DO {...} part LOADI32_EAX &process_do_string_2 ; Using "ERROR in process_do\nMISSING while\n" LOADI32_EBX &while_string ; Using "while" CALL32 %require_match ; Make sure we have it LOADI32_EAX &process_do_string_3 ; Using "ERROR in process_do\nMISSING (\n" LOADI32_EBX &open_paren ; Using "(" CALL32 %require_match ; Make sure we have it CALL32 %expression ; Do the WHILE (...) part LOADI32_EAX &process_do_string_4 ; Using "ERROR in process_do\nMISSING )\n" LOADI32_EBX &close_paren ; Using ")" CALL32 %require_match ; Make sure we have it LOADI32_EAX &process_do_string_5 ; Using "ERROR in process_do\nMISSING ;\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Make sure we have it LOADI32_EAX &process_do_string_6 ; Using "TEST\nJUMP_NE %DO_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_do_string_7 ; Using ":DO_END_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) CALL32 %restore_break_frame ; Restore the old break frame POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :process_do_string_0 "DO_END_" :process_do_string_1 ":DO_" :process_do_string_2 "ERROR in process_do MISSING while " :process_do_string_3 "ERROR in process_do MISSING ( " :process_do_string_4 "ERROR in process_do MISSING ) " :process_do_string_5 "ERROR in process_do MISSING ; " :process_do_string_6 "TEST JUMP_NE %DO_" :process_do_string_7 ":DO_END_" ;; process_while function ;; Receives nothing ;; Returns nothing ;; Increments current_count and leverages save/restore_break_frame pieces ;; Uses ECX for char* NUMBER_STRING :process_while PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX CALL32 %save_break_frame ; Save break_frame LOAD32_Absolute32_eax ¤t_count ; Using current count COPY_EAX_to_EBX ; Preparing for update ADDI8_EBX !1 ; current_count + 1 STORE32_Absolute32_ebx ¤t_count ; current_count = current_count + 1 CALL32 %numerate_number ; convert to string COPY_EAX_to_ECX ; put NUMBER_STRING in place LOADI32_EAX &process_while_string_0 ; Using "END_WHILE_" COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %set_break_frame ; Set it and forget it LOADI32_EAX &process_while_string_1 ; Using ":WHILE_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOADI32_EAX &process_while_string_2 ; Using "ERROR in process_while\nMISSING (\n" LOADI32_EBX &open_paren ; Using "(" CALL32 %require_match ; Make sure we have it CALL32 %expression ; Deal with the WHILE (...) part LOADI32_EAX &process_while_string_3 ; Using "TEST\nJUMP_EQ %END_WHILE_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_while_string_4 ; Using "# THEN_while_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_while_string_5 ; Using "ERROR in process_while\nMISSING )\n" LOADI32_EBX &close_paren ; Using ")" CALL32 %require_match ; Make sure we have it CALL32 %statement ; Deal with the {....} part LOADI32_EAX &process_while_string_6 ; Using "JUMP %WHILE_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_while_string_7 ; Using ":END_WHILE_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) CALL32 %restore_break_frame ; Restore the old break frame POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :process_while_string_0 "END_WHILE_" :process_while_string_1 ":WHILE_" :process_while_string_2 "ERROR in process_while MISSING ( " :process_while_string_3 "TEST JUMP_EQ %END_WHILE_" :process_while_string_4 "# THEN_while_" :process_while_string_5 "ERROR in process_while MISSING ) " :process_while_string_6 "JUMP %WHILE_" :process_while_string_7 ":END_WHILE_" ;; process_for function ;; Receives Nothing ;; Returns Nothing ;; Increments current_count and leverages save/restore_break_frame pieces ;; Uses ECX for char* NUMBER_STRING :process_for PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX CALL32 %save_break_frame ; Save the frame LOAD32_Absolute32_eax ¤t_count ; Using current count COPY_EAX_to_EBX ; Preparing for update ADDI8_EBX !1 ; current_count + 1 STORE32_Absolute32_ebx ¤t_count ; current_count = current_count + 1 CALL32 %numerate_number ; convert to string COPY_EAX_to_ECX ; put NUMBER_STRING in place LOADI32_EAX &process_for_string_0 ; Using "FOR_END_" COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %set_break_frame ; Set it and forget it LOADI32_EAX &process_for_string_1 ; Using "# FOR_initialization_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOADI32_EAX &process_for_string_2 ; Using "ERROR in process_for\nMISSING (\n" LOADI32_EBX &open_paren ; Using "(" CALL32 %require_match ; Make Sure we have it LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &semicolon ; Using ";" CALL32 %match ; IF global_token->S == ";" CMPI8_EAX !0 ; Then no initializer JE32 %process_for_terminator ; And skip getting the expression ;; Deal with FOR (...; case CALL32 %expression ; Get the FOR ( ... ; part :process_for_terminator LOADI32_EAX &process_for_string_3 ; Using ":FOR_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_for_string_4 ; Using "ERROR in process_for\nMISSING ;1\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Make sure we have it CALL32 %expression ; Get the FOR ( ; ... ; Part LOADI32_EAX &process_for_string_5 ; Using "TEST\nJUMP_EQ %FOR_END_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_for_string_6 ; Using "JUMP %FOR_THEN_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_for_string_7 ; Using ":FOR_ITER_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_for_string_8 ; Using "ERROR in process_for\nMISSING ;2\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Make sure we have it CALL32 %expression ; Get the FOR (;;...) part LOADI32_EAX &process_for_string_9 ; Using "JUMP %FOR_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_for_string_10 ; Using ":FOR_THEN_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_for_string_11 ; Using "ERROR in process_for\nMISSING )\n" LOADI32_EBX &close_paren ; Using ")" CALL32 %require_match ; Make sure we have it CALL32 %statement ; Get FOR (;;) {...} part LOADI32_EAX &process_for_string_12 ; Using "JUMP %FOR_ITER_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Passing NUMBER_STRING CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) LOADI32_EAX &process_for_string_13 ; Using ":FOR_END_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S CALL32 %uniqueID_out ; uniqueID_out(function->s, number_string) CALL32 %restore_break_frame ; Restore the old break frame POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :process_for_string_0 "FOR_END_" :process_for_string_1 "# FOR_initialization_" :process_for_string_2 "ERROR in process_for MISSING ( " :process_for_string_3 ":FOR_" :process_for_string_4 "ERROR in process_for MISSING ;1 " :process_for_string_5 "TEST JUMP_EQ %FOR_END_" :process_for_string_6 "JUMP %FOR_THEN_" :process_for_string_7 ":FOR_ITER_" :process_for_string_8 "ERROR in process_for MISSING ;2 " :process_for_string_9 "JUMP %FOR_" :process_for_string_10 ":FOR_THEN_" :process_for_string_11 "ERROR in process_for MISSING ) " :process_for_string_12 "JUMP %FOR_ITER_" :process_for_string_13 ":FOR_END_" ;; process_break function ;; Receives nothing ;; Returns nothing ;; Handles the break out of loops case ;; Uses EBX for struct token_list* break_frame and ECX for struct token_list* I :process_break PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOAD32_Absolute32_eax &break_target_head ; Catch big error CMPI8_EAX !0 ; IF(NULL == break_target_head) JE32 %process_break_bad ; I'm sorry Mr White but you have stage-3 lung cancer LOAD32_Absolute32_eax &function ; Using function LOAD32_ECX_from_EAX_Immediate8 !4 ; I = function->LOCALS LOAD32_Absolute32_ebx &break_frame ; Put break_frame in the right spot LOADI32_EAX &process_break_string_1 ; Using "POP_ebx\t# break_cleanup_locals\n" :process_break_iter CMPI8_ECX !0 ; IF (NULL == I) JE32 %process_break_cleaned ; We are done CMP_EBX_ECX ; IF I != break_frame JE32 %process_break_cleaned ; We are done CALL32 %emit_out ; Emit it LOAD32_ECX_from_ECX ; I = I->NEXT JMP32 %process_break_iter ; Keep looping :process_break_cleaned LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOADI32_EAX &process_break_string_2 ; Using "JUMP %" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &break_target_head ; Get what we are in CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &break_target_func ; Get what function we are in CALL32 %emit_out ; Emit it LOADI32_EAX &underline ; Using "_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &break_target_num ; Get dem digits CALL32 %emit_out ; Emit it LOADI32_EAX &process_break_string_3 ; Using "\n" CALL32 %emit_out ; Emit it LOADI32_EAX &process_break_string_4 ; Using "ERROR in break statement\nMissing ;\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Make sure we have it POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :process_break_bad ;; Breaking badly LOADI32_EAX %2 ; Using standard error STORE32_Absolute32_eax &Output_file ; write to standard error ; CALL32 %line_error ; Write useful debug info COPY_ECX_to_EAX ; put S in the right place CALL32 %File_Print ; print it LOADI32_EAX &process_break_string_0 ; Ending string CALL32 %File_Print ; print it JMP32 %Exit_Failure ; Abort Hard :process_break_string_0 "Not inside of a loop or case statement" :process_break_string_1 "POP_ebx # break_cleanup_locals " :process_break_string_2 "JUMP %" :process_break_string_3 " " :process_break_string_4 "ERROR in break statement Missing ; " ;; expression function ;; Receives Nothing ;; Returns Nothing ;; Walks global_token and updates output_list ;; Uses EAX and EBX for match and ECX for char* store :expression PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX CALL32 %bitwise_expr ; Collect bitwise expressions LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &equal ; "=" CALL32 %match ; IF global_token->S == "=" CMPI8_EAX !0 ; We have to deal with assignment JNE32 %expression_done ; Looks like nope ;; Deal with possible assignment LOADI32_ECX &expression_string_1 ; Assume "STORE_CHAR\n" by default LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !4 ; global_token->PREV LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->PREV->S LOADI32_EAX &close_bracket ; Using "]" CALL32 %match ; IF global_token->S == "]" CMPI8_EAX !0 ; Then we might have a char JNE32 %expression_int ; Otherwise INT LOAD32_Absolute32_ebx ¤t_target ; Using current_target LOAD32_EBX_from_EBX_Immediate8 !24 ; current_target->NAME LOADI32_EAX &type_char_indirect_name ; Using "char*" CALL32 %match ; Intensional inefficency because I feel like it CMPI8_EAX !0 ; IF current_target->NAME == "char*" JNE32 %expression_int ; Do char anyway JMP32 %expression_common ; Looks like we have to use "STORE_CHAR\n" :expression_int LOADI32_ECX &expression_string_0 ; Use "STORE_INTEGER\n" :expression_common LOADI32_EAX &expression ; Passing expression CALL32 %common_recursion ; Recurse COPY_ECX_to_EAX ; Using Store CALL32 %emit_out ; Emit it LOADI32_EAX %0 ; Using NULL STORE32_Absolute32_eax ¤t_target ; current_target = NULL :expression_done POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :expression_string_0 "STORE_INTEGER " :expression_string_1 "STORE_CHAR " ;; bitwise_expr function ;; Receives nothing ;; Returns nothing ;; Walks global_token list and updates output list ;; Just calls other functions :bitwise_expr CALL32 %relational_expr ; Walk up the tree CALL32 %bitwise_expr_stub ; Let general recursion do the work RETURN ;; bitwise_expr_stub function ;; Receives nothing ;; Returns Nothing ;; Just calls general_recursion a bunch ;; Uses EAX, EBX, ECX and EDX for passing constants to general recursion :bitwise_expr_stub PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX LOADI32_EAX &relational_expr ; Using relational_expr LOADI32_EBX &bitwise_expr_stub_string_0 ; Using "AND_eax_ebx\n" LOADI32_ECX &bitwise_and ; Using "&" LOADI32_EDX &bitwise_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &relational_expr ; Using relational_expr LOADI32_EBX &bitwise_expr_stub_string_0 ; Using "AND_eax_ebx\n" LOADI32_ECX &logical_and ; Using "&&" LOADI32_EDX &bitwise_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &relational_expr ; Using relational_expr LOADI32_EBX &bitwise_expr_stub_string_1 ; Using "OR_eax_ebx\n" LOADI32_ECX &bitwise_or ; Using "|" LOADI32_EDX &bitwise_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &relational_expr ; Using relational_expr LOADI32_EBX &bitwise_expr_stub_string_1 ; Using "OR_eax_ebx\n" LOADI32_ECX &logical_or ; Using "||" LOADI32_EDX &bitwise_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &relational_expr ; Using relational_expr LOADI32_EBX &bitwise_expr_stub_string_2 ; Using "XOR_ebx_eax_into_eax\n" LOADI32_ECX &bitwise_xor ; Using "^" LOADI32_EDX &bitwise_expr_stub ; And recurse CALL32 %general_recursion ; Hit it POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :bitwise_expr_stub_string_0 "AND_eax_ebx " :bitwise_expr_stub_string_1 "OR_eax_ebx " :bitwise_expr_stub_string_2 "XOR_ebx_eax_into_eax " ;; relational_expr function ;; Receives nothing ;; Returns Nothing ;; Walks global_token list and updates output list ;; just calls other function :relational_expr CALL32 %additive_expr ; Walk up the tree CALL32 %relational_expr_stub ; Recurse RETURN ;; relational_expr_stub function ;; Receives nothing ;; Returns Nothing ;; Just calls general_recursion a bunch ;; Uses EAX, EBX, ECX and EDX for passing constants to general recursion :relational_expr_stub PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX LOADI32_EAX &additive_expr ; Using additive_expr LOADI32_EBX &relational_expr_stub_string_0 ; Using "CMP\nSETL\nMOVEZBL\n" LOADI32_ECX &less_than_string ; Using "<" LOADI32_EDX &relational_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &additive_expr ; Using additive_expr LOADI32_EBX &relational_expr_stub_string_1 ; Using "CMP\nSETLE\nMOVEZBL\n" LOADI32_ECX &less_than_equal_string ; Using "<=" LOADI32_EDX &relational_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &additive_expr ; Using additive_expr LOADI32_EBX &relational_expr_stub_string_2 ; Using "CMP\nSETGE\nMOVEZBL\n" LOADI32_ECX &greater_than_equal_string ; Using ">=" LOADI32_EDX &relational_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &additive_expr ; Using additive_expr LOADI32_EBX &relational_expr_stub_string_3 ; Using "CMP\nSETG\nMOVEZBL\n" LOADI32_ECX &greater_than_string ; Using ">" LOADI32_EDX &relational_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &additive_expr ; Using additive_expr LOADI32_EBX &relational_expr_stub_string_4 ; Using "CMP\nSETE\nMOVEZBL\n" LOADI32_ECX &equal_to_string ; Using "==" LOADI32_EDX &relational_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &additive_expr ; Using additive_expr LOADI32_EBX &relational_expr_stub_string_5 ; Using "CMP\nSETNE\nMOVEZBL\n" LOADI32_ECX ¬_equal_string ; Using "!=" LOADI32_EDX &relational_expr_stub ; And recurse CALL32 %general_recursion ; Hit it POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :relational_expr_stub_string_0 "CMP SETL MOVEZBL " :relational_expr_stub_string_1 "CMP SETLE MOVEZBL " :relational_expr_stub_string_2 "CMP SETGE MOVEZBL " :relational_expr_stub_string_3 "CMP SETG MOVEZBL " :relational_expr_stub_string_4 "CMP SETE MOVEZBL " :relational_expr_stub_string_5 "CMP SETNE MOVEZBL " ;; additive_expr function ;; Receives nothing ;; Returns Nothing ;; Walks global_token list and updates output list ;; just calls other function :additive_expr CALL32 %postfix_expr ; Walk up the tree CALL32 %additive_expr_stub ; Recurse RETURN ;; additive_expr_stub function ;; Receives nothing ;; Returns Nothing ;; Just calls general_recursion a bunch ;; Uses EAX, EBX, ECX and EDX for passing constants to general recursion :additive_expr_stub PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX LOADI32_EAX &postfix_expr ; Using postfix_expr LOADI32_EBX &additive_expr_stub_string_0 ; Using "ADD_ebx_to_eax\n" LOADI32_ECX &plus_string ; Using "+" LOADI32_EDX &additive_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &postfix_expr ; Using postfix_expr LOADI32_EBX &additive_expr_stub_string_1 ; Using "SUBTRACT_eax_from_ebx_into_ebx\nMOVE_ebx_to_eax\n" LOADI32_ECX &minus_string ; Using "-" LOADI32_EDX &additive_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &postfix_expr ; Using postfix_expr LOADI32_EBX &additive_expr_stub_string_2 ; Using "MULTIPLY_eax_by_ebx_into_eax\n" LOADI32_ECX &multiply_string ; Using "*" LOADI32_EDX &additive_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &postfix_expr ; Using postfix_expr LOADI32_EBX &additive_expr_stub_string_3 ; Using "XCHG_eax_ebx\nLOAD_IMMEDIATE_edx %0\nDIVIDE_eax_by_ebx_into_eax\n" LOADI32_ECX ÷_string ; Using "/" LOADI32_EDX &additive_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &postfix_expr ; Using postfix_expr LOADI32_EBX &additive_expr_stub_string_4 ; Using "XCHG_eax_ebx\nLOAD_IMMEDIATE_edx %0\nMODULUS_eax_from_ebx_into_ebx\nMOVE_edx_to_eax\n" LOADI32_ECX &modulus_string ; Using "%" LOADI32_EDX &additive_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &postfix_expr ; Using postfix_expr LOADI32_EBX &additive_expr_stub_string_5 ; Using "COPY_eax_to_ecx\nCOPY_ebx_to_eax\nSAL_eax_cl\n" LOADI32_ECX &left_shift_string ; Using "<<" LOADI32_EDX &additive_expr_stub ; And recurse CALL32 %general_recursion ; Hit it LOADI32_EAX &postfix_expr ; Using postfix_expr LOADI32_EBX &additive_expr_stub_string_6 ; Using "COPY_eax_to_ecx\nCOPY_ebx_to_eax\nSAR_eax_cl\n" LOADI32_ECX &right_shift_string ; Using ">>" LOADI32_EDX &additive_expr_stub ; And recurse CALL32 %general_recursion ; Hit it POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :additive_expr_stub_string_0 "ADD_ebx_to_eax " :additive_expr_stub_string_1 "SUBTRACT_eax_from_ebx_into_ebx MOVE_ebx_to_eax " :additive_expr_stub_string_2 "MULTIPLY_eax_by_ebx_into_eax " :additive_expr_stub_string_3 "XCHG_eax_ebx LOAD_IMMEDIATE_edx %0 DIVIDE_eax_by_ebx_into_eax " :additive_expr_stub_string_4 "XCHG_eax_ebx LOAD_IMMEDIATE_edx %0 MODULUS_eax_from_ebx_into_ebx MOVE_edx_to_eax " :additive_expr_stub_string_5 "COPY_eax_to_ecx COPY_ebx_to_eax SAL_eax_cl " :additive_expr_stub_string_6 "COPY_eax_to_ecx COPY_ebx_to_eax SAR_eax_cl " ;; postfix_expr function ;; Receives nothing ;; Returns Nothing ;; Walks global_token list and updates output list ;; just calls other function :postfix_expr CALL32 %primary_expr ; Walk up the tree CALL32 %postfix_expr_stub ; Recurse RETURN ;; postfix_expr_stub function ;; Receives nothing ;; Returns Nothing ;; Checks for "[" and "->" and deals with them otherwise does nothing ;; Uses EAX, EBX, ECX and EDX for passing constants to general recursion :postfix_expr_stub PUSH_EBX ; Protect EBX LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &open_bracket ; Using "[" CALL32 %match ; IF global_token->S == "[" CMPI8_EAX !0 ; then we have an array JNE32 %postfix_expr_stub_arrow ; Otherwise try arrow ;; Deal with array CALL32 %postfix_expr_array ; Get it CALL32 %postfix_expr_stub ; Recurse :postfix_expr_stub_arrow LOADI32_EAX &arrow_string ; Using "->" CALL32 %match ; IF global_token->S == "->" CMPI8_EAX !0 ; Then we need to deal with struct offsets JNE32 %postfix_expr_stub_done ; Otherwise be done ;; Deal with arrow CALL32 %postfix_expr_arrow ; Get it CALL32 %postfix_expr_stub ; Recurse :postfix_expr_stub_done POP_EBX ; Restore EBX RETURN ;; unary_expr_sizeof function ;; Receives nothing ;; Returns nothing ;; Uses ECX for A->SIZE :unary_expr_sizeof PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOADI32_EAX &unary_expr_sizeof_string_0 ; Using "ERROR in unary_expr\nMissing (\n" LOADI32_EBX &open_paren ; Using "(" CALL32 %require_match ; Make sure we have it CALL32 %type_name ; Get the type LOAD32_ECX_from_EAX_Immediate8 !4 ; Set A->TYPE LOADI32_EAX &unary_expr_sizeof_string_1 ; Using "ERROR in unary_expr\nMissing )\n" LOADI32_EBX &close_paren ; Using ")" CALL32 %require_match ; Make sure we have it LOADI32_EAX &unary_expr_sizeof_string_2 ; Using "LOAD_IMMEDIATE_eax %" CALL32 %emit_out ; Emit it COPY_ECX_to_EAX ; Put A->SIZE in the right place CALL32 %numerate_number ; Turn into string CALL32 %emit_out ; Emit it LOADI32_EAX &unary_expr_sizeof_string_3 ; Using "\n" CALL32 %emit_out ; Emit it POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :unary_expr_sizeof_string_0 "ERROR in unary_expr Missing ( " :unary_expr_sizeof_string_1 "ERROR in unary_expr Missing ) " :unary_expr_sizeof_string_2 "LOAD_IMMEDIATE_eax %" :unary_expr_sizeof_string_3 " " ;; postfix_expr_array function ;; Receives Nothing ;; Returns Nothing ;; Uses EBX for struct type* ARRAY and ECX for char* ASSIGN :postfix_expr_array PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOAD32_Absolute32_eax ¤t_target ; ARRAY = current_target PUSH_EAX ; Protect it LOADI32_EAX &expression ; Using expression CALL32 %common_recursion ; Recurse POP_EBX ; Restore array STORE32_Absolute32_ebx ¤t_target ; current_target = ARRAY LOADI32_ECX &postfix_expr_array_string_0 ; ASSIGN = "LOAD_INTEGER\n" LOADI32_EAX &type_char_indirect_name ; Using "char*" LOAD32_EBX_from_EBX_Immediate8 !24 ; current_target->NAME CALL32 %match ; IF current_target->NAME == "char*" CMPI8_EAX !0 ; load a byte JNE32 %postfix_expr_array_large ; Otherwise adjust ;; Deal with loading byte LOADI32_ECX &postfix_expr_array_string_1 ; ASSIGN = "LOAD_BYTE\n" JMP32 %postfix_expr_array_common ; Do the next bit :postfix_expr_array_large ;; deal with arrays made of things other than chars LOADI32_EAX &postfix_expr_array_string_2 ; Using "SAL_eax_Immediate8 !" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax ¤t_target ; Using current_target LOAD32_EAX_from_EAX_Immediate8 !12 ; current_target->INDIRECT LOAD32_EAX_from_EAX_Immediate8 !4 ; current_target->INDIRECT->SIZE CALL32 %ceil_log2 ; ceil_log2(current_target->indirect->size) CALL32 %numerate_number ; numerate_number(ceil_log2(current_target->indirect->size)) CALL32 %emit_out ; Emit it LOADI32_EAX &postfix_expr_array_string_3 ; Using "\n" CALL32 %emit_out ; Emit it :postfix_expr_array_common LOADI32_EAX &postfix_expr_array_string_4 ; Using "ADD_ebx_to_eax\n" CALL32 %emit_out ; Emit it LOADI32_EAX &postfix_expr_array_string_5 ; Using "ERROR in postfix_expr\nMissing ]\n" LOADI32_EBX &close_bracket ; Using "]" CALL32 %require_match ; Make sure we have it LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &equal ; Using "=" CALL32 %match ; IF global_token->S == "=" CMPI8_EAX !0 ; We need to preserve address JNE32 %postfix_expr_array_done ; Otherwise be done ;; Clearing out assign LOADI32_ECX &postfix_expr_array_string_6 ; ASSIGN = "" :postfix_expr_array_done COPY_ECX_to_EAX ; Using ASSIGN CALL32 %emit_out ; Emit it POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :postfix_expr_array_string_0 "LOAD_INTEGER " :postfix_expr_array_string_1 "LOAD_BYTE " :postfix_expr_array_string_2 "SAL_eax_Immediate8 !" :postfix_expr_array_string_3 " " :postfix_expr_array_string_4 "ADD_ebx_to_eax " :postfix_expr_array_string_5 "ERROR in postfix_expr Missing ] " :postfix_expr_array_string_6 "" ;; ceil_log2 function ;; Receives int a in EAX ;; Performs log2 on A and ;; Returns result in EAX ;; Uses EBX for INT A and ECX for INT RESULT :ceil_log2 PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOADI32_ECX %0 ; RESULT = 0 COPY_EAX_to_EBX ; put A in right place SUBI8_EAX !1 ; (A - 1) AND_EAX_EBX ; A & (A - 1) CMPI8_EAX !0 ; IF 0 == (A & (A - 1)) JNE32 %ceil_log2_iter ; Starting from -1 LOADI32_ECX %-1 ; RESULT = -1 :ceil_log2_iter CMPI8_EBX !0 ; IF A > 0 JLE32 %ceil_log2_done ; Otherwise be done ADDI8_ECX !1 ; RESULT = RESULT + 1 SHRI8_EBX !1 ; A = A >> 1 JMP32 %ceil_log2_iter ; Keep looping :ceil_log2_done COPY_ECX_to_EAX ; Return RESULT POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; postfix_expr_arrow function ;; Receives nothing ;; Returns nothing ;; Emits a bunch and updates current_target ;; Uses EBX for struct type* I :postfix_expr_arrow PUSH_EBX ; Protect EBX LOADI32_EAX &postfix_expr_arrow_string_0 ; Using "# looking up offset\n" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOAD32_EBX_from_EAX_Immediate8 !8 ; Using global_token->S LOAD32_Absolute32_eax ¤t_target ; Using current_target CALL32 %lookup_member ; lookup_member(current_target, global_token->s) COPY_EAX_to_EBX ; struct type* I = lookup_member(current_target, global_token->s) LOAD32_EAX_from_EAX_Immediate8 !20 ; I->TYPE STORE32_Absolute32_eax ¤t_target ; current_target = I->TYPE LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOAD32_EAX_from_EBX_Immediate8 !8 ; I->OFFSET CMPI8_EAX !0 ; IF 0 != I->OFFSET JE32 %postfix_expr_arrow_first ; Then we don't need to do an offset ;; Deal with needing an offset LOADI32_EAX &postfix_expr_arrow_string_1 ; Using "# -> offset calculation\nLOAD_IMMEDIATE_ebx %" CALL32 %emit_out ; Emit it LOAD32_EAX_from_EBX_Immediate8 !8 ; I->OFFSET CALL32 %numerate_number ; Convert to string CALL32 %emit_out ; Emit it LOADI32_EAX &postfix_expr_arrow_string_2 ; Using "\nADD_ebx_to_eax\n" CALL32 %emit_out ; Emit it :postfix_expr_arrow_first LOAD32_EAX_from_EBX_Immediate8 !4 ; I->SIZE CMPI8_EAX !4 ; IF I->SIZE >= 4 JL32 %postfix_expr_arrow_done ; Otherwise be done ;; Last chance for load LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EBX_from_EAX_Immediate8 !8 ; global_token->S LOADI32_EAX &equal ; Using "=" CALL32 %match ; IF global_token->S == "=" CMPI8_EAX !0 ; Then we have assignment and should not load JE32 %postfix_expr_arrow_done ; Be done ;; Deal with load case LOADI32_EAX &postfix_expr_arrow_string_3 ; Using "LOAD_INTEGER\n" CALL32 %emit_out ; Emit it :postfix_expr_arrow_done POP_EBX ; Restore EBX RETURN :postfix_expr_arrow_string_0 "# looking up offset " :postfix_expr_arrow_string_1 "# -> offset calculation LOAD_IMMEDIATE_ebx %" :postfix_expr_arrow_string_2 " ADD_ebx_to_eax " :postfix_expr_arrow_string_3 "LOAD_INTEGER " ;; primary_expr function ;; Receives nothing ;; Returns nothing :primary_expr PUSH_EBX ; Protect EBX LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EBX_from_EAX_Immediate8 !8 ; global_token->S LOADI32_EAX &sizeof_string ; Using "sizeof" CALL32 %match ; See if match CMPI8_EAX !0 ; IF match JNE32 %primary_expr_neg ; Otherwise try negatives ;; Deal with sizeof CALL32 %unary_expr_sizeof ; Lets do this JMP32 %primary_expr_done ; Be done :primary_expr_neg LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; Make it useful CMPI8_EAX !45 ; IF global_token->S[0] == "-" JNE32 %primary_expr_not ; Otherwise try logical NOT ;; Deal with negative numbers LOADI32_EAX &primary_expr_string_0 ; Using "LOAD_IMMEDIATE_eax %0\n" CALL32 %emit_out ; Emit it LOADI32_EAX &postfix_expr ; Passing postfix_expr CALL32 %common_recursion ; Get what it is notting LOADI32_EAX &primary_expr_string_1 ; Using "SUBTRACT_eax_from_ebx_into_ebx\nMOVE_ebx_to_eax\n" CALL32 %emit_out ; Emit it JMP32 %primary_expr_done ; Be done :primary_expr_not LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; Make it useful CMPI8_EAX !33 ; IF global_token->S[0] == "!" JNE32 %primary_expr_bin ; Otherwise try '~' ;; Deal with logical not LOADI32_EAX &primary_expr_string_2 ; Using "LOAD_IMMEDIATE_eax %1\n" CALL32 %emit_out ; Emit it LOADI32_EAX &postfix_expr ; Passing postfix_expr CALL32 %common_recursion ; Get what it is notting LOADI32_EAX &primary_expr_string_3 ; Using "XOR_ebx_eax_into_eax\n" CALL32 %emit_out ; Emit it JMP32 %primary_expr_done ; Be done :primary_expr_bin LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; Make it useful CMPI8_EAX !126 ; IF global_token->S[0] == "~" JNE32 %primary_expr_paren ; Otherwise try paren ;; Deal with binary NOT LOADI32_EAX &postfix_expr ; Passing postfix_expr CALL32 %common_recursion ; Get what it is notting LOADI32_EAX &primary_expr_string_4 ; Using "NOT_eax\n" CALL32 %emit_out ; Emit it JMP32 %primary_expr_done ; Be done :primary_expr_paren LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; Make it useful CMPI8_EAX !40 ; IF global_token->S[0] == "(" JNE32 %primary_expr_ch ; Otherwise try char ;; deal with nesting LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT CALL32 %expression ; Lets recurse LOADI32_EAX &primary_expr_string_5 ; Using "Error in Primary expression\nDidn't get )\n" LOADI32_EBX &close_paren ; Using ")" CALL32 %require_match ; Make sure we have it JMP32 %primary_expr_done ; Be done :primary_expr_ch LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; Make it useful CMPI8_EAX !39 ; Using "'" JNE32 %primary_expr_str ; Otherwise try string ;; Deal with chars CALL32 %primary_expr_char ; Handle that char JMP32 %primary_expr_done ; Be done :primary_expr_str LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; Make it useful CMPI8_EAX !34 ; Using '\"' JNE32 %primary_expr_var ; Otherwise try a variable ;; Deal with strings CALL32 %primary_expr_string ; Handle that string JMP32 %primary_expr_done ; Be done :primary_expr_var LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; Make it useful LOADI32_EBX &primary_expr_string_6 ; Using "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_" CALL32 %In_Set ; See if we have a match CMPI8_EAX !1 ; IF match JNE32 %primary_expr_num ; otherwise try number ;; Deal with variables CALL32 %primary_expr_variable ; Deal with variable JMP32 %primary_expr_done ; Be done :primary_expr_num LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; Make it useful LOADI32_EBX &primary_expr_string_7 ; Using "0123456789" CALL32 %In_Set ; See if we have a match CMPI8_EAX !1 ; IF match JNE32 %primary_expr_fail ; otherwise we failed hard ;; Deal with numbers CALL32 %primary_expr_number ; Collect the number JMP32 %primary_expr_done ; Be done :primary_expr_fail ;; looks like we hit bad input ;; abort before it gets bad CALL32 %primary_expr_failure ; No match means failure :primary_expr_done POP_EBX ; Restore EBX RETURN :primary_expr_string_0 "LOAD_IMMEDIATE_eax %0 " :primary_expr_string_1 "SUBTRACT_eax_from_ebx_into_ebx MOVE_ebx_to_eax " :primary_expr_string_2 "LOAD_IMMEDIATE_eax %1 " :primary_expr_string_3 "XOR_ebx_eax_into_eax " :primary_expr_string_4 "NOT_eax " :primary_expr_string_5 "Error in Primary expression Didn't get ) " :primary_expr_string_6 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_" :primary_expr_string_7 "0123456789" ;; primary_expr_variable function ;; Receives nothing ;; Returns nothing ;; Walks global and updates output ;; Uses EAX for struct token_list* a and ECX for char* S :primary_expr_variable PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_ECX_from_EAX_Immediate8 !8 ; S = global_token->S LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT COPY_ECX_to_EAX ; Using S LOAD32_Absolute32_ebx &global_constant_list ; Using global_constant_list CALL32 %sym_lookup ; sym_lookup(s, global_constant_list) CMPI8_EAX !0 ; IF NULL == sym_lookup(s, global_constant_list) JE32 %primary_expr_variable_local ; Try locals next ;; Deal with constant load LOAD32_EBX_from_EAX_Immediate8 !16 ; a->ARGS LOADI32_EAX &primary_expr_variable_string_2 ; Using "LOAD_IMMEDIATE_eax %" CALL32 %emit_out ; Emit it LOAD32_EAX_from_EBX_Immediate8 !8 ; a->ARGS->S CALL32 %emit_out ; Emit it LOADI32_EAX &primary_expr_variable_string_1 ; Using "\n" CALL32 %emit_out ; Emit it JMP32 %primary_expr_variable_done ; Be done :primary_expr_variable_local COPY_ECX_to_EAX ; Using S LOAD32_Absolute32_ebx &function ; Using function LOAD32_EBX_from_EBX_Immediate8 !4 ; function->locals CALL32 %sym_lookup ; sym_lookup(s, function->locals) CMPI8_EAX !0 ; IF NULL == sym_lookup(s, global_constant_list) JE32 %primary_expr_variable_arguments ; try arguments next ;; Deal with local load CALL32 %variable_load ; Collect it JMP32 %primary_expr_variable_done ; Be done :primary_expr_variable_arguments COPY_ECX_to_EAX ; Using S LOAD32_Absolute32_ebx &function ; Using function LOAD32_EBX_from_EBX_Immediate8 !16 ; function->args CALL32 %sym_lookup ; sym_lookup(s, function->args) CMPI8_EAX !0 ; IF NULL == sym_lookup(s, global_constant_list) JE32 %primary_expr_variable_function ; try functions next ;; Deal with argument load CALL32 %variable_load ; Collect it JMP32 %primary_expr_variable_done ; Be done :primary_expr_variable_function COPY_ECX_to_EAX ; Using S LOAD32_Absolute32_ebx &global_function_list ; Using global_function_list CALL32 %sym_lookup ; sym_lookup(s, global_function_list) CMPI8_EAX !0 ; IF NULL == sym_lookup(s, global_function_list) JE32 %primary_expr_variable_global ; try globals next ;; Deal with functions CALL32 %function_load ; Deal with the function JMP32 %primary_expr_variable_done ; Be done :primary_expr_variable_global COPY_ECX_to_EAX ; Using S LOAD32_Absolute32_ebx &global_symbol_list ; Using global_symbol_list CALL32 %sym_lookup ; sym_lookup(s, global_symbol_list) CMPI8_EAX !0 ; IF NULL == sym_lookup(s, global_symbol_list) JE32 %primary_expr_variable_error ; Give up ;; Deal with globals CALL32 %global_load ; Collect that global JMP32 %primary_expr_variable_done ; Be done :primary_expr_variable_error LOADI32_EAX %2 ; Using standard error STORE32_Absolute32_eax &Output_file ; write to standard error ; CALL32 %line_error ; Write useful debug info COPY_ECX_to_EAX ; put S in the right place CALL32 %File_Print ; print it LOADI32_EAX &primary_expr_variable_string_0 ; Ending string CALL32 %File_Print ; print it JMP32 %Exit_Failure ; Abort Hard :primary_expr_variable_done POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :primary_expr_variable_string_0 " is not a defined symbol " :primary_expr_variable_string_1 " " :primary_expr_variable_string_2 "LOAD_IMMEDIATE_eax %" ;; function_call function ;; Receives char* S in EAX and int BOOL in EBX ;; Builds stack frames before and tears them down after function calls ;; Uses ECX for char* S, EDX for int BOOL, ESI for PASSED :function_call PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX PUSH_ESI ; Protect ESI COPY_EAX_to_ECX ; Put S in place COPY_EBX_to_EDX ; Put BOOL in place LOADI32_ESI %0 ; PASSED = 0 LOADI32_EAX &function_call_string_0 ; Using "ERROR in process_expression_list\nNo ( was found\n" LOADI32_EBX &open_paren ; Using "(" CALL32 %require_match ; Make sure we have it LOADI32_EAX &function_call_string_1 ; Using "PUSH_edi\t# Prevent overwriting in recursion\n" CALL32 %emit_out ; Emit it LOADI32_EAX &function_call_string_2 ; Using "PUSH_ebp\t# Protect the old base pointer\n" CALL32 %emit_out ; Emit it LOADI32_EAX &function_call_string_3 ; Using "COPY_esp_to_edi\t# Copy new base pointer\n" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; Make it useful CMPI8_EAX !41 ; IF global_token->S[0] == ")" JE32 %function_call_gen_done ; Then no arguments to send ;; looks like we have arguments to collect CALL32 %expression ; Collect the argument LOADI32_EAX &function_call_string_4 ; Using "PUSH_eax\t#_process_expression1\n" CALL32 %emit_out ; Emit it LOADI32_ESI %1 ; PASSED = 1 :function_call_gen_iter LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; Make it useful CMPI8_EAX !44 ; IF global_token->S[0] == "," JNE32 %function_call_gen_done ; Otherwise we are done LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT CALL32 %expression ; Collect the argument LOADI32_EAX &function_call_string_5 ; Using "PUSH_eax\t#_process_expression2\n" CALL32 %emit_out ; Emit it ADDI8_ESI !1 ; PASSED = PASSED + 1 JMP32 %function_call_gen_iter ; Keep trying :function_call_gen_done ;; All is collected LOADI32_EAX &function_call_string_6 ; Using "ERROR in process_expression_list\nNo ) was found\n" LOADI32_EBX &close_paren ; Using ")" CALL32 %require_match ; Make sure we have it CMPI8_EDX !0 ; IF(BOOL == TRUE) JNE32 %function_call_static ; Otherwise it is a static call ;; Deal with a passed function pointer LOADI32_EAX &function_call_string_7 ; Using "LOAD_BASE_ADDRESS_eax %" CALL32 %emit_out ; Emit it COPY_ECX_to_EAX ; Using S CALL32 %emit_out ; Emit it LOADI32_EAX &function_call_string_8 ; Using "\nLOAD_INTEGER\n" CALL32 %emit_out ; Emit it LOADI32_EAX &function_call_string_9 ; Using "COPY_edi_to_ebp\n" CALL32 %emit_out ; Emit it LOADI32_EAX &function_call_string_10 ; Using "CALL_eax\n" CALL32 %emit_out ; Emit it LOADI32_EAX &function_call_string_13 ; Using "POP_ebx\t# _process_expression_locals\n" JMP32 %function_call_cleanup ; Clean up :function_call_static ;; Deal with fixed function name LOADI32_EAX &function_call_string_9 ; Using "COPY_edi_to_ebp\n" CALL32 %emit_out ; Emit it LOADI32_EAX &function_call_string_11 ; Using "CALL_IMMEDIATE %FUNCTION_" CALL32 %emit_out ; Emit it COPY_ECX_to_EAX ; Using S CALL32 %emit_out ; Emit it LOADI32_EAX &function_call_string_12 ; Using "\n" CALL32 %emit_out ; Emit it LOADI32_EAX &function_call_string_13 ; Using "POP_ebx\t# _process_expression_locals\n" :function_call_cleanup CMPI8_ESI !0 ; IF PASSED > 0 JLE32 %function_call_done ; Otherwise be done ;; The desired string is already in EAX CALL32 %emit_out ; Emit it SUBI8_ESI !1 ; PASSED = PASSED - 1 JMP32 %function_call_cleanup ; Keep going :function_call_done LOADI32_EAX &function_call_string_14 ; Using "POP_ebp\t# Restore old base pointer\n" CALL32 %emit_out ; Emit it LOADI32_EAX &function_call_string_15 ; Using "POP_edi\t# Prevent overwrite\n" CALL32 %emit_out ; Emit it POP_ESI ; Restore ESI POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :function_call_string_0 "ERROR in process_expression_list No ( was found " :function_call_string_1 "PUSH_edi # Prevent overwriting in recursion " :function_call_string_2 "PUSH_ebp # Protect the old base pointer " :function_call_string_3 "COPY_esp_to_edi # Copy new base pointer " :function_call_string_4 "PUSH_eax #_process_expression1 " :function_call_string_5 "PUSH_eax #_process_expression2 " :function_call_string_6 "ERROR in process_expression_list No ) was found " :function_call_string_7 "LOAD_BASE_ADDRESS_eax %" :function_call_string_8 " LOAD_INTEGER " :function_call_string_9 "COPY_edi_to_ebp " :function_call_string_10 "CALL_eax " :function_call_string_11 "CALL_IMMEDIATE %FUNCTION_" :function_call_string_12 " " :function_call_string_13 "POP_ebx # _process_expression_locals " :function_call_string_14 "POP_ebp # Restore old base pointer " :function_call_string_15 "POP_edi # Prevent overwrite " ;; variable_load function ;; Receives struct token_list* A in EAX ;; Returns nothing ;; Updates output and current_target ;; Uses ECX for A :variable_load PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX COPY_EAX_to_ECX ; Protect A LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &open_paren ; Using "(" CALL32 %match ; IF global_token->S == "(" CMPI8_EAX !0 ; Then it might be a function JNE32 %variable_load_regular ; Otherwise it is regular LOAD32_EBX_from_ECX_Immediate8 !12 ; A->TYPE LOAD32_EBX_from_EBX_Immediate8 !24 ; A->TYPE->NAME LOADI32_EAX &type_function_name ; Using "FUNCTION" CALL32 %match ; IF A->TYPE->NAME == "FUNCTION" CMPI8_EAX !0 ; Then it must be a function JNE32 %variable_load_regular ; otherwise just another regular ;; deal with function LOAD32_EAX_from_ECX_Immediate8 !16 ; A->DEPTH CALL32 %numerate_number ; Convert to string LOADI32_EBX %0 ; pass 0 for true CALL32 %function_call ; Create the function call JMP32 %variable_load_done ; Be done :variable_load_regular LOAD32_EAX_from_ECX_Immediate8 !12 ; A->TYPE STORE32_Absolute32_eax ¤t_target ; current_target = A->TYPE LOADI32_EAX &variable_load_string_0 ; Using "LOAD_BASE_ADDRESS_eax %" CALL32 %emit_out ; Emit it LOAD32_EAX_from_ECX_Immediate8 !16 ; A->DEPTH CALL32 %numerate_number ; Convert to string CALL32 %emit_out ; Emit it LOADI32_EAX &variable_load_string_1 ; Using "\n" CALL32 %emit_out ; Emit it ;; Check for special case of assignment LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &equal ; Using "=" CALL32 %match ; IF global_token->S == "=" CMPI8_EAX !0 ; Then we skip loading JE32 %variable_load_done ; And be done ;; Deal with common case LOADI32_EAX &variable_load_string_2 ; Using "LOAD_INTEGER\n" CALL32 %emit_out ; Emit it :variable_load_done POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :variable_load_string_0 "LOAD_BASE_ADDRESS_eax %" :variable_load_string_1 " " :variable_load_string_2 "LOAD_INTEGER " ;; function_load function ;; Receives struct token_list* a in EAX ;; Returns nothing ;; Uses ECX to hold A->S :function_load PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOAD32_EAX_from_EAX_Immediate8 !8 ; A->S COPY_EAX_to_ECX ; Protect A->S LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &open_paren ; Using "(" CALL32 %match ; IF global_token->S == "(" CMPI8_EAX !0 ; The we need to do a function call JNE32 %function_load_regular ; Otherwise just load it's address ;; Deal with function call COPY_ECX_to_EAX ; Using A->S LOADI32_EBX %1 ; Using FALSE CALL32 %function_call ; Deal with it JMP32 %function_load_done ; Be done :function_load_regular LOADI32_EAX &function_load_string_0 ; Using "LOAD_IMMEDIATE_eax &FUNCTION_" CALL32 %emit_out ; Emit it COPY_ECX_to_EAX ; Using A->S CALL32 %emit_out ; Emit it LOADI32_EAX &function_load_string_1 ; Using "\n" CALL32 %emit_out ; Emit it :function_load_done POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :function_load_string_0 "LOAD_IMMEDIATE_eax &FUNCTION_" :function_load_string_1 " " ;; global_load function ;; Receives struct token_list* A in EAX ;; Returns nothing ;; Uses EBX to hold A->S :global_load PUSH_EBX ; Protect EBX COPY_EAX_to_EBX ; Set as A LOAD32_EBX_from_EBX_Immediate8 !8 ; Set as A->S LOAD32_EAX_from_EAX_Immediate8 !12 ; A->TYPE STORE32_Absolute32_eax ¤t_target ; current_target = A->TYPE LOADI32_EAX &global_load_string_0 ; Using "LOAD_IMMEDIATE_eax &GLOBAL_" CALL32 %emit_out ; Emit it COPY_EBX_to_EAX ; Using A->S CALL32 %emit_out ; Emit it LOADI32_EAX &global_load_string_1 ; Using "\n" CALL32 %emit_out ; Emit it LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &equal ; "=" CALL32 %match ; IF global_token->S == "=" CMPI8_EAX !0 ; We need to skip for assignment JE32 %global_load_done ; and be done ;; Otherwise we are loading the contents LOADI32_EAX &global_load_string_2 ; Using "LOAD_INTEGER\n" CALL32 %emit_out ; Emit it :global_load_done POP_EBX ; Restore EBX RETURN :global_load_string_0 "LOAD_IMMEDIATE_eax &GLOBAL_" :global_load_string_1 " " :global_load_string_2 "LOAD_INTEGER " ;; sym_lookup function ;; Receives char* S in EAX and struct token_list* symbol_list in EBX ;; Uses I->S in EAX, S in EBX and I in ECX ;; Returns match or NULL :sym_lookup PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX COPY_EBX_to_ECX ; I = symbol_list COPY_EAX_to_EBX ; Put S in the right place :sym_lookup_iter CMPI8_ECX !0 ; IF NULL == I JE32 %sym_lookup_done ; We failed to find match LOAD32_EAX_from_ECX_Immediate8 !8 ; Using I->S CALL32 %match ; IF I->S == S CMPI8_EAX !0 ; then be done JE32 %sym_lookup_done ; Failed LOAD32_ECX_from_ECX ; I = I->NEXT JMP32 %sym_lookup_iter ; otherwise keep looping :sym_lookup_done COPY_ECX_to_EAX ; Return I POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; primary_expr_number function ;; Receives nothing ;; Returns nothing ;; Simply uses current global token to update output and then steps to next global_token :primary_expr_number LOADI32_EAX &primary_expr_number_string_0 ; Using "LOAD_IMMEDIATE_eax %" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S CALL32 %emit_out ; Emit it LOADI32_EAX &primary_expr_number_string_1 ; Using "\n" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT RETURN :primary_expr_number_string_0 "LOAD_IMMEDIATE_eax %" :primary_expr_number_string_1 " " ;; primary_expr_string function ;; receives nothing ;; Returns nothing ;; creates entries for string and calls to generate string output ;; uses ECX for char* number_string :primary_expr_string PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOAD32_Absolute32_ebx ¤t_count ; Using current_count COPY_EBX_to_EAX ; And putting it in the right place CALL32 %numerate_number ; Get the string COPY_EAX_to_ECX ; protect number_string ADDI8_EBX !1 ; current_count + 1 STORE32_Absolute32_ebx ¤t_count ; current_count = current_count + 1 LOADI32_EAX &primary_expr_string_string_0 ; Using "LOAD_IMMEDIATE_eax &STRING_" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S COPY_ECX_to_EBX ; Put number_string in the right place CALL32 %uniqueID_out ; Make it unique ;; Generate the target LOADI32_EAX &primary_expr_string_string_1 ; Using ":STRING_" LOAD32_Absolute32_ebx &strings_list ; Using strings_list CALL32 %emit ; Emit it COPY_EAX_to_EBX ; put new strings_list in place LOAD32_Absolute32_eax &function ; Using function LOAD32_EAX_from_EAX_Immediate8 !8 ; function->S CALL32 %uniqueID ; Make it unique COPY_EAX_to_EBX ; put new strings_list in place ;; Parse the string LOAD32_Absolute32_eax &global_token ; Using global token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S CALL32 %parse_string ; convert to useful form CALL32 %emit ; Emit it STORE32_Absolute32_eax &strings_list ; Update Strings _list LOAD32_Absolute32_eax &global_token ; Using global token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :primary_expr_string_string_0 "LOAD_IMMEDIATE_eax &STRING_" :primary_expr_string_string_1 ":STRING_" ;; primary_expr_char function ;; Receives nothing ;; Returns nothing ;; Updates output_list using global_token :primary_expr_char PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOADI32_EAX &primary_expr_char_string_0 ; Using "LOAD_IMMEDIATE_eax %" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &global_token ; Using global token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S ADDI8_EAX !1 ; global_token->S + 1 CALL32 %escape_lookup ; Get the char CALL32 %numerate_number ; Convert to string CALL32 %emit_out ; emit it LOADI32_EAX &primary_expr_char_string_1 ; Using "\n" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &global_token ; Using global token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :primary_expr_char_string_0 "LOAD_IMMEDIATE_eax %" :primary_expr_char_string_1 " " ;; primary_expr_failure function ;; Receives nothing ;; Does not return but aborts hard ;; Complains about the bad input :primary_expr_failure ; CALL32 %line_error ; Get line of issue LOADI32_EAX %2 ; Using Standard error STORE32_Absolute32_eax &Output_file ; write to standard error LOADI32_EAX &primary_expr_failure_string_0 ; Using "Received " CALL32 %File_Print ; Print it LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S CALL32 %File_Print ; Print it LOADI32_EAX &primary_expr_failure_string_1 ; Using " in primary_expr\n" CALL32 %File_Print ; Print it JMP32 %Exit_Failure ; Abort Hard :primary_expr_failure_string_0 "Received " :primary_expr_failure_string_1 " in primary_expr " ;; general_recursion function ;; Receives FUNCTION F in EAX, char* S in EBX, char* name in ECX and FUNCTION iterate in EDX ;; Returns nothing ;; Uses ECX for char* S, EDX for FUNCTION iterate and EBP for FUNCTION F ;; But generally recurses a shitload :general_recursion PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX PUSH_EBP ; Protect EBP COPY_EAX_to_EBP ; Protect F COPY_ECX_to_EAX ; Put name in the right place COPY_EBX_to_ECX ; Protect S LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S CALL32 %match ; IF match(name, global_token->s) CMPI8_EAX !0 ; If true we do JNE32 %general_recursion_done ; Otherwise skip it ;; Deal with the recursion COPY_EBP_to_EAX ; Put F in the right place CALL32 %common_recursion ; Recurse COPY_ECX_to_EAX ; Put S in the right place CALL32 %emit_out ; Emit it COPY_EDX_to_EAX ; Put iterate in the right place CALL_EAX ; Down the rabbit hole :general_recursion_done POP_EBP ; Restore EBP POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; promote_type function ;; Receives struct type* a in EAX and struct type* b in EBX ;; Returns the most recent type in EAX ;; Uses EAX for struct type* I, ECX for struct type* A and EDX for struct type* B :promote_type PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX CMPI8_EBX !0 ; IF NULL == A JE32 %promote_type_done ; Just return A COPY_EAX_to_ECX ; Put A in place COPY_EBX_to_EDX ; Put B in place COPY_EBX_to_EAX ; IF NULL == A CMPI8_ECX !0 ; Then we just return B JE32 %promote_type_done ; Be done ;; Looks like we need to walk the list LOAD32_ECX_from_ECX_Immediate8 !24 ; A->NAME LOAD32_EDX_from_EDX_Immediate8 !24 ; B->NAME LOAD32_Absolute32_eax &global_types ; I = global_types :promote_type_iter CMPI8_EAX !0 ; IF NULL == I JE32 %promote_type_done ; Just be done LOAD32_EBX_from_EAX_Immediate8 !24 ; I->NAME CMP_ECX_EBX ; IF(A->NAME == I->NAME) JE32 %promote_type_done ; Be done CMP_EBX_EDX ; IF(B->NAME == I->NAME) JE32 %promote_type_done ; Be done LOAD32_EBX_from_EAX_Immediate8 !12 ; I->INDIRECT LOAD32_EBX_from_EBX_Immediate8 !24 ; I->INDIRECT->NAME CMP_ECX_EBX ; IF(A->NAME == I->INDIRECT->NAME) JE32 %promote_type_done ; Be done CMP_EBX_EDX ; IF(B->NAME == I->INDIRECT->NAME) JE32 %promote_type_done ; Be done LOAD32_EAX_from_EAX ; I = I->NEXT JMP32 %promote_type_iter ; Keep going :promote_type_done POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; common_recursion function ;; Receives FUNCTION F in EAX ;; Returns Nothing ;; Walks global_token list and update output_list ;; Updates current_target ;; Uses EBX to hold FUNCTION F and struct type* last_type :common_recursion PUSH_EBX ; Protect EBX COPY_EAX_to_EBX ; Put FUNCTION F safely out of the way LOADI32_EAX &common_recursion_string_0 ; Using "PUSH_eax\t#_common_recursion\n" CALL32 %emit_out ; Emit it LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT COPY_EBX_to_EAX ; Prepare for function call LOAD32_Absolute32_ebx ¤t_target ; Get last type CALL_EAX ; F(); LOAD32_Absolute32_eax ¤t_target ; Get current_target CALL32 %promote_type ; get the right type STORE32_Absolute32_eax ¤t_target ; Set new current_target LOADI32_EAX &common_recursion_string_1 ; Using "POP_ebx\t# _common_recursion\n" CALL32 %emit_out ; Emit it POP_EBX ; Restore EBX RETURN :common_recursion_string_0 "PUSH_eax #_common_recursion " :common_recursion_string_1 "POP_ebx # _common_recursion " ;; require_match function ;; Receives char* message in EAX and char* required in EBX ;; Returns nothing ;; Uses ECX to hold message and updates global_token :require_match PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX COPY_EAX_to_ECX ; put the message somewhere safe LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S CALL32 %match ; IF required == global_token->S CMPI8_EAX !0 ; we are fine JE32 %require_match_good ; otherwise pain ;; Deal will bad times ; CALL32 %line_error ; Tell user what went wrong LOADI32_EAX %2 ; Using standard error STORE32_Absolute32_eax &Output_file ; write to standard error COPY_ECX_to_EAX ; using our message CALL32 %File_Print ; Print it JMP32 %Exit_Failure ; Abort HARD :require_match_good LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->next STORE32_Absolute32_eax &global_token ; global_token = global_token->next POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; uniqueID Function ;; Receives char*S in EAX, struct token_list* l in EBX snd char* num in ECX ;; Returns updated struct token_list* L in EAX :uniqueID PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX CALL32 %emit ; emit(s, l) COPY_EAX_to_EBX ; Put L in correct place LOADI32_EAX &underline ; Usinf "_" CALL32 %emit ; emit("_", l) COPY_EAX_to_EBX ; Put L in correct place COPY_ECX_to_EAX ; Put num in correct place CALL32 %emit ; emit(num, l) COPY_EAX_to_EBX ; Put L in correct place LOADI32_EAX &uniqueID_string_0 ; Using "\n" CALL32 %emit ; emit("\n", l) POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :uniqueID_string_0 " " ;; uniqueID_out function ;; Receives char* S in EAX and char* num in EBX ;; Returns nothing :uniqueID_out PUSH_EAX ; Protect EAX PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX COPY_EBX_to_ECX ; Put num in right spot LOAD32_Absolute32_ebx &output_list ; Using output_list CALL32 %uniqueID ; Get updated list STORE32_Absolute32_eax &output_list ; output_list = uniqueID(s, output_list, num) POP_ECX ; Restore ECX POP_EBX ; Restore EBX POP_EAX ; Restore EAX RETURN ;; emit_out function ;; Receives char* S in EAX ;; Returns nothing ;; Updates output_list ;; MUST NOT ALTER REGISTERS :emit_out PUSH_EAX ; Protect EAX PUSH_EBX ; Protect EBX LOAD32_Absolute32_ebx &output_list ; Using output_list CALL32 %emit ; emit it STORE32_Absolute32_eax &output_list ; update it POP_EBX ; Restore EBX POP_EAX ; Restore EAX RETURN ;; emit function ;; Receives char *s in EAX and struct token_list* head in EBX ;; Returns struct token_list* T in EAX :emit PUSH_ECX ; Protect ECX COPY_EAX_to_ECX ; put S out of the way LOADI32_EAX %20 ; sizeof(struct token_list) CALL32 %malloc ; get T STORE32_EBX_into_EAX ; t->next = head; STORE32_ECX_into_EAX_Immediate8 !8 ; t->s = s; POP_ECX ; Restore ECX RETURN ;; escape_lookup function ;; Receives char* c in EAX ;; Returns integer value of char in EAX ;; Aborts hard if unknown escape is received ;; Uses ECX to hold char* C :escape_lookup PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX COPY_EAX_to_ECX ; Put char* C in safe place LOAD8_al_from_ECX ; Load c[0] MOVZX_al ; make it useful CMPI8_EAX !92 ; If '\\' != c[0] JNE32 %escape_lookup_done ; Be done COPY_ECX_to_EBX ; Prepare for walk ADDI8_EBX !1 ; increment LOAD8_bl_from_EBX ; load c[1] MOVZX_bl ; make it useful CMPI8_EBX !120 ; Check if \x?? JE32 %escape_lookup_hex ; Deal with hex ;; Deal with \? escapes LOADI32_EAX %10 ; Guess "\n" CMPI8_EBX !110 ; If n JE32 %escape_lookup_done ; Be done LOADI32_EAX %9 ; Guess "\t" CMPI8_EBX !116 ; If t JE32 %escape_lookup_done ; Be done COPY_EBX_to_EAX ; "\\", "'" and '\"' all encode as themselves CMPI8_EBX !92 ; If "\\" JE32 %escape_lookup_done ; Be done CMPI8_EBX !39 ; IF "'" JE32 %escape_lookup_done ; Be done CMPI8_EBX !34 ; IF '\"' JE32 %escape_lookup_done ; Be done LOADI32_EAX %13 ; Guess "\r" CMPI8_EBX !114 ; IF r JE32 %escape_lookup_done ; Be done ;; Looks like we have no clue what we are doing ;; Aborting hard LOADI32_EAX %2 ; Using Standard error STORE32_Absolute32_eax &Output_file ; write to standard error LOADI32_EAX &escape_lookup_string_0 ; Using "Unknown escape received: " CALL32 %File_Print ; Print it COPY_ECX_to_EAX ; Using C CALL32 %File_Print ; Print it LOADI32_EAX &escape_lookup_string_1 ; Using " Unable to process\n" CALL32 %File_Print ; Print it JMP32 %Exit_Failure ; Abort Hard :escape_lookup_done POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :escape_lookup_hex ;; Give up on C and just assume they know what they are doing ADDI8_ECX !2 ; increment LOAD8_al_from_ECX ; c[2] MOVZX_al ; make it useful ADDI8_ECX !1 ; increment CALL32 %char2hex ; Get the hex value SALI8_EAX !4 ; c << 4 LOAD8_bl_from_ECX ; c[3] MOVZX_bl ; make it useful SWAP_EAX_EBX ; protect c << 4 CALL32 %char2hex ; Get the hex value ADD_ebx_into_eax ; hex(c[2]) << 4 + hex(c[3]) JMP32 %escape_lookup_done ; Be done :escape_lookup_string_0 "Unknown escape received: " :escape_lookup_string_1 " Unable to process " ;; char2hex function ;; Receives char in EAX ;; Returns hex or aborts hard :char2hex SUBI8_EAX !48 ; Try 0-9 CMPI8_EAX !10 ; Otherwise fun times JL32 %char2hex_done ; Be done ;; Deal with A-F ANDI32_EAX %0xDF ; Unset High bit turning a-f into A-F SUBI8_EAX !7 ; Shift down into position CMPI8_EAX !10 ; Everything below A is bad JL32 %char2hex_fail ; And should fail CMPI8_EAX !16 ; Make sure we are below F JL32 %char2hex_done ; If so be done :char2hex_fail ;; Time to fail hard LOADI32_EAX %2 ; Using Standard error STORE32_Absolute32_eax &Output_file ; write to standard error LOADI32_EAX &char2hex_string_0 ; Using "Tried to print non-hex number\n" CALL32 %File_Print ; Print it JMP32 %Exit_Failure ; Abort Hard :char2hex_done RETURN :char2hex_string_0 "Tried to print non-hex number " ;; parse_string function ;; Receives char* string in EAX ;; Returns cleaned up string ;; Protects char* string in EBX :parse_string PUSH_EBX ; Protect EBX COPY_EAX_to_EBX ; Protect char* string CALL32 %weird ; Determine if we have a weird string CMPI8_EAX !0 ; If weird JE32 %parse_string_weird ; Deal with it ;; Dealing with regular string COPY_EBX_to_EAX ; Passing Char* string CALL32 %collect_regular_string ; Collect it JMP32 %parse_string_done ; Be done :parse_string_weird COPY_EBX_to_EAX ; Passing Char* string CALL32 %collect_weird_string ; Collect it :parse_string_done POP_EBX ; Restore EBX RETURN ;; weird function ;; Receives char* string in EAX ;; Returns true(0) or false(1) in EAX ;; Uses ECX to hold char* string :weird PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX COPY_EAX_to_ECX ; Place string in safe place ADDI8_ECX !1 ; increment past the '\"' :weird_reset LOAD8_al_from_ECX ; Load a char MOVZX_al ; Make it useful CMPI8_EAX !0 ; IF NULL == C JE32 %weird_false ; Nothing weird found CMPI8_EAX !92 ; IF '\\' JNE32 %weird_escaped ; Deal with escaping ;; Deal with escape COPY_ECX_to_EAX ; We are passing the string CALL32 %escape_lookup ; to look it up ADDI8_ECX !1 ; string = string + 1 LOAD8_bl_from_ECX ; get string[1] MOVZX_bl ; make it useful CMPI8_EBX !120 ; IF 'x' == string[1] JNE32 %weird_escaped ; otherwise skip the gap ADDI8_ECX !2 ; string = string + 2 :weird_escaped PUSH_EAX ; Protect C in case we need it LOADI32_EBX &weird_string_0 ; Use "\t\n !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" CALL32 %In_Set ; To find if weird CMPI8_EAX !1 ; IF TRUE POP_EAX ; Restore C JNE32 %weird_true ; Then not weird ADDI8_ECX !1 ; string = string + 1 ;; Last chance for weird LOADI32_EBX &weird_string_1 ; Use "\t\n\r " CALL32 %In_Set ; Check for special case CMPI8_EAX !1 ; IF TRUE JNE32 %weird_reset ; Otherwise not in the special case ;; Deal with possible special case LOAD8_al_from_ECX ; Load string[1] MOVZX_al ; Make it useful CMPI8_EAX !58 ; IF string[1] == ":" JE32 %weird_true ; Then we hit the special case JMP32 %weird_reset ; Keep trying :weird_done POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :weird_true LOADI32_EAX %0 ; Return true JMP32 %weird_done ; Be done :weird_false LOADI32_EAX %1 ; Return false JMP32 %weird_done ; Be done :weird_string_0 " !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" :weird_string_1 '09 0A 0D 20' ; "\t\n\r " ;; collect_regular_string function ;; Receives char* string in EAX ;; Malloc and creates new string to return in EAX ;; Uses ECX for return string and EDX for passed string :collect_regular_string PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX COPY_EAX_to_EDX ; Protect our passed string LOADI32_EAX %256 ; We need 256bytes of storage CALL32 %malloc ; Get our new pointer COPY_EAX_to_ECX ; put it in place PUSH_EAX ; protect until done :collect_regular_string_reset LOAD8_al_from_EDX ; string[0] MOVZX_al ; Make it useful CMPI8_EAX !0 ; See if we hit the end JE32 %collect_regular_string_done ; And be done CMPI8_EAX !92 ; IF string[0] == '\\' JE32 %collect_regular_string_escaped ; Deal with that mess ;; deal with boring char STORE8_al_into_Address_ECX ; hold_string[index] = string[0] ADDI8_ECX !1 ; Increment it ADDI8_EDX !1 ; Increment it JMP32 %collect_regular_string_reset ; And keep going :collect_regular_string_escaped COPY_EDX_to_EAX ; Using string CALL32 %escape_lookup ; Get the char STORE8_al_into_Address_ECX ; hold_string[index] = escape_lookup(string) ADDI8_EDX !1 ; Increment it ADDI8_ECX !1 ; Increment it LOAD8_al_from_EDX ; string[0] MOVZX_al ; Make it useful ADDI8_EDX !1 ; Increment it CMPI8_EAX !120 ; IF 'x' == string[1] JNE32 %collect_regular_string_reset ; Otherwise keep going ADDI8_EDX !2 ; Increment it JMP32 %collect_regular_string_reset ; Keep going :collect_regular_string_done LOADI32_EAX %34 ; Using '\"' STORE8_al_into_Address_ECX ; hold_string[index] = '\"' ADDI8_ECX !1 ; Increment it LOADI32_EAX %10 ; Using "\n" STORE8_al_into_Address_ECX ; hold_string[index] = '\n' POP_EAX ; Return our new string POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; collect_weird_string function ;; Receives char* string in EAX ;; Mallocs and returns char* hold in EAX ;; Uses ECX for char* hold and EDX for char* string :collect_weird_string PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX COPY_EAX_to_EDX ; Protect our passed string LOADI32_EAX %512 ; We need 512bytes of storage CALL32 %malloc ; Get our new pointer COPY_EAX_to_ECX ; put it in place PUSH_EAX ; protect until done LOADI32_EAX %39 ; Using "'" STORE8_al_into_Address_ECX ; hold_string[index] = "'" ADDI8_ECX !1 ; Increment it ADDI8_EDX !1 ; Increment it :collect_weird_string_reset LOAD8_al_from_EDX ; Read a byte MOVZX_al ; Make it useful CMPI8_EAX !0 ; IF NULL == string[0] JE32 %collect_weird_string_done ; Be done LOADI32_EAX %32 ; Using ' ' STORE8_al_into_Address_ECX ; hold_string[index] = ' ' ADDI8_ECX !1 ; Increment it COPY_EDX_to_EAX ; Using string CALL32 %escape_lookup ; Get the char CALL32 %hex8 ; Update ECX LOAD8_al_from_EDX ; Read a byte MOVZX_al ; Make it useful ADDI8_EDX !1 ; Increment it CMPI8_EAX !92 ; IF string[0] == '\\' JNE32 %collect_weird_string_reset ; Otherwise keep going LOAD8_al_from_EDX ; Read a byte MOVZX_al ; Make it useful ADDI8_EDX !1 ; Increment it CMPI8_EAX !120 ; IF 'x' == string[1] JNE32 %collect_weird_string_reset ; Otherwise keep going ADDI8_EDX !2 ; Increment it JMP32 %collect_weird_string_reset ; Keep going :collect_weird_string_done LOADI32_EAX %32 ; Using ' ' STORE8_al_into_Address_ECX ; hold_string[index] = ' ' ADDI8_ECX !1 ; Increment it LOADI32_EAX %48 ; Using '0' STORE8_al_into_Address_ECX ; hold_string[index] = '0' ADDI8_ECX !1 ; Increment it STORE8_al_into_Address_ECX ; hold_string[index] = '0' ADDI8_ECX !1 ; Increment it LOADI32_EAX %39 ; Using "'" STORE8_al_into_Address_ECX ; hold_string[index] = "'" ADDI8_ECX !1 ; Increment it LOADI32_EAX %10 ; Using "\n" STORE8_al_into_Address_ECX ; hold_string[index] = '\n' POP_EAX ; Return our new string POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; HEX to ascii routine ;; Receives INT in EAX and CHAR* in ECX ;; Stores ascii of INT in CHAR* ;; Returns only modifying EAX and ECX :hex8 PUSH_EAX ; Protect bottom nibble SHRI8_EAX !4 ; do high nibble first CALL32 %hex4 ; Store it POP_EAX ; do low nibble :hex4 ANDI32_EAX %0xF ; isolate nibble ADDI8_EAX !48 ; convert to ascii CMPI8_EAX !57 ; valid digit? JBE8 !hex1 ; yes ADDI8_EAX !7 ; use alpha range :hex1 STORE8_al_into_Address_ECX ; store result ADDI8_ECX !1 ; next position RETURN ;; type_name function ;; Receives nothing ;; Returns type_size in EAX ;; Uses ECX for STRUCT TYPE* RET :type_name PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX_Immediate8 !8 ; global_token->S LOADI32_EAX &struct ; Using "struct" CALL32 %match ; IF global_token->S == "struct" COPY_EAX_to_ECX ; Protect structure CMPI8_EAX !0 ; need to skip over "struct" JNE32 %type_name_native ; otherwise keep going ;; Deal with possible STRUCTs LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX ; global_token->next STORE32_Absolute32_ebx &global_token ; global_token = global_token->next LOAD32_EAX_from_EBX_Immediate8 !8 ; global_token->S LOAD32_Absolute32_ebx &global_types ; get all known types CALL32 %lookup_type ; Find type if possible COPY_EAX_to_ECX ; Set ret CMPI8_EAX !0 ; IF NULL == ret JNE32 %type_name_common ; We have to create struct ;; Create a struct CALL32 %create_struct ; Create a new struct LOADI32_ECX %0 ; We wish to return NULL JMP32 %type_name_done ; be done :type_name_native ;; Deal only with native types COPY_EBX_to_EAX ; Put global_token->S in the right place LOAD32_Absolute32_ebx &global_types ; get all known types CALL32 %lookup_type ; Find the type if possible COPY_EAX_to_ECX ; Set ret CMPI8_EAX !0 ; IF NULL == ret JNE32 %type_name_common ; We need to abort hard ;; Aborting hard LOADI32_EAX %2 ; Using Standard error STORE32_Absolute32_eax &Output_file ; write to standard error LOADI32_EAX &type_name_string_0 ; Print header CALL32 %File_Print ; Print it LOAD32_Absolute32_eax &global_token ; Using global token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S CALL32 %File_Print ; Print it LOADI32_EAX &type_name_string_1 ; Print footer CALL32 %File_Print ; Print it ; CALL32 %line_error ; Give details JMP32 %Exit_Failure ; Abort :type_name_common LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX ; global_token->next STORE32_Absolute32_ebx &global_token ; global_token = global_token->next :type_name_iter LOAD32_EAX_from_EBX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; make it useful CMPI8_EAX !42 ; IF global_token->S[0] == '*' JNE32 %type_name_done ; recurse ;; Deal with char** LOAD32_ECX_from_ECX_Immediate8 !12 ; ret = ret->indirect LOAD32_Absolute32_ebx &global_token ; Using global_token LOAD32_EBX_from_EBX ; global_token->next STORE32_Absolute32_ebx &global_token ; global_token = global_token->next JMP32 %type_name_iter ; keep looping :type_name_done COPY_ECX_to_EAX ; put ret in the right place POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :type_name_string_0 "Unknown type " :type_name_string_1 " " ;; lookup_type function ;; Receives char* s in EAX and struct type* start in EBX ;; Returns struct type* in EAX ;; Uses EBX for S and ECX for I :lookup_type PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX COPY_EBX_to_ECX ; I = Start COPY_EAX_to_EBX ; Put S in place :lookup_type_iter CMPI8_ECX !0 ; Check if I == NULL JE32 %lookup_type_done ; return NULL LOAD32_EAX_from_ECX_Immediate8 !24 ; I->NAME CALL32 %match ; Check if matching CMPI8_EAX !0 ; IF I->NAME == S JE32 %lookup_type_done ; return it LOAD32_ECX_from_ECX ; Otherwise I = I->NEXT JMP32 %lookup_type_iter ; And keep looping :lookup_type_done COPY_ECX_to_EAX ; return either I or NULL POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; create_struct function ;; Receives nothing ;; Returns nothing ;; Uses global_token to malloc a struct's definition ;; Uses ECX for int OFFSET, EDX for struct type* head, EBP for struct type* I, ;; EDI for member_size (Which is passed) and ESI for LAST ;; EAX and EBX are used for scratch :create_struct PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX PUSH_EBP ; Protect EBP PUSH_EDI ; Protect EDI PUSH_ESI ; Protect ESI LOADI32_ECX %0 ; OFFSET = 0 LOADI32_EDI %0 ; member_size = 0 LOADI32_EAX %28 ; sizeof(struct type) CALL32 %malloc ; malloc(sizeof(struct type)) COPY_EAX_to_EDX ; Set HEAD LOADI32_EAX %28 ; sizeof(struct type) CALL32 %malloc ; malloc(sizeof(struct type)) COPY_EAX_to_EBP ; Set I LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S STORE32_EAX_into_EDX_Immediate8 !24 ; HEAD->NAME = global_token->S STORE32_EAX_into_EBP_Immediate8 !24 ; I->NAME = global_token->S STORE32_EBP_into_EDX_Immediate8 !12 ; HEAD->INDIRECT = I STORE32_EDX_into_EBP_Immediate8 !12 ; I->INDIRECT = HEAD LOAD32_Absolute32_eax &global_types ; Using global_types STORE32_EAX_into_EDX ; HEAD->NEXT = global_types STORE32_Absolute32_edx &global_types ; global_types = HEAD LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOADI32_EAX %4 ; Using register size STORE32_EAX_into_EBP_Immediate8 !4 ; I->SIZE = register size LOADI32_EAX &create_struct_string_0 ; Using "ERROR in create_struct\n Missing {\n" LOADI32_EBX &open_curly_brace ; Using "{" CALL32 %require_match ; Make sure we have it LOADI32_ESI %0 ; LAST = NULL :create_struct_iter LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; Make it useful CMPI8_EAX !125 ; IF global_token->S[0] == "}" JE32 %create_struct_done ; be done ;; Looks like we are adding members ;; Lets see if it is a union LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOADI32_EBX &union ; Using "union" CALL32 %match ; IF match(global_token->s, "union") CMPI8_EAX !0 ; Deal with union JNE32 %create_struct_single ; Otherwise deal with singles ;; Deal with union COPY_ESI_to_EAX ; Put last in right place COPY_ECX_to_EBX ; put offset in right place CALL32 %build_union ; ASSEMBLE COPY_EAX_to_ESI ; last = build_union(last, offset) ADD_edi_into_ecx ; offset = offset + member_size LOADI32_EAX &create_struct_string_1 ; Using "ERROR in create_struct\n Missing ;\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Make sure we have it JMP32 %create_struct_iter ; keep going :create_struct_single ;; deal with singles COPY_ESI_to_EAX ; Put last in right place COPY_ECX_to_EBX ; put offset in right place CALL32 %build_member ; ASSEMBLE COPY_EAX_to_ESI ; last = build_union(last, offset) ADD_edi_into_ecx ; offset = offset + member_size LOADI32_EAX &create_struct_string_1 ; Using "ERROR in create_struct\n Missing ;\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Make sure we have it JMP32 %create_struct_iter ; keep going :create_struct_done LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOADI32_EAX &create_struct_string_1 ; Using "ERROR in create_struct\n Missing ;\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Make sure we have it STORE32_ECX_into_EDX_Immediate8 !4 ; HEAD->SIZE = OFFSET STORE32_ESI_into_EDX_Immedate8 !16 ; HEAD->MEMBERS = LAST STORE32_ESI_into_EBP_Immedate8 !16 ; I->MEMBERS = LAST POP_ESI ; Restore ESI POP_EDI ; Restore EDI POP_EBP ; Restore EBP POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :create_struct_string_0 "ERROR in create_struct Missing { " :create_struct_string_1 "ERROR in create_struct Missing ; " ;; lookup_member function ;; Receives struct type* parent in EAX and char* name in EBX ;; Returns struct type* I in EAX ;; Uses char* NAME in EBX, ECX for struct type* I and EDX to hold parent for errors ;; Aborts hard if not found :lookup_member PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX COPY_EAX_to_EDX ; Protect Parent LOAD32_ECX_from_EAX_Immediate8 !16 ; struct type* I = parent->MEMBERS :lookup_member_iter CMPI8_ECX !0 ; IF I == NULL JE32 %lookup_member_fail ; Abort HARD LOAD32_EAX_from_ECX_Immediate8 !24 ; Using I->NAME CALL32 %match ; IF I->NAME == NAME CMPI8_EAX !0 ; Then we have found the member COPY_ECX_to_EAX ; Prepare for return LOAD32_ECX_from_ECX_Immediate8 !16 ; Prepare for loop I = I->MEMBERS JNE32 %lookup_member_iter ; Looks like we are looping ;; I is already in EAX POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :lookup_member_fail LOADI32_EAX %2 ; Using Standard error STORE32_Absolute32_eax &Output_file ; write to standard error LOADI32_EAX &lookup_member_string_0 ; Using "ERROR in lookup_member " CALL32 %File_Print ; print it LOAD32_EAX_from_EDX_Immediate8 !24 ; PARENT->NAME CALL32 %File_Print ; print it LOADI32_EAX &arrow_string ; Using "->" CALL32 %File_Print ; print it LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S CALL32 %File_Print ; print it LOADI32_EAX &lookup_member_string_1 ; Using " does not exist\n" CALL32 %File_Print ; print it ; CALL32 %line_error ; Write useful debug info LOADI32_EAX &lookup_member_string_2 ; Using "\n" CALL32 %File_Print ; print it JMP32 %Exit_Failure ; Abort Hard :lookup_member_string_0 "ERROR in lookup_member " :lookup_member_string_1 " does not exist " :lookup_member_string_2 " " ;; build_member function ;; Receives struct type* last in EAX, int offset in EBX and global member_size in EDI ;; Updates member_size in EDI and returns struct type* I in EAX ;; Uses ECX for struct type* member_type and EDX for struct type* I :build_member PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX COPY_EAX_to_EDX ; Put last out of the way LOADI32_EAX %28 ; Allocate type CALL32 %malloc ; Get I STORE32_EDX_into_EAX_Immediate8 !16 ; I->MEMBERS = LAST STORE32_EBX_into_EAX_Immediate8 !8 ; I->OFFSET = OFFSET COPY_EAX_to_EDX ; Put I in place CALL32 %type_name ; Get member_type COPY_EAX_to_ECX ; Put in place STORE32_ECX_into_EDX_Immediate8 !20 ; I->TYPE = MEMBER_TYPE LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EBX_from_EAX_Immediate8 !8 ; global_token->S STORE32_EBX_into_EDX_Immediate8 !24 ; I->NAME = global_token->S LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT ;; Check if we have an array LOAD32_EBX_from_EAX_Immediate8 !8 ; global_token->S LOADI32_EAX &open_bracket ; Using "[" CALL32 %match ; IF global_token->S == "[" CMPI8_EAX !0 ; Then we have to deal with arrays in our structs JE32 %build_member_array ; So deal with that pain ;; Deal with non-array case LOAD32_EAX_from_ECX_Immediate8 !4 ; member_type->SIZE STORE32_EAX_into_EDX_Immediate8 !4 ; I->SIZE = member_type->SIZE JMP32 %build_member_done ; Be done :build_member_array LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S CALL32 %numerate_string ; convert number LOAD32_EBX_from_ECX_Immediate8 !20 ; member_type->TYPE LOAD32_EBX_from_EBX_Immediate8 !4 ; member_type->TYPE->SIZE IMUL_EAX_by_EBX ; member_type->type->size * numerate_string(global_token->s) STORE32_EAX_into_EDX_Immediate8 !4 ; I->SIZE = member_type->type->size * numerate_string(global_token->s) LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOADI32_EAX &build_member_string_0 ; Using "Struct only supports [num] form\n" LOADI32_EBX &close_bracket ; Using "]" CALL32 %require_match ; Make sure we have it :build_member_done LOAD32_EDI_from_EDX_Immediate8 !4 ; MEMBER_SIZE = I->SIZE STORE32_ECX_into_EDX_Immediate8 !20 ; I->TYPE = MEMBER_TYPE COPY_EDX_to_EAX ; Return I POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :build_member_string_0 "Struct only supports [num] form " ;; build_union function ;; Receives struct type* last in EAX, int offset in EBX and global member_size in EDI ;; Updates member_size in EDI and returns struct type* LAST in EAX ;; Uses ECX for struct type* last, EDX for int offset, ESI for int size and EDI for int member_size :build_union PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX PUSH_ESI ; Protect ESI COPY_EAX_to_ECX ; Put LAST in right spot COPY_EBX_to_EDX ; Put OFFSET in right spot LOADI32_ESI %0 ; SIZE = 0 LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT LOADI32_EAX &build_union_string_0 ; Using "ERROR in build_union\nMissing {\n" LOADI32_EBX &open_curly_brace ; Using "{" CALL32 %require_match ; Make sure we have it :build_union_iter LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX_Immediate8 !8 ; global_token->S LOAD8_al_from_EAX ; global_token->S[0] MOVZX_al ; make it useful CMPI8_EAX !125 ; IF global_token->S[0] == "}" JE32 %build_union_done ; Be done ;; Collect union member COPY_ECX_to_EAX ; Passing LAST COPY_EDX_to_EBX ; Passing offset CALL32 %build_member ; build_member(last, offset) COPY_EAX_to_ECX ; last = build_member(last, offset) CMP_EDI_ESI ; IF member_size > size JG32 %build_union_size ; Then update size ;; deal with member_size > size COPY_EDI_to_ESI ; SIZE = MEMBER_SIZE :build_union_size LOADI32_EAX &build_union_string_1 ; Using "ERROR in build_union\nMissing ;\n" LOADI32_EBX &semicolon ; Using ";" CALL32 %require_match ; Make sure we have it JMP32 %build_union_iter ; Keep going :build_union_done COPY_ESI_to_EDI ; MEMBER_SIZE = SIZE LOAD32_Absolute32_eax &global_token ; Using global_token LOAD32_EAX_from_EAX ; global_token->NEXT STORE32_Absolute32_eax &global_token ; global_token = global_token->NEXT COPY_ECX_to_EAX ; Return last POP_ESI ; Restore ESI POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :build_union_string_0 "ERROR in build_union Missing { " :build_union_string_1 "ERROR in build_union Missing ; " ;; sym_declare function ;; Receives char *s in EAX, struct type* t in EBX, and struct token_list* list in ECX ;; Returns struct token_list* in EAX ;; Uses EAX for A :sym_declare PUSH_EDX ; Protect EDX COPY_EAX_to_EDX ; Get char *S safely out of the way LOADI32_EAX %20 ; Using sizeof(struct token_list) CALL32 %malloc ; Get pointer to A STORE32_ECX_into_EAX ; A->NEXT = LIST STORE32_EDX_into_EAX_Immediate8 !8 ; A->S = S STORE32_EBX_into_EAX_Immediate8 !12 ; A->TYPE = T POP_EDX ; Restore EDX RETURN ;; 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 RETURN ;; numerate_number function ;; Receives an INT A in EAX ;; Returns char* result in EAX ;; Allocates 16 bytes of memory ;; Behaves badly when given a negative number too large ;; Uses EAX for temp, EBX for DIVISOR, EDX for mod/0, ESI for result[i] and EBP for A :numerate_number PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX PUSH_ESI ; Protect ESI PUSH_EBP ; Protect EBP COPY_EAX_to_EBP ; Protect A LOADI32_EAX %16 ; 16bytes CALL32 %malloc ; Get our pointer PUSH_EAX ; Protect our pointer COPY_EAX_to_ESI ; put pointer in right place LOADI32_EBX %0x3B9ACA00 ; Set divisor to largest positive number that fits in 32bits CMPI8_EBP !0 ; Deal with 0 case JE32 %numerate_number_ZERO ; If it is JG32 %numerate_number_positive ; If it is positive ;; Deal with negative case LOADI32_EAX %45 ; Using "-" STORE8_al_into_Address_ESI ; Write it ADDI8_ESI !1 ; increment IMULI8_EBP !-1 ; A = A * -1 :numerate_number_positive LOADI32_EDX %0 ; Set top to 0 COPY_EBP_to_EAX ; Using A as bottom IDIV_EBX ; edx:eax % ebx -> edx + edx:eax / ebx -> eax [Even if we don't want it] CMPI8_EAX !0 ; IF 0 == (a / divisor) JNE32 %numerate_number_iter ; Clean up those leading Zeros LOADI32_EDX %0 ; Set top to 0 COPY_EBX_to_EAX ; Using Divisor for bottom LOADI32_EBX %10 ; Make this shit work because idiv 10 doesn't work IDIV_EBX ; edx:eax % 10 -> edx + edx:eax / 10 -> eax [Even if we don't want it] COPY_EAX_to_EBX ; Update divisor JMP32 %numerate_number_positive ; Keep collecting :numerate_number_iter CMPI8_EBX !0 ; IF DIVISOR < 0 JLE32 %numerate_number_done ; Be done LOADI32_EDX %0 ; Set top to 0 COPY_EBP_to_EAX ; Using A as bottom IDIV_EBX ; edx:eax % ebx -> edx + edx:eax / ebx -> eax [Even if we don't want it] ADDI8_EAX !48 ; ((a / divisor) + 48) STORE8_al_into_Address_ESI ; Write it COPY_EDX_to_EBP ; a = a % divisor LOADI32_EDX %0 ; Set top to 0 COPY_EBX_to_EAX ; Using Divisor for bottom LOADI32_EBX %10 ; Make this shit work because idiv 10 doesn't work IDIV_EBX ; edx:eax % 10 -> edx + edx:eax / 10 -> eax [Even if we don't want it] COPY_EAX_to_EBX ; Update divisor ADDI8_ESI !1 ; increment JMP32 %numerate_number_iter ; Keep going :numerate_number_done POP_EAX ; Restore our result POP_EBP ; Restore EBP POP_ESI ; Restore ESI POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN :numerate_number_ZERO LOADI32_EAX %48 ; Using '0' STORE8_al_into_Address_ESI ; Write it ADDI8_ESI !1 ; increment JMP32 %numerate_number_done ; Be done ;; numerate_string function ;; Receives CHAR* in EAX ;; Returns value of CHAR* in EAX ;; Uses EAX for VALUE, EBX for S, ECX for CH and ESI for NEGATIVE? :numerate_string PUSH_EBX ; Protect EBX PUSH_ECX ; Protect ECX PUSH_EDX ; Protect EDX PUSH_ESI ; Protect ESI 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_ESI %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_ESI %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_into_eax ; VALUE = VALUE + CH ADDI8_EBX !1 ; S = S + 1 JMP32 %numerate_decimal ; Keep looping :numerate_decimal_done CMPI8_ESI !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 SALI8_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_into_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_ESI ; Restore ESI POP_EDX ; Restore EDX POP_ECX ; Restore ECX POP_EBX ; Restore EBX RETURN ;; Exit_Failure function ;; Receives nothing ;; And aborts hard ;; Does NOT return :Exit_Failure LOADI32_EBX %1 ; All is wrong LOADI32_EAX %1 ; put the exit syscall number in eax INT_80 ; Call it a bad day ;; Keywords :union "union" :struct "struct" :constant "CONSTANT" :main_string "main" :argc_string "argc" :argv_string "argv" :if_string "if" :else_string "else" :do_string "do" :while_string "while" :for_string "for" :asm_string "asm" :goto_string "goto" :return_string "return" :break_string "break" :continue_string "continue" :sizeof_string "sizeof" :plus_string "+" :minus_string "-" :multiply_string "*" :divide_string "/" :modulus_string "%" :left_shift_string "<<" :right_shift_string ">>" :less_than_string "<" :less_than_equal_string "<=" :greater_than_equal_string ">=" :greater_than_string ">" :equal_to_string "==" :not_equal_string "!=" :bitwise_and "&" :logical_and "&&" :bitwise_or "|" :logical_or "||" :bitwise_xor "^" :arrow_string "->" ;; Frequently Used strings ;; Generally used by require_match :open_curly_brace "{" :close_curly_brace "}" :open_paren "(" :close_paren ")" :open_bracket "[" :close_bracket "]" :comma "," :semicolon ";" :equal "=" :percent "%" :newline "\n" :underline "_" :prim_types :type_void &type_int ; NEXT %4 ; SIZE %0 ; OFFSET &type_void ; INDIRECT %0 ; MEMBERS &type_void ; TYPE &type_void_name ; NAME :type_void_name "void" :type_int &type_char ; NEXT %4 ; SIZE %0 ; OFFSET &type_int ; INDIRECT %0 ; MEMBERS &type_int ; TYPE &type_int_name ; NAME :type_int_name "int" :type_char &type_file ; NEXT %1 ; SIZE %0 ; OFFSET &type_char_indirect ; INDIRECT %0 ; MEMBERS &type_char ; TYPE &type_char_name ; NAME :type_char_name "char" :type_char_indirect &type_file ; NEXT %4 ; SIZE %0 ; OFFSET &type_char_double_indirect ; INDIRECT %0 ; MEMBERS &type_char_indirect ; TYPE &type_char_indirect_name ; NAME :type_char_indirect_name "char*" :type_char_double_indirect &type_file ; NEXT %4 ; SIZE %0 ; OFFSET &type_char_double_indirect ; INDIRECT %0 ; MEMBERS &type_char_indirect ; TYPE &type_char_double_indirect_name ; NAME :type_char_double_indirect_name "char**" :type_file &type_function ; NEXT %4 ; SIZE %0 ; OFFSET &type_file ; INDIRECT %0 ; MEMBERS &type_file ; TYPE &type_file_name ; NAME :type_file_name "FILE" :type_function &type_unsigned ; NEXT %4 ; SIZE %0 ; OFFSET &type_function ; INDIRECT %0 ; MEMBERS &type_function ; TYPE &type_function_name ; NAME :type_function_name "FUNCTION" :type_unsigned %0 ; NEXT %4 ; SIZE %0 ; OFFSET &type_unsigned ; INDIRECT %0 ; MEMBERS &type_unsigned ; TYPE &type_unsigned_name ; NAME :type_unsigned_name "unsigned" ;; debug_list function ;; Receives struct token_list* in EAX ;; Prints contents of list and exits ;; Does NOT return :debug_list COPY_EAX_to_EBP ; Protect the list pointer LOADI32_EAX %2 ; Using Standard error STORE32_Absolute32_eax &Output_file ; write to standard error :debug_list_iter ;; Header LOADI32_EAX &debug_list_string0 ; Using our first string CALL32 %File_Print ; Print it COPY_EBP_to_EAX ; Use address of pointer CALL32 %numerate_number ; Convert it into string CALL32 %File_Print ; Print it ;; NEXT LOADI32_EAX &debug_list_string1 ; Using our second string CALL32 %File_Print ; Print it LOAD32_EAX_from_EBP_Immediate8 !0 ; Use address of pointer CALL32 %numerate_number ; Convert it into string CALL32 %File_Print ; Print it ;; PREV LOADI32_EAX &debug_list_string2 ; Using our third string CALL32 %File_Print ; Print it LOAD32_EAX_from_EBP_Immediate8 !4 ; Use address of pointer CALL32 %numerate_number ; Convert it into string CALL32 %File_Print ; Print it ;; S LOADI32_EAX &debug_list_string3 ; Using our fourth string CALL32 %File_Print ; Print it LOAD32_EAX_from_EBP_Immediate8 !8 ; Use address of pointer CALL32 %numerate_number ; Convert it into string CALL32 %File_Print ; Print it ;; S Contents LOADI32_EAX &debug_list_string4 ; Using our fifth string CALL32 %File_Print ; Print it LOAD32_EAX_from_EBP_Immediate8 !8 ; Use address of string CMPI8_EAX !0 ; IF NULL Pointer JNE32 %debug_list_null ; otherwise display LOADI32_EAX &debug_list_string_null ; Give meaningful message instead :debug_list_null CALL32 %File_Print ; Print it ;; TYPE LOADI32_EAX &debug_list_string5 ; Using our sixth string CALL32 %File_Print ; Print it LOAD32_EAX_from_EBP_Immediate8 !12 ; Use address of pointer CALL32 %numerate_number ; Convert it into string CALL32 %File_Print ; Print it ;; ARGS/DEPTH LOADI32_EAX &debug_list_string6 ; Using our third string CALL32 %File_Print ; Print it LOAD32_EAX_from_EBP_Immediate8 !16 ; Use address of pointer CALL32 %numerate_number ; Convert it into string CALL32 %File_Print ; Print it LOADI32_EAX %10 ; Add "\n" CALL32 %fputc ; print it CALL32 %fputc ; print it LOAD32_EBP_from_EBP ; TOKEN = TOKEN->NEXT CMPI8_EBP !0 ; Check if NULL JNE32 %debug_list_iter ; iterate otherwise LOADI32_EBX %666 ; All is HELL LOADI32_EAX %1 ; put the exit syscall number in eax INT_80 ; Call it a bad day :debug_list_string0 "Token_list node at address: " :debug_list_string1 " NEXT address: " :debug_list_string2 " PREV address: " :debug_list_string3 " S address: " :debug_list_string4 " The contents of S are: " :debug_list_string5 " TYPE address: " :debug_list_string6 " ARGUMENTS address: " :debug_list_string_null ">::::<" :Address_of NULL :C NULL :Input_file NULL :MALLOC NULL :Output_file NULL :Token NULL :break_frame NULL :break_target_func NULL :break_target_head NULL :break_target_num NULL :current_count NULL :current_target NULL :function NULL :global_constant_list NULL :global_function_list NULL :global_symbol_list NULL :global_token NULL :global_types &prim_types :globals_list NULL :output_list NULL :string_index NULL :strings_list NULL :ELF_end