stage0/Linux Bootstrap/x86/cc_x86.S

4619 lines
162 KiB
ArmAsm

;; Copyright (C) 2017 Jeremiah Orians
;; This file is part of stage0.
;;
;; stage0 is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; stage0 is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with stage0. If not, see <http://www.gnu.org/licenses/>.
section .text
global _start
;; 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
mov ecx, 0 ;·prepare·read_only
mov eax, 5 ;·the·syscall·number·for·open()
int 0x80 ; Now open that damn file
mov [Input_file], eax ; Preserve the file pointer we were given
pop ebx ;·Get·the·actual·output name
mov ecx, 577 ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
mov edx, 384 ; Prepare file as RW for owner only (600 in octal)
mov eax, 5 ;·the·syscall·number·for·open()
int 0x80 ; Now open that damn file
cmp eax, 0 ; Check for missing output
jg _start_out ; Have real input
mov eax, 1 ; Use stdout
_start_out:
mov [Output_file], eax ; Preserve the file pointer we were given
mov eax, 45 ; the Syscall # for SYS_BRK
mov ebx, 0 ; Get current brk
int 0x80 ; Let the kernel do the work
mov [MALLOC], eax ; Set our malloc pointer
mov eax, 0 ; HEAD = NULL
call read_all_tokens ; Read all tokens
call Reverse_List ; Reverse order
; call debug_list ; Try to figure out what is wrong
mov [global_token], eax ; Set global_token
call program ; Convert into program
mov eax, header_string1 ; Our header string
call File_Print ; Print it
mov eax, [output_list] ; Our output_list
call recursive_output ; Print core program
mov eax, header_string2 ; Our second label
call File_Print ; Print it
mov eax, [globals_list] ; Our globals
call recursive_output ; Get them
mov eax, header_string3 ; Our final header
call File_Print ; Print it
mov eax, [strings_list] ; Our strings
call recursive_output ; Get them
Done:
; program completed Successfully
mov ebx, 0 ; All is well
mov eax, 1 ; put the exit syscall number in eax
int 0x80 ; Call it a good day
header_string1: db 10, "# Core program", 10, 0
header_string2: db 10, ":ELF_data", 10, 10, "# Program global variables", 10, 0
header_string3: db 10, "# Program strings", 10, 0
;; 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:
mov [Token], eax
call fgetc
read_all_tokens_loop:
cmp eax, -4 ; Check for EOF
je read_all_tokens_done ; Stop if found
call get_token ; Read all tokens
jmp read_all_tokens_loop ; Loop
read_all_tokens_done:
mov eax, [Token]
ret
;; 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
mov [C], eax ; Set C
mov eax, 20 ; Malloc CURRENT
call malloc ; Get Pointer
mov edx, eax ; Set CURRENT
mov eax, 256 ; Malloc the string
call malloc ; Get pointer to S
mov ecx, eax ; Set S
mov [edx+8], ecx ; CURRENT->S = S
reset:
mov [string_index], ecx ; S[0]
mov eax, [C] ; Using C
call clear_white_space ; Clear WhiteSpace
mov [C], eax ; Set C
cmp eax, -4 ; Check for EOF
je get_token_abort ; if EOF abort
cmp eax, 35 ; Check for '#'
jne get_token_alpha ; Nope
;; Deal with # line comments
call purge_macro ; Let it handle it
mov [C], eax ; Set C
jmp reset ; Try again
get_token_alpha:
mov eax, [C] ; Send C
mov ebx, alphas ; Get alphanumerics
call In_Set ; See if in set
cmp eax, 1 ; IF TRUE
jne get_token_symbol ; Otherwise
;; Store keywords
mov eax, [C] ; Send C
call preserve_keyword ; Store
mov [C], eax ; Set C
jmp get_token_done ; Be done with this token
get_token_symbol:
mov eax, [C] ; Send C
mov ebx, symbols ; Get symbols
call In_Set ; See if in set
cmp eax, 1 ; IF TRUE
jne get_token_strings ; Otherwise
;; Store symbols
mov eax, [C] ; Send C
call preserve_symbol ; Store
mov [C], eax ; Set C
jmp get_token_done ; Be done with this token
get_token_strings:
mov eax, [C] ; Send C
mov ebx, strings ; Get symbols
call In_Set ; See if in set
cmp eax, 1 ; IF TRUE
jne get_token_comment ; Otherwise
;; Store String
mov eax, [C] ; Send C
call consume_word ; Store
mov [C], eax ; Set C
jmp get_token_done ; Be done with this token
get_token_comment:
mov eax, [C] ; Send C
cmp eax, 47 ; IF '/' == C
jne get_token_else ; Otherwise
call consume_byte ; Hope it just is '/'
mov [C], eax ; Set C
cmp eax, 42 ; IF '*' we have '/*'
jne get_token_comment_line ; Check for '//'
;; Deal with /* block comments */
call fgetc ; get next C
mov [C], eax ; Set C
get_token_comment_block_outer:
mov eax, [C] ; Using C
cmp eax, 47 ; IF '/' != C
je get_token_comment_block_done ; be done
get_token_comment_block_inner:
mov eax, [C] ; Using C
cmp eax, 42 ; IF '*' != C
je get_token_comment_block_iter ; jump over
;; Deal with inner loop
call fgetc ; get next C
mov [C], eax ; Set C
jmp get_token_comment_block_inner ; keep going
get_token_comment_block_iter:
call fgetc ; get next C
mov [C], eax ; Set C
jmp get_token_comment_block_outer
get_token_comment_block_done:
call fgetc ; get next C
mov [C], eax ; Set C
jmp reset ; throw away, try again
get_token_comment_line:
cmp eax, 47 ; IF '/' we have //
jne get_token_done ; keep if just '/'
;; Deal with // line comment
call fgetc ; drop to match
mov [C], eax ; Set C
jmp reset ; throw away, try again
get_token_else:
mov eax, [C] ; Send C
call consume_byte
mov [C], eax ; Set C
get_token_done:
mov eax, [Token] ; TOKEN
mov [edx+4], eax ; CURRENT->PREV = TOKEN
mov [edx], eax ; CURRENT->NEXT = TOKEN
mov [Token], edx ; TOKEN = CURRENT
get_token_abort:
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
mov eax, [C] ; Return C
ret
;; Malloc isn't actually required if the program being built fits in the initial memory
;; However, it doesn't take much to add it.
;; Requires [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
mov ebx, [MALLOC] ; Using the current pointer
add ebx, eax ; Request the number of desired bytes
mov eax, 45 ; the Syscall # for SYS_BRK
int 0x80 ; call the Kernel
mov eax, [MALLOC] ; Return pointer
mov [MALLOC], ebx ; Update pointer
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; clear_white_space function
;; Receives INT C in EAX
;; Returns first non-whitespace char in EAX
clear_white_space:
cmp eax, 32 ; Check for ' '
je clear_white_space_wipe ; wipe it out
cmp eax, 10 ; Check for '\n'
je clear_white_space_wipe ; wipe it output
cmp eax, 9 ; Check for '\t'
jne clear_white_space_done ; looks like non-whitespace
clear_white_space_wipe:
call fgetc ; Read a new byte
cmp eax, -4 ; Check for EOF
je clear_white_space_done ; Short circuit
jmp clear_white_space ; iterate
clear_white_space_done:
ret
;; In_Set function
;; Receives Char C in EAX and CHAR* in EBX
;; Returns 1 if true, zero if false in EAX
In_Set:
push ebx ; Protect EBX
push ecx ; Protect ECX
In_Set_loop:
mov cl, [ebx] ; Read char
movzx ecx, cl ; Zero extend it
cmp eax, ecx ; See if they match
je In_Set_True ; return true
cmp ecx, 0 ; Check for NULL
je In_Set_False ; return false
add ebx, 1 ; s = s + 1
jmp In_Set_loop ; Keep looping
In_Set_True:
mov eax, 1 ; Set True
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
In_Set_False:
mov eax, 0 ; Set FALSE
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
alphas: db "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz", 0
symbols: db "<=>|&!-", 0
strings: db 34, 39, 0
;; purge_macro function
;; Receives CH in EAX
;; Reads chars until Line feed is read
;; returns line feed
purge_macro:
call fgetc ; read next char
cmp eax, 10 ; Check for '\n'
jne purge_macro ; Keep going
ret
;; 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
mov ecx, eax ; Setup C
mov ebx, alphas ; Concerning ourselves with "abc.."
preserve_keyword_loop:
call In_Set ; Check if alphanumerics
cmp eax, 1 ; IF TRUE
jne preserve_keyword_label ; Otherwise check for label
mov eax, ecx ; Pass C
call consume_byte ; consume that byte
mov ecx, eax ; Update C
jmp preserve_keyword_loop ; keep looping
preserve_keyword_label:
mov eax, ecx ; Fix return
cmp eax, 58 ; Check for ':'
jne preserve_keyword_done ; be done
;; Fix our goto label
call fixup_label ; Fix the label
mov eax, 32 ; Return Whitespace
preserve_keyword_done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
mov ecx, eax ; Setup C
mov ebx, symbols ; Concerning ourselves with "<=>.."
preserve_symbol_loop:
call In_Set ; Check if alphanumerics
cmp eax, 1 ; IF TRUE
jne preserve_symbol_done ; Otherwise be done
mov eax, ecx ; Pass C
call consume_byte ; consume that byte
mov ecx, eax ; Update C
jmp preserve_symbol_loop ; keep looping
preserve_symbol_done:
mov eax, ecx ; Fix return
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
mov ebx, eax ; FREQ = C
mov ecx, 0 ; ESCAPE = FALSE
consume_word_loop:
cmp ecx, 0 ; IF !ESCAPE
jne consume_word_escape ; Enable escape
cmp eax, 92 ; if '\\'
jne consume_word_iter ; keep state
mov ecx, 1 ; ESCAPE = TRUE
jmp consume_word_iter ; keep going
consume_word_escape:
mov ecx, 0 ; ESCAPE = FALSE
consume_word_iter:
call consume_byte ; read next char
cmp ecx, 0 ; IF ESCAPE
jne consume_word_loop ; keep looping
cmp eax, ebx ; IF C != FREQ
jne consume_word_loop ; keep going
call fgetc ; return next char
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
mov ebx, [string_index] ; S[0]
mov [ebx], al ; S[0] = C
add ebx, 1 ; S = S + 1
mov [string_index], ebx ; Update S
call fgetc
pop ebx ; Restore EBX
ret
;; 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
mov eax, 58 ; HOLD = ':'
mov ecx, [edx+8] ; HOLD_STRING[0]
fixup_label_loop:
mov ebx, eax ; PREV = HOLD
mov al, [ecx] ; HOLD = HOLD_STRING[I]
movzx eax, al ; make useful
mov [ecx], bl ; HOLD_STRING[I] = PREV
add ecx, 1 ; I = I + 1
cmp eax, 0 ; IF NULL == HOLD
jne fixup_label_loop ; Keep looping
pop ecx ; Restore ECX
pop ebx ; Restore EBX
pop eax ; Restore EAX
ret
;; 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
mov eax, -4 ; Put EOF in eax
push eax ; Assume bad (If nothing read, value will remain EOF)
lea ecx, [esp] ; Get stack address
mov ebx, [Input_file] ; Where are we reading from
mov eax, 3 ; the syscall number for read
mov edx, 1 ; set the size of chars we want
int 0x80 ; call the Kernel
pop eax ; Get either char or EOF
cmp eax, -4 ; Check for EOF
je fgetc_done ; Return as is
movzx eax, al ; Make it useful
fgetc_done:
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; Reverse_List function
;; Receives List in EAX
;; Returns the list reversed in EAX
Reverse_List:
push ebx ; Protect EBX
push ecx ; Protect ECX
mov ebx, eax ; Set HEAD
mov eax, 0 ; ROOT = NULL
Reverse_List_Loop:
cmp ebx, 0 ; WHILE HEAD != NULL
je Reverse_List_Done ; Stop otherwise
mov ecx, [ebx] ; NEXT = HEAD->NEXT
mov [ebx], eax ; HEAD->NEXT = ROOT
mov eax, ebx ; ROOT = HEAD
mov ebx, ecx ; HEAD = NEXT
jmp Reverse_List_Loop ; Keep Going
Reverse_List_Done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
cmp eax, 0 ; Check for NULL
je recursive_output_done ; Skip the work
mov ebx, eax ; I = Head
mov eax, [ebx] ; Iterate to next Token
call recursive_output ; Recurse
mov eax, [ebx+8] ; Using S
call File_Print ; Print it
recursive_output_done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; File_Print function
;; Receives CHAR* in EAX
;; calls fputc for every non-null char
File_Print:
push ebx ; Protect EBX
push ecx ; Protect ECX
mov ebx, eax ; Protect S
cmp eax, 0 ; Protect against nulls
je File_Print_Done ; Simply don't try to print them
File_Print_Loop:
mov al, [ebx] ; Read byte
movzx eax, al ; zero extend
cmp eax, 0 ; Check for NULL
je File_Print_Done ; Stop at NULL
call fputc ; write it
add ebx, 1 ; S = S + 1
jmp File_Print_Loop ; Keep going
File_Print_Done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
lea ecx, [esp] ; Get stack address
mov ebx, [Output_file] ; Write to target file
mov eax, 4 ; the syscall number for write
mov edx, 1 ; set the size of chars we want
int 0x80 ; call the Kernel
pop eax ; Restore stack
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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:
mov eax, [global_token] ; Using global_token
cmp eax, 0 ; Check if NULL
je program_done ; Be done if null
mov ebx, [eax+8] ; GLOBAL_TOKEN->S
mov eax, constant ; "CONSTANT"
call match ; IF GLOBAL_TOKEN->S == "CONSTANT"
cmp eax, 0 ; If true
jne program_else ; Looks like not a constant
;; Deal with minimal constant case
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
mov eax, [eax+8] ; global_token->S
mov ebx, 0 ; NULL
mov ecx, [global_constant_list] ; global_constant_list
call sym_declare ; Declare that constant
mov [global_constant_list], eax ; global_constant_list = sym_declare(global_token->s, NULL, global_constant_list);
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx] ; global_token->next
mov [eax+16], ebx ; global_constant_list->arguments = global_token->next
mov ebx, [ebx] ; global_token->next->next
mov [global_token], ebx ; global_token = global_token->next->next;
jmp new_type ; go around again
program_else:
call type_name ; Figure out the type_size
cmp eax, 0 ; IF NULL == type_size
je new_type ; it was a new type
;; Add to global symbol table
mov ebx, eax ; put type_size in the right spot
mov eax, [global_token] ; Using global token
mov eax, [eax+8] ; global_token->S
mov ecx, [global_symbol_list] ; Using global_symbol_list
call sym_declare ; Declare symbol
mov [global_symbol_list], eax ; global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list);
mov ebx, [global_token] ; Using global token
mov ebx, [ebx] ; global_token->next
mov [global_token], ebx ; global_token = global_token->next
mov ebx, [global_token] ; Using global token
mov ebx, [ebx+8] ; global_token->S
mov eax, semicolon ; ";"
call match ; if(match(";", global_token->s))
cmp eax, 0 ; If true
jne program_function ; looks like not a match
;; Deal with the global variable
mov ebx, [globals_list] ; Using globals_list
mov eax, program_string_0 ; ":GLOBAL_"
call emit ; Emit it
mov ebx, eax ; update globals_list
mov eax, [global_token] ; Using global token
mov eax, [eax+4] ; global token->prev
mov eax, [eax+8] ; global token->prev->s
call emit ; Emit it
mov ebx, eax ; update globals_list
mov eax, program_string_1 ; "\nNOP\n"
call emit ; Emit it
mov [globals_list], eax ; update globals_list
mov eax, [global_token] ; Using global token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
jmp new_type ; go around again
program_function:
mov ebx, [global_token] ; Using global token
mov ebx, [ebx+8] ; global_token->S
mov eax, open_paren ; "("
call match ; if(match(";", global_token->s))
cmp eax, 0 ; If true
jne program_error ; Otherwise deal with error case
;; Deal with function definition
call declare_function ; Lets get the parsing rolling
jmp new_type ; Keep looping through functions
program_error:
;; Deal with the case of something we don't support
program_done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; Strings needed by the program function
program_string_0: db ":GLOBAL_", 0
program_string_1: db 10, "NOP", 10, 0
;; 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
mov eax, 0 ; Using NULL
mov [current_count], eax ; current_count = 0
mov eax, [global_token] ; Using global token
mov eax, [eax+4] ; global token->prev
mov eax, [eax+8] ; global token->prev->s
mov ebx, 0 ; NULL
mov ecx, [global_function_list] ; global_function_list
call sym_declare ; sym_declare(global_token->prev->s, NULL, global_function_list);
mov [function], eax ; function = sym_declare(global_token->prev->s, NULL, global_function_list);
mov [global_function_list], eax ; global_function_list = function
call collect_arguments ; collect all of the function arguments
mov eax, [global_token] ; Using global token
mov eax, [eax+8] ; global token->s
mov ebx, semicolon ; ";"
call match ; IF global token->s == ";"
cmp eax, 0 ; If true
jne declare_function_full ; It was a prototype
;; Deal with prototypes
mov eax, [global_token] ; Using global token
mov eax, [eax] ; global token->next
mov [global_token], eax ; global token = global token->next
jmp declare_function_done ; Move on
declare_function_full:
;; Deal will full function definitions
mov eax, declare_function_string_0 ; "# Defining function "
call emit_out ; emit it
mov eax, [function] ; function
mov eax, [eax+8] ; function->s
call emit_out ; emit it
mov eax, declare_function_string_1 ; "\n:FUNCTION_"
call emit_out ; emit it
mov eax, [function] ; function
mov eax, [eax+8] ; function->s
call emit_out ; emit it
mov eax, declare_function_string_3 ; "\n"
call emit_out ; emit it
call statement ; Recursively get the function pieces
mov eax, [output_list] ; output
mov eax, [eax+8] ; output->s
mov ebx, declare_function_string_2 ; "RETURN\n"
call match ; IF output->s == "RETURN\n"
cmp eax, 0 ; If true we can skip adding it
je declare_function_done ; otherwise we need to add it
;; Add the return to the end of a function lacking a return;
mov eax, declare_function_string_2 ; "RETURN\n"
call emit_out ; emit it
declare_function_done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
declare_function_string_0: db 35, " Defining function ", 0
declare_function_string_1: db 10, ":FUNCTION_", 0
declare_function_string_2: db "RETURN", 10, 0
declare_function_string_3: db 10, 0
;; 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
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
collect_arguments_loop:
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, close_paren ; ")"
call match ; IF global_token->S == ")"
cmp eax, 0 ; we reached the end
je collect_arguments_done ; be done
;; deal with the case of there are arguments
call type_name ; Get the type
mov ecx, eax ; put type_size safely out of the way
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, close_paren ; ")"
call match ; IF global_token->S == ")"
cmp eax, 0 ; is a foo(int, char,void) case
je collect_arguments_common ; deal with commas
;; Trying second else
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, comma ; ","
call match ; IF global_token->S == ","
cmp eax, 0 ; then deal with the common
je collect_arguments_common ; case of commas between arguments
;; deal with foo(int a, char b)
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov ebx, ecx ; put type_size in the right place
mov ecx, [function] ; Using function
mov ecx, [ecx+16] ; function->args
call sym_declare ; sym_declare(global_token->s, type_size, function->arguments);
mov ecx, eax ; put a in a safe place
mov eax, [function] ; Using function
mov eax, [eax+16] ; function->args
cmp eax, 0 ; IF function->args == NULL
jne collect_arguments_another ; otherwise it isn't the first
;; Deal with the case of first argument in the function
mov eax, -4 ; -4
mov [ecx+16], eax ; a->depth = -4
jmp collect_arguments_next ; get to next
collect_arguments_another:
;; deal with the case of non-first arguments
mov eax, [function] ; Using function
mov eax, [eax+16] ; function->args
mov eax, [eax+16] ; function->args->depth
sub eax, 4 ; function->args->depth - 4
mov [ecx+16], eax ; a->depth = function->args->depth - 4
collect_arguments_next:
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
mov eax, [function] ; Using function
mov [eax+16], ecx ; function->args = a
collect_arguments_common:
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, comma ; ","
call match ; IF global_token->S == ","
cmp eax, 0 ; then deal with the comma
jne collect_arguments_loop ; otherwise loop
;; keep foo(bar(), 1) expressions working
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
jmp collect_arguments_loop ; keep going
collect_arguments_done:
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, open_curly_brace ; "{"
call match ; IF global_token->S == "{"
jne statement_label ; otherwise try label
;; deal with { statement }
call recursive_statement ; Statements inside of statements for days
jmp statement_done ; Be done
statement_label:
mov al, [ebx] ; global_token->S[0]
movzx eax, al ; make it useful
cmp eax, 58 ; IF global_token->S == ':'
jne statement_local ; otherwise try locals
;; deal with labels
mov eax, ebx ; put global_token->S in the right spot
call emit_out ; emit it
mov eax, statement_string_0 ; Using "\t#C goto label\n"
call emit_out ; emit it
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
jmp statement_done ; be done
statement_local:
mov eax, ebx ; put global_token->S in the right place
mov ebx, prim_types ; pointer to primative types
call lookup_type ; See if found
cmp eax, 0 ; IF NULL == lookup_type(global_token->S, prim_types)
jne statement_local_success ; Sweet a new local
;; Second chance
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, struct ; "struct"
call match ; IF global_token->S == "struct"
cmp eax, 0 ; then we are a local
jne statement_if ; otherwise try IF
statement_local_success:
call collect_local ; Grab those locals
jmp statement_done ; be done
statement_if:
mov eax, if_string ; Using "if"
call match ; IF global_token->S == "if"
cmp eax, 0 ; then we have an if statement
jne statement_do ; otherwise try DO
;; Deal with IF statement
call process_if ; DO IT
jmp statement_done ; be done
statement_do:
mov eax, do_string ; Using "do
call match ; IF global_token->S == "do"
cmp eax, 0 ; then we have a do statement
jne statement_while ; otherwise try WHILE
;; Deal with DO statement
call process_do ; DO IT
jmp statement_done ; be done
statement_while:
mov eax, while_string ; Using "while"
call match ; IF global_token->S == "while"
cmp eax, 0 ; then we have a while statement
jne statement_for ; otherwise try FOR
;; Deal with WHILE statement
call process_while ; DO IT
jmp statement_done ; be done
statement_for:
mov eax, for_string ; Using "for"
call match ; IF global_token->S == "for"
cmp eax, 0 ; then we have a for statement
jne statement_asm ; otherwise try ASM
;; Deal with FOR statement
call process_for ; DO IT
jmp statement_done ; be done
statement_asm:
mov eax, asm_string ; Using "asm"
call match ; IF global_token->S == "asm"
cmp eax, 0 ; then we have an asm statement
jne statement_goto ; otherwise try GOTO
;; Deal with ASM statement
call process_asm ; Hit it
jmp statement_done ; be done
statement_goto:
mov eax, goto_string ; Using "goto"
call match ; IF global_token->S == "goto"
cmp eax, 0 ; then we have a goto statement
jne statement_return ; Otherwise try RETURN
;; Deal with GOTO statement
mov eax, statement_string_1 ; Using "JUMP %"
call emit_out ; emit it
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
mov eax, [eax+8] ; global_token->S
call emit_out ; emit it
mov eax, statement_string_2 ; Using "\n"
call emit_out ; emit it
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
mov eax, statement_string_4 ; Using "ERROR in statement\nMissing ;\n"
mov ebx, semicolon ; Using ";"
call require_match ; Make sure it has the required
jmp statement_done ; Be done
statement_return:
mov eax, return_string ; Using "return"
call match ; IF global_token->S == "return"
cmp eax, 0 ; then we have a return statement
jne statement_break ; Otherwise try BREAK
;; Deal with RETURN Statement
call return_result ; Return anything they want
jmp statement_done ; be done
statement_break:
mov eax, break_string ; Using "break"
call match ; IF global_token->S == "break"
cmp eax, 0 ; then we have a break statement
jne statement_continue ; Otherwise try CONTINUE
;; Deal with BREAK statement
call process_break ; Lets do some damage
jmp statement_done ; be done
statement_continue:
mov eax, continue_string ; Using "continue"
call match ; IF global_token->S == "continue"
cmp eax, 0 ; then we have a continue statement
jne statement_else ; Otherwise we are punting to an expression
;; Deal with CONTINUE statement
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
mov eax, statement_string_3 ; Using "\n#continue statement\n"
call emit_out ; emit it
mov eax, statement_string_4 ; Using "ERROR in statement\nMissing ;\n"
mov ebx, semicolon ; Using ";"
call require_match ; Don't forget the ";"
jmp statement_done ; Be done
statement_else:
call expression ; Collect expression
mov eax, statement_string_4 ; Using "ERROR in statement\nMissing ;\n"
mov ebx, semicolon ; Using ";"
call require_match ; make sure we have it
statement_done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
statement_string_0: db 9, "#C goto label", 10, 0
statement_string_1: db "JUMP %", 0
statement_string_2: db 10, 0
statement_string_3: db 10, "#continue statement", 10, 0
statement_string_4: db "ERROR in statement", 10, "Missing ;", 10, 0
;; 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
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
mov ecx, [function] ; Using function
mov ecx, [ecx+4] ; frame = function->locals
recursive_statement_loop:
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, close_curly_brace ; Using "}"
call match ; IF global_token->S == "}"
cmp eax, 0 ; Then we are done recuring
je recursive_statement_cleanup ; and then we clean up
;; Deal with the recursive calls
call statement ; Deal with another statement
jmp recursive_statement_loop ; loop some more
recursive_statement_cleanup:
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
mov eax, recursive_statement_string_0 ; Using "RETURN\n"
mov ebx, [output_list] ; Using output
mov ebx, [ebx+8] ; output->S
call match ; IF output->S == "RETURN\n"
cmp eax, 0 ; Then we can skip the clean up
je recursive_statement_done ; and be done
;; Deal with cleanup
mov ebx, [function] ; Using function
mov ebx, [ebx+4] ; i = function->locals
mov eax, recursive_statement_string_1 ; Using "POP_ebx\t# _recursive_statement_locals\n"
recursive_statement_locals:
cmp ebx, ecx ; IF frame != i
je recursive_statement_done ; Otherwise be done
;; Lets emit
call emit_out ; emit it
mov ebx, [ebx] ; i = i->next
jmp recursive_statement_locals ; keep going
recursive_statement_done:
mov eax, [function] ; Using function
mov [eax+4], ecx ; function->locals = frame
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
recursive_statement_string_0: db "RETURN", 10, 0
recursive_statement_string_1: db "POP_ebx", 9, "# _recursive_statement_locals", 10, 0
;; 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
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; make it useful
cmp eax, 59 ; If global_token->S[0] == ';'
je return_result_cleanup ; Go straight to cleanup
call expression ; get the expression we are returning
return_result_cleanup:
mov eax, return_result_string_0 ; Using "ERROR in return_result\nMISSING ;\n"
mov ebx, semicolon ; Using ";"
call require_match ; Make sure we have it
mov ebx, [function] ; Using function
mov ebx, [ebx+4] ; function->locals
mov eax, return_result_string_1 ; Using "POP_ebx\t# _return_result_locals\n"
return_result_locals:
cmp ebx, 0 ; IF NULL == i
je return_result_done ; Be done
call emit_out ; Emit out pop
mov ebx, [ebx] ; i = i->NEXT
jmp return_result_locals ; Keep going
return_result_done:
mov eax, return_result_string_2 ; Using "RETURN\n"
call emit_out ; Emit it
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
return_result_string_0: db "ERROR in return_result", 10, "MISSING ;", 10, 0
return_result_string_1: db "POP_ebx", 9, "# _return_result_locals", 10, 0
return_result_string_2: db "RETURN", 10, 0
;; 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
call type_name ; Get the local's type
mov ebx, eax ; Put struct type* type_size in the right place
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov ecx, [function] ; Using function
mov ecx, [ecx+4] ; function->locals
call sym_declare ; Declare it
mov ecx, eax ; put it away safely
;; Try for main
mov eax, main_string ; Using "main"
mov ebx, [function] ; Using function
mov ebx, [ebx+8] ; function->S
call match ; IF match("main", function->s)
cmp eax, 0 ; possible
jne collect_local_fresh ; try to see if fresh function
;; Ok we are in main, now to see if main is fresh
mov eax, [function] ; Using function
mov eax, [eax+4] ; function->locals
cmp eax, 0 ; IF NULL == function->locals
jne collect_local_fresh ; try to see if fresh function
;; Sweet we are in a fresh main
mov eax, -20 ; We start at -20
mov [ecx+16], eax ; a->DEPTH = -20
jmp collect_local_common ; Go to the commons
collect_local_fresh:
mov eax, [function] ; Using function
mov eax, [eax+16] ; function->args
cmp eax, 0 ; IF NULL == function->locals
jne collect_local_first ; Otherwise see if first
mov eax, [function] ; Using function
mov eax, [eax+4] ; function->locals
cmp eax, 0 ; IF NULL == function->locals
jne collect_local_first ; Otherwise try first
;; Sweet we are in a fresh function
mov eax, -8 ; We start at -8
mov [ecx+16], eax ; a->DEPTH = -8
jmp collect_local_common ; Go to the commons
collect_local_first:
mov eax, [function] ; Using function
mov eax, [eax+4] ; function->locals
cmp eax, 0 ; IF NULL == function->locals
jne collect_local_else ; Looks like we are just another local
;; Ok we are the first local
mov eax, [function] ; Using function
mov eax, [eax+16] ; function->args
mov eax, [eax+16] ; function->args->depth
sub eax, 8 ; function->arguments->depth - 8
mov [ecx+16], eax ; a->DEPTH = function->arguments->depth - 8
jmp collect_local_common ; Go to the commons
collect_local_else:
;; Always the last to know
mov eax, [function] ; Using function
mov eax, [eax+4] ; function->locals
mov eax, [eax+16] ; function->locals->depth
sub eax, 4 ; function->locals->depth - 4
mov [ecx+16], eax ; a->DEPTH = function->locals->depth - 4
collect_local_common:
mov eax, [function] ; Using function
mov [eax+4], ecx ; function->locals = a
mov ecx, [ecx+8] ; a->S
mov eax, collect_local_string_0 ; Using "# Defining local "
call emit_out ; emit it
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
call emit_out ; emit it
mov eax, collect_local_string_1 ; Using "\n"
call emit_out ; emit it
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov ebx, [eax+8] ; global_token->S
mov eax, equal ; Using "="
call match ; IF match("=", global_token->s)
cmp eax, 0 ; Deal with assignment
jne collect_local_done ; Otherwise finish it
;; Deal with assignment
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
call expression ; Recurse
collect_local_done:
mov eax, collect_local_string_2 ; Using "ERROR in collect_local\nMissing ;\n"
mov ebx, semicolon ; Using ";"
call require_match ; Make sure we have it
mov eax, collect_local_string_3 ; Using "PUSH_eax\t#"
call emit_out ; emit it
mov eax, ecx ; put A->S where it belongs
call emit_out ; emit it
mov eax, collect_local_string_1 ; Using "\n"
call emit_out ; emit it
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
collect_local_string_0: db "# Defining local ", 0
collect_local_string_1: db 10, 0
collect_local_string_2: db "ERROR in collect_local", 10, "Missing ;", 10, 0
collect_local_string_3: db "PUSH_eax", 9, "#", 0
;; 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
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, process_asm_string_0 ; Using "ERROR in process_asm\nMISSING (\n"
mov ebx, open_paren ; Using "("
call require_match ; Make sure we have it
mov ebx, [global_token] ; Using global_token
process_asm_iter:
mov eax, [ebx+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; Make it useful
cmp eax, 34 ; IF global_token->S[0] == '"'
jne process_asm_done ; Otherwise be done
mov eax, [ebx+8] ; global_token->S
add eax, 1 ; global_token->S + 1
call emit_out ; Emit it
mov eax, process_asm_string_1 ; Using "\n"
call emit_out ; Emit it
mov ebx, [ebx] ; global_token->NEXT
mov [global_token], ebx ; global_token = global_token->NEXT
jmp process_asm_iter ; keep going
process_asm_done:
mov eax, process_asm_string_2 ; Using "ERROR in process_asm\nMISSING )\n"
mov ebx, close_paren ; Using ")"
call require_match ; Make sure we have it
mov eax, process_asm_string_3 ; Using "ERROR in process_asm\nMISSING ;\n"
mov ebx, semicolon ; Using ";"
call require_match ; Make sure we have it
pop ebx ; Restore EBX
ret
process_asm_string_0: db "ERROR in process_asm", 10, "MISSING (", 10, 0
process_asm_string_1: db 10, 0
process_asm_string_2: db "ERROR in process_asm", 10, "MISSING )", 10, 0
process_asm_string_3: db "ERROR in process_asm", 10, "MISSING ;", 10, 0
;; 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
mov eax, [current_count] ; Using current count
mov ebx, eax ; Preparing for update
add ebx, 1 ; current_count + 1
mov [current_count], ebx ; current_count = current_count + 1
call numerate_number ; convert to string
mov ecx, eax ; put NUMBER_STRING in place
mov eax, process_if_string_0 ; Using "# IF_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, process_if_string_1 ; Using "ERROR in process_if\nMISSING (\n"
mov ebx, open_paren ; Using "("
call require_match ; Make sure we have it
call expression ; Recurse to get the IF(...) part
mov eax, process_if_string_2 ; Using "TEST\nJUMP_EQ %ELSE_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_if_string_3 ; Using "ERROR in process_if\nMISSING )\n"
mov ebx, close_paren ; Using ")"
call require_match ; Make sure we have it
call statement ; Recursive to get the IF(){...} part
mov eax, process_if_string_4 ; Using "JUMP %_END_IF_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_if_string_5 ; Using ":ELSE_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, else_string ; Using "else"
call match ; IF global_token->S == "else"
cmp eax, 0 ; Then we need to collect the else too
jne process_if_done ; Otherwise finish up
;; deal with else statement
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
call statement ; Recurse to get the ELSE {...} part
process_if_done:
mov eax, process_if_string_6 ; Using ":_END_IF_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
process_if_string_0: db "# IF_",0
process_if_string_1: db "ERROR in process_if", 10, "MISSING (", 10, 0
process_if_string_2: db "TEST", 10, "JUMP_EQ %ELSE_", 0
process_if_string_3: db "ERROR in process_if", 10, "MISSING )", 10, 0
process_if_string_4: db "JUMP %_END_IF_", 0
process_if_string_5: db ":ELSE_", 0
process_if_string_6: db ":_END_IF_", 0
;; save_break_frame microfunction
;; Overwrites EAX abd EBX
;; Saves break frame on stack
;; Returns to caller
save_break_frame:
pop ebx ; Save return Address
mov eax, [break_frame] ; Get break_frame
push eax ; Store as nested_locals
mov eax, [break_target_head] ; Get break_target_head
push eax ; Store as nested_break_head
mov eax, [break_target_func] ; Get break_target_func
push eax ; Store as nested_break_func
mov eax, [break_target_num] ; Get break_target_num
push eax ; Store as nested_break_num
push ebx ; Put return back in place
ret ; 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
mov [break_target_num], eax ; Restore break_target_num
pop eax ; Get nested_break_func
mov [break_target_func], eax ; Restore break_target_func
pop eax ; Get nested_break_head
mov [break_target_head], eax ; Restore break_target_head
pop eax ; Get nested_locals
mov [break_frame], eax ; Restore break_frame
push ebx ; Put return back in place
ret ; 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:
mov [break_target_head], eax ; update break_target_head
mov [break_target_num], ebx ; update break_target_num
mov ebx, [function] ; Using function
mov eax, [ebx+4] ; function->LOCALS
mov [break_frame], eax ; break_frame = function->LOCALS
mov eax, [ebx+8] ; function->S
mov [break_target_func], eax ; break_target_func = function->S
ret ; 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
call save_break_frame ; Save the frame
mov eax, [current_count] ; Using current count
mov ebx, eax ; Preparing for update
add ebx, 1 ; current_count + 1
mov [current_count], ebx ; current_count = current_count + 1
call numerate_number ; convert to string
mov ecx, eax ; put NUMBER_STRING in place
mov eax, process_do_string_0 ; Using "DO_END_"
mov ebx, ecx ; Passing NUMBER_STRING
call set_break_frame ; Set the frame
mov eax, process_do_string_1 ; Using ":DO_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
call statement ; Do the DO {...} part
mov eax, process_do_string_2 ; Using "ERROR in process_do\nMISSING while\n"
mov ebx, while_string ; Using "while"
call require_match ; Make sure we have it
mov eax, process_do_string_3 ; Using "ERROR in process_do\nMISSING (\n"
mov ebx, open_paren ; Using "("
call require_match ; Make sure we have it
call expression ; Do the WHILE (...) part
mov eax, process_do_string_4 ; Using "ERROR in process_do\nMISSING )\n"
mov ebx, close_paren ; Using ")"
call require_match ; Make sure we have it
mov eax, process_do_string_5 ; Using "ERROR in process_do\nMISSING ;\n"
mov ebx, semicolon ; Using ";"
call require_match ; Make sure we have it
mov eax, process_do_string_6 ; Using "TEST\nJUMP_NE %DO_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_do_string_7 ; Using ":DO_END_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
call uniqueID_out ; uniqueID_out(function->s, number_string)
call restore_break_frame ; Restore the old break frame
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
process_do_string_0: db "DO_END_", 0
process_do_string_1: db ":DO_", 0
process_do_string_2: db "ERROR in process_do", 10, "MISSING while", 10, 0
process_do_string_3: db "ERROR in process_do", 10, "MISSING (", 10, 0
process_do_string_4: db "ERROR in process_do", 10, "MISSING )", 10, 0
process_do_string_5: db "ERROR in process_do", 10, "MISSING ;", 10, 0
process_do_string_6: db "TEST", 10, "JUMP_NE %DO_", 0
process_do_string_7: db ":DO_END_", 0
;; 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
call save_break_frame ; Save break_frame
mov eax, [current_count] ; Using current count
mov ebx, eax ; Preparing for update
add ebx, 1 ; current_count + 1
mov [current_count], ebx ; current_count = current_count + 1
call numerate_number ; convert to string
mov ecx, eax ; put NUMBER_STRING in place
mov eax, process_while_string_0 ; Using "END_WHILE_"
mov ebx, ecx ; Passing NUMBER_STRING
call set_break_frame ; Set it and forget it
mov eax, process_while_string_1 ; Using ":WHILE_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, process_while_string_2 ; Using "ERROR in process_while\nMISSING (\n"
mov ebx, open_paren ; Using "("
call require_match ; Make sure we have it
call expression ; Deal with the WHILE (...) part
mov eax, process_while_string_3 ; Using "TEST\nJUMP_EQ %END_WHILE_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_while_string_4 ; Using "# THEN_while_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_while_string_5 ; Using "ERROR in process_while\nMISSING )\n"
mov ebx, close_paren ; Using ")"
call require_match ; Make sure we have it
call statement ; Deal with the {....} part
mov eax, process_while_string_6 ; Using "JUMP %WHILE_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_while_string_7 ; Using ":END_WHILE_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
call uniqueID_out ; uniqueID_out(function->s, number_string)
call restore_break_frame ; Restore the old break frame
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
process_while_string_0: db "END_WHILE_", 0
process_while_string_1: db ":WHILE_", 0
process_while_string_2: db "ERROR in process_while", 10, "MISSING (", 10, 0
process_while_string_3: db "TEST", 10, "JUMP_EQ %END_WHILE_", 0
process_while_string_4: db "# THEN_while_", 0
process_while_string_5: db "ERROR in process_while", 10, "MISSING )", 10, 0
process_while_string_6: db "JUMP %WHILE_", 0
process_while_string_7: db ":END_WHILE_", 0
;; 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
call save_break_frame ; Save the frame
mov eax, [current_count] ; Using current count
mov ebx, eax ; Preparing for update
add ebx, 1 ; current_count + 1
mov [current_count], ebx ; current_count = current_count + 1
call numerate_number ; convert to string
mov ecx, eax ; put NUMBER_STRING in place
mov eax, process_for_string_0 ; Using "FOR_END_"
mov ebx, ecx ; Passing NUMBER_STRING
call set_break_frame ; Set it and forget it
mov eax, process_for_string_1 ; Using "# FOR_initialization_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, process_for_string_2 ; Using "ERROR in process_for\nMISSING (\n"
mov ebx, open_paren ; Using "("
call require_match ; Make Sure we have it
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, semicolon ; Using ";"
call match ; IF global_token->S == ";"
cmp eax, 0 ; Then no initializer
je process_for_terminator ; And skip getting the expression
;; Deal with FOR (...; case
call expression ; Get the FOR ( ... ; part
process_for_terminator:
mov eax, process_for_string_3 ; Using ":FOR_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_for_string_4 ; Using "ERROR in process_for\nMISSING ;1\n"
mov ebx, semicolon ; Using ";"
call require_match ; Make sure we have it
call expression ; Get the FOR ( ; ... ; Part
mov eax, process_for_string_5 ; Using "TEST\nJUMP_EQ %FOR_END_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_for_string_6 ; Using "JUMP %FOR_THEN_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_for_string_7 ; Using ":FOR_ITER_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_for_string_8 ; Using "ERROR in process_for\nMISSING ;2\n"
mov ebx, semicolon ; Using ";"
call require_match ; Make sure we have it
call expression ; Get the FOR (;;...) part
mov eax, process_for_string_9 ; Using "JUMP %FOR_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_for_string_10 ; Using ":FOR_THEN_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_for_string_11 ; Using "ERROR in process_for\nMISSING )\n"
mov ebx, close_paren ; Using ")"
call require_match ; Make sure we have it
call statement ; Get FOR (;;) {...} part
mov eax, process_for_string_12 ; Using "JUMP %FOR_ITER_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Passing NUMBER_STRING
call uniqueID_out ; uniqueID_out(function->s, number_string)
mov eax, process_for_string_13 ; Using ":FOR_END_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
call uniqueID_out ; uniqueID_out(function->s, number_string)
call restore_break_frame ; Restore the old break frame
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
process_for_string_0: db "FOR_END_", 0
process_for_string_1: db "# FOR_initialization_", 0
process_for_string_2: db "ERROR in process_for", 10, "MISSING (", 10, 0
process_for_string_3: db ":FOR_", 0
process_for_string_4: db "ERROR in process_for", 10, "MISSING ;1", 10, 0
process_for_string_5: db "TEST", 10, "JUMP_EQ %FOR_END_", 0
process_for_string_6: db "JUMP %FOR_THEN_", 0
process_for_string_7: db ":FOR_ITER_", 0
process_for_string_8: db "ERROR in process_for", 10, "MISSING ;2", 10, 0
process_for_string_9: db "JUMP %FOR_", 0
process_for_string_10: db ":FOR_THEN_", 0
process_for_string_11: db "ERROR in process_for",10, "MISSING )", 10, 0
process_for_string_12: db "JUMP %FOR_ITER_", 0
process_for_string_13: db ":FOR_END_", 0
;; 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
mov eax, [break_target_head] ; Catch big error
cmp eax, 0 ; IF(NULL == break_target_head)
je process_break_bad ; I'm sorry Mr White but you have stage-3 lung cancer
mov eax, [function] ; Using function
mov ecx, [eax+4] ; I = function->LOCALS
mov ebx, [break_frame] ; Put break_frame in the right spot
mov eax, process_break_string_1 ; Using "POP_ebx\t# break_cleanup_locals\n"
process_break_iter:
cmp ecx, 0 ; IF (NULL == I)
je process_break_cleaned ; We are done
cmp ecx, ebx ; IF I != break_frame
je process_break_cleaned ; We are done
call emit_out ; Emit it
mov ecx, [ecx] ; I = I->NEXT
jmp process_break_iter ; Keep looping
process_break_cleaned:
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, process_break_string_2 ; Using "JUMP %"
call emit_out ; Emit it
mov eax, [break_target_head] ; Get what we are in
call emit_out ; Emit it
mov eax, [break_target_func] ; Get what function we are in
call emit_out ; Emit it
mov eax, underline ; Using "_"
call emit_out ; Emit it
mov eax, [break_target_num] ; Get dem digits
call emit_out ; Emit it
mov eax, process_break_string_3 ; Using "\n"
call emit_out ; Emit it
mov eax, process_break_string_4 ; Using "ERROR in break statement\nMissing ;\n"
mov ebx, semicolon ; Using ";"
call require_match ; Make sure we have it
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
process_break_bad:
;; Breaking badly
mov eax, 2 ; Using standard error
mov [Output_file], eax ; write to standard error
; call line_error ; Write useful debug info
mov eax, ecx ; put S in the right place
call File_Print ; print it
mov eax, process_break_string_0 ; Ending string
call File_Print ; print it
jmp Exit_Failure ; Abort Hard
process_break_string_0: db "Not inside of a loop or case statement", 0
process_break_string_1: db "POP_ebx", 9, "# break_cleanup_locals", 10, 0
process_break_string_2: db "JUMP %", 0
process_break_string_3: db 10, 0
process_break_string_4: db "ERROR in break statement",10, "Missing ;", 10, 0
;; 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
call bitwise_expr ; Collect bitwise expressions
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, equal ; "="
call match ; IF global_token->S == "="
cmp eax, 0 ; We have to deal with assignment
jne expression_done ; Looks like nope
;; Deal with possible assignment
mov ecx, expression_string_1 ; Assume "STORE_CHAR\n" by default
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+4] ; global_token->PREV
mov ebx, [ebx+8] ; global_token->PREV->S
mov eax, close_bracket ; Using "]"
call match ; IF global_token->S == "]"
cmp eax, 0 ; Then we might have a char
jne expression_int ; Otherwise INT
mov ebx, [current_target] ; Using current_target
mov ebx, [ebx+24] ; current_target->NAME
mov eax, type_char_indirect_name ; Using "char*"
call match ; Intensional inefficency because I feel like it
cmp eax, 0 ; IF current_target->NAME == "char*"
jne expression_int ; Do char anyway
jmp expression_common ; Looks like we have to use "STORE_CHAR\n"
expression_int:
mov ecx, expression_string_0 ; Use "STORE_INTEGER\n"
expression_common:
mov eax, expression ; Passing expression
call common_recursion ; Recurse
mov eax, ecx ; Using Store
call emit_out ; Emit it
mov eax, 0 ; Using NULL
mov [current_target], eax ; current_target = NULL
expression_done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
expression_string_0: db "STORE_INTEGER", 10, 0
expression_string_1: db "STORE_CHAR", 10, 0
;; bitwise_expr function
;; Receives nothing
;; Returns nothing
;; Walks global_token list and updates output list
;; Just calls other functions
bitwise_expr:
call relational_expr ; Walk up the tree
call bitwise_expr_stub ; Let general recursion do the work
ret
;; 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
mov eax, relational_expr ; Using relational_expr
mov ebx, bitwise_expr_stub_string_0 ; Using "AND_eax_ebx\n"
mov ecx, bitwise_and ; Using "&"
mov edx, bitwise_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, relational_expr ; Using relational_expr
mov ebx, bitwise_expr_stub_string_0 ; Using "AND_eax_ebx\n"
mov ecx, logical_and ; Using "&&"
mov edx, bitwise_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, relational_expr ; Using relational_expr
mov ebx, bitwise_expr_stub_string_1 ; Using "OR_eax_ebx\n"
mov ecx, bitwise_or ; Using "|"
mov edx, bitwise_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, relational_expr ; Using relational_expr
mov ebx, bitwise_expr_stub_string_1 ; Using "OR_eax_ebx\n"
mov ecx, logical_or ; Using "||"
mov edx, bitwise_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, relational_expr ; Using relational_expr
mov ebx, bitwise_expr_stub_string_2 ; Using "XOR_ebx_eax_into_eax\n"
mov ecx, bitwise_xor ; Using "^"
mov edx, bitwise_expr_stub ; And recurse
call general_recursion ; Hit it
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
bitwise_expr_stub_string_0: db "AND_eax_ebx", 10, 0
bitwise_expr_stub_string_1: db "OR_eax_ebx", 10, 0
bitwise_expr_stub_string_2: db "XOR_ebx_eax_into_eax", 10, 0
;; relational_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
relational_expr:
call additive_expr ; Walk up the tree
call relational_expr_stub ; Recurse
ret
;; 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
mov eax, additive_expr ; Using additive_expr
mov ebx, relational_expr_stub_string_0 ; Using "CMP\nSETL\nMOVEZBL\n"
mov ecx, less_than_string ; Using "<"
mov edx, relational_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, additive_expr ; Using additive_expr
mov ebx, relational_expr_stub_string_1 ; Using "CMP\nSETLE\nMOVEZBL\n"
mov ecx, less_than_equal_string ; Using "<="
mov edx, relational_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, additive_expr ; Using additive_expr
mov ebx, relational_expr_stub_string_2 ; Using "CMP\nSETGE\nMOVEZBL\n"
mov ecx, greater_than_equal_string ; Using ">="
mov edx, relational_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, additive_expr ; Using additive_expr
mov ebx, relational_expr_stub_string_3 ; Using "CMP\nSETG\nMOVEZBL\n"
mov ecx, greater_than_string ; Using ">"
mov edx, relational_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, additive_expr ; Using additive_expr
mov ebx, relational_expr_stub_string_4 ; Using "CMP\nSETE\nMOVEZBL\n"
mov ecx, equal_to_string ; Using "=="
mov edx, relational_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, additive_expr ; Using additive_expr
mov ebx, relational_expr_stub_string_5 ; Using "CMP\nSETNE\nMOVEZBL\n"
mov ecx, not_equal_string ; Using "!="
mov edx, relational_expr_stub ; And recurse
call general_recursion ; Hit it
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
relational_expr_stub_string_0: db "CMP", 10, "SETL", 10, "MOVEZBL", 10, 0
relational_expr_stub_string_1: db "CMP", 10, "SETLE", 10, "MOVEZBL", 10, 0
relational_expr_stub_string_2: db "CMP", 10, "SETGE", 10, "MOVEZBL", 10, 0
relational_expr_stub_string_3: db "CMP", 10, "SETG", 10, "MOVEZBL", 10, 0
relational_expr_stub_string_4: db "CMP", 10, "SETE", 10, "MOVEZBL", 10, 0
relational_expr_stub_string_5: db "CMP", 10, "SETNE", 10, "MOVEZBL", 10, 0
;; additive_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
additive_expr:
call postfix_expr ; Walk up the tree
call additive_expr_stub ; Recurse
ret
;; 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
mov eax, postfix_expr ; Using postfix_expr
mov ebx, additive_expr_stub_string_0 ; Using "ADD_ebx_to_eax\n"
mov ecx, plus_string ; Using "+"
mov edx, additive_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, postfix_expr ; Using postfix_expr
mov ebx, additive_expr_stub_string_1 ; Using "SUBTRACT_eax_from_ebx_into_ebx\nMOVE_ebx_to_eax\n"
mov ecx, minus_string ; Using "-"
mov edx, additive_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, postfix_expr ; Using postfix_expr
mov ebx, additive_expr_stub_string_2 ; Using "MULTIPLY_eax_by_ebx_into_eax\n"
mov ecx, multiply_string ; Using "*"
mov edx, additive_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, postfix_expr ; Using postfix_expr
mov ebx, additive_expr_stub_string_3 ; Using "XCHG_eax_ebx\nLOAD_IMMEDIATE_edx %0\nDIVIDE_eax_by_ebx_into_eax\n"
mov ecx, divide_string ; Using "/"
mov edx, additive_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, postfix_expr ; Using postfix_expr
mov 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"
mov ecx, modulus_string ; Using "%"
mov edx, additive_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, postfix_expr ; Using postfix_expr
mov ebx, additive_expr_stub_string_5 ; Using "COPY_eax_to_ecx\nCOPY_ebx_to_eax\nSAL_eax_cl\n"
mov ecx, left_shift_string ; Using "<<"
mov edx, additive_expr_stub ; And recurse
call general_recursion ; Hit it
mov eax, postfix_expr ; Using postfix_expr
mov ebx, additive_expr_stub_string_6 ; Using "COPY_eax_to_ecx\nCOPY_ebx_to_eax\nSAR_eax_cl\n"
mov ecx, right_shift_string ; Using ">>"
mov edx, additive_expr_stub ; And recurse
call general_recursion ; Hit it
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
additive_expr_stub_string_0: db "ADD_ebx_to_eax", 10, 0
additive_expr_stub_string_1: db "SUBTRACT_eax_from_ebx_into_ebx", 10, "MOVE_ebx_to_eax", 10, 0
additive_expr_stub_string_2: db "MULTIPLY_eax_by_ebx_into_eax", 10, 0
additive_expr_stub_string_3: db "XCHG_eax_ebx", 10, "LOAD_IMMEDIATE_edx %0", 10, "DIVIDE_eax_by_ebx_into_eax", 10, 0
additive_expr_stub_string_4: db "XCHG_eax_ebx", 10, "LOAD_IMMEDIATE_edx %0", 10, "MODULUS_eax_from_ebx_into_ebx", 10, "MOVE_edx_to_eax", 10, 0
additive_expr_stub_string_5: db "COPY_eax_to_ecx", 10, "COPY_ebx_to_eax", 10, "SAL_eax_cl", 10, 0
additive_expr_stub_string_6: db "COPY_eax_to_ecx", 10, "COPY_ebx_to_eax", 10, "SAR_eax_cl", 10, 0
;; postfix_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
postfix_expr:
call primary_expr ; Walk up the tree
call postfix_expr_stub ; Recurse
ret
;; 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
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, open_bracket ; Using "["
call match ; IF global_token->S == "["
cmp eax, 0 ; then we have an array
jne postfix_expr_stub_arrow ; Otherwise try arrow
;; Deal with array
call postfix_expr_array ; Get it
call postfix_expr_stub ; Recurse
postfix_expr_stub_arrow:
mov eax, arrow_string ; Using "->"
call match ; IF global_token->S == "->"
cmp eax, 0 ; Then we need to deal with struct offsets
jne postfix_expr_stub_done ; Otherwise be done
;; Deal with arrow
call postfix_expr_arrow ; Get it
call postfix_expr_stub ; Recurse
postfix_expr_stub_done:
pop ebx ; Restore EBX
ret
;; unary_expr_sizeof function
;; Receives nothing
;; Returns nothing
;; Uses ECX for A->SIZE
unary_expr_sizeof:
push ebx ; Protect EBX
push ecx ; Protect ECX
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, unary_expr_sizeof_string_0 ; Using "ERROR in unary_expr\nMissing (\n"
mov ebx, open_paren ; Using "("
call require_match ; Make sure we have it
call type_name ; Get the type
mov ecx, [eax+4] ; Set A->TYPE
mov eax, unary_expr_sizeof_string_1 ; Using "ERROR in unary_expr\nMissing )\n"
mov ebx, close_paren ; Using ")"
call require_match ; Make sure we have it
mov eax, unary_expr_sizeof_string_2 ; Using "LOAD_IMMEDIATE_eax %"
call emit_out ; Emit it
mov eax, ecx ; Put A->SIZE in the right place
call numerate_number ; Turn into string
call emit_out ; Emit it
mov eax, unary_expr_sizeof_string_3 ; Using "\n"
call emit_out ; Emit it
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
unary_expr_sizeof_string_0: db "ERROR in unary_expr", 10, "Missing (", 10, 0
unary_expr_sizeof_string_1: db "ERROR in unary_expr", 10, "Missing )", 10, 0
unary_expr_sizeof_string_2: db "LOAD_IMMEDIATE_eax %", 0
unary_expr_sizeof_string_3: db 10, 0
;; 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
mov eax, [current_target] ; ARRAY = current_target
push eax ; Protect it
mov eax, expression ; Using expression
call common_recursion ; Recurse
pop ebx ; Restore array
mov [current_target], ebx ; current_target = ARRAY
mov ecx, postfix_expr_array_string_0 ; ASSIGN = "LOAD_INTEGER\n"
mov eax, type_char_indirect_name ; Using "char*"
mov ebx, [ebx+24] ; current_target->NAME
call match ; IF current_target->NAME == "char*"
cmp eax, 0 ; load a byte
jne postfix_expr_array_large ; Otherwise adjust
;; Deal with loading byte
mov ecx, postfix_expr_array_string_1 ; ASSIGN = "LOAD_BYTE\n"
jmp postfix_expr_array_common ; Do the next bit
postfix_expr_array_large:
;; deal with arrays made of things other than chars
mov eax, postfix_expr_array_string_2 ; Using "SAL_eax_Immediate8 !"
call emit_out ; Emit it
mov eax, [current_target] ; Using current_target
mov eax, [eax+12] ; current_target->INDIRECT
mov eax, [eax+4] ; current_target->INDIRECT->SIZE
call ceil_log2 ; ceil_log2(current_target->indirect->size)
call numerate_number ; numerate_number(ceil_log2(current_target->indirect->size))
call emit_out ; Emit it
mov eax, postfix_expr_array_string_3 ; Using "\n"
call emit_out ; Emit it
postfix_expr_array_common:
mov eax, postfix_expr_array_string_4 ; Using "ADD_ebx_to_eax\n"
call emit_out ; Emit it
mov eax, postfix_expr_array_string_5 ; Using "ERROR in postfix_expr\nMissing ]\n"
mov ebx, close_bracket ; Using "]"
call require_match ; Make sure we have it
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, equal ; Using "="
call match ; IF global_token->S == "="
cmp eax, 0 ; We need to preserve address
jne postfix_expr_array_done ; Otherwise be done
;; Clearing out assign
mov ecx, postfix_expr_array_string_6 ; ASSIGN = ""
postfix_expr_array_done:
mov eax, ecx ; Using ASSIGN
call emit_out ; Emit it
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
postfix_expr_array_string_0: db "LOAD_INTEGER", 10, 0
postfix_expr_array_string_1: db "LOAD_BYTE", 10, 0
postfix_expr_array_string_2: db "SAL_eax_Immediate8 !", 0
postfix_expr_array_string_3: db 10, 0
postfix_expr_array_string_4: db "ADD_ebx_to_eax", 10, 0
postfix_expr_array_string_5: db "ERROR in postfix_expr", 10, "Missing ]", 10, 0
postfix_expr_array_string_6: db 0
;; 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
mov ecx, 0 ; RESULT = 0
mov ebx, eax ; put A in right place
sub eax, 1 ; (A - 1)
and eax, ebx ; A & (A - 1)
cmp eax, 0 ; IF 0 == (A & (A - 1))
jne ceil_log2_iter ; Starting from -1
mov ecx, -1 ; RESULT = -1
ceil_log2_iter:
cmp ebx, 0 ; IF A > 0
jle ceil_log2_done ; Otherwise be done
add ecx, 1 ; RESULT = RESULT + 1
shr ebx, 1 ; A = A >> 1
jmp ceil_log2_iter ; Keep looping
ceil_log2_done:
mov eax, ecx ; Return RESULT
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
mov eax, postfix_expr_arrow_string_0 ; Using "# looking up offset\n"
call emit_out ; Emit it
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov ebx, [eax+8] ; Using global_token->S
mov eax, [current_target] ; Using current_target
call lookup_member ; lookup_member(current_target, global_token->s)
mov ebx, eax ; struct type* I = lookup_member(current_target, global_token->s)
mov eax, [eax+20] ; I->TYPE
mov [current_target], eax ; current_target = I->TYPE
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, [ebx+8] ; I->OFFSET
cmp eax, 0 ; IF 0 != I->OFFSET
je postfix_expr_arrow_first ; Then we don't need to do an offset
;; Deal with needing an offset
mov eax, postfix_expr_arrow_string_1 ; Using "# -> offset calculation\nLOAD_IMMEDIATE_ebx %"
call emit_out ; Emit it
mov eax, [ebx+8] ; I->OFFSET
call numerate_number ; Convert to string
call emit_out ; Emit it
mov eax, postfix_expr_arrow_string_2 ; Using "\nADD_ebx_to_eax\n"
call emit_out ; Emit it
postfix_expr_arrow_first:
mov eax, [ebx+4] ; I->SIZE
cmp eax, 4 ; IF I->SIZE >= 4
jl postfix_expr_arrow_done ; Otherwise be done
;; Last chance for load
mov eax, [global_token] ; Using global_token
mov ebx, [eax+8] ; global_token->S
mov eax, equal ; Using "="
call match ; IF global_token->S == "="
cmp eax, 0 ; Then we have assignment and should not load
je postfix_expr_arrow_done ; Be done
;; Deal with load case
mov eax, postfix_expr_arrow_string_3 ; Using "LOAD_INTEGER\n"
call emit_out ; Emit it
postfix_expr_arrow_done:
pop ebx ; Restore EBX
ret
postfix_expr_arrow_string_0: db "# looking up offset", 10, 0
postfix_expr_arrow_string_1: db "# -> offset calculation", 10, "LOAD_IMMEDIATE_ebx %", 0
postfix_expr_arrow_string_2: db 10, "ADD_ebx_to_eax", 10, 0
postfix_expr_arrow_string_3: db "LOAD_INTEGER", 10, 0
;; primary_expr function
;; Receives nothing
;; Returns nothing
primary_expr:
push ebx ; Protect EBX
mov eax, [global_token] ; Using global_token
mov ebx, [eax+8] ; global_token->S
mov eax, sizeof_string ; Using "sizeof"
call match ; See if match
cmp eax, 0 ; IF match
jne primary_expr_neg ; Otherwise try negatives
;; Deal with sizeof
call unary_expr_sizeof ; Lets do this
jmp primary_expr_done ; Be done
primary_expr_neg:
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; Make it useful
cmp eax, 45 ; IF global_token->S[0] == "-"
jne primary_expr_not ; Otherwise try logical NOT
;; Deal with negative numbers
mov eax, primary_expr_string_0 ; Using "LOAD_IMMEDIATE_eax %0\n"
call emit_out ; Emit it
mov eax, postfix_expr ; Passing postfix_expr
call common_recursion ; Get what it is notting
mov eax, primary_expr_string_1 ; Using "SUBTRACT_eax_from_ebx_into_ebx\nMOVE_ebx_to_eax\n"
call emit_out ; Emit it
jmp primary_expr_done ; Be done
primary_expr_not:
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; Make it useful
cmp eax, 33 ; IF global_token->S[0] == "!"
jne primary_expr_bin ; Otherwise try '~'
;; Deal with logical not
mov eax, primary_expr_string_2 ; Using "LOAD_IMMEDIATE_eax %1\n"
call emit_out ; Emit it
mov eax, postfix_expr ; Passing postfix_expr
call common_recursion ; Get what it is notting
mov eax, primary_expr_string_3 ; Using "XOR_ebx_eax_into_eax\n"
call emit_out ; Emit it
jmp primary_expr_done ; Be done
primary_expr_bin:
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; Make it useful
cmp eax, 126 ; IF global_token->S[0] == "~"
jne primary_expr_paren ; Otherwise try paren
;; Deal with binary NOT
mov eax, postfix_expr ; Passing postfix_expr
call common_recursion ; Get what it is notting
mov eax, primary_expr_string_4 ; Using "NOT_eax\n"
call emit_out ; Emit it
jmp primary_expr_done ; Be done
primary_expr_paren:
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; Make it useful
cmp eax, 40 ; IF global_token->S[0] == "("
jne primary_expr_ch ; Otherwise try char
;; deal with nesting
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
call expression ; Lets recurse
mov eax, primary_expr_string_5 ; Using "Error in Primary expression\nDidn't get )\n"
mov ebx, close_paren ; Using ")"
call require_match ; Make sure we have it
jmp primary_expr_done ; Be done
primary_expr_ch:
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; Make it useful
cmp eax, 39 ; Using "'"
jne primary_expr_str ; Otherwise try string
;; Deal with chars
call primary_expr_char ; Handle that char
jmp primary_expr_done ; Be done
primary_expr_str:
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; Make it useful
cmp eax, 34 ; Using '"'
jne primary_expr_var ; Otherwise try a variable
;; Deal with strings
call primary_expr_string ; Handle that string
jmp primary_expr_done ; Be done
primary_expr_var:
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; Make it useful
mov ebx, primary_expr_string_6 ; Using "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
call In_Set ; See if we have a match
cmp eax, 1 ; IF match
jne primary_expr_num ; otherwise try number
;; Deal with variables
call primary_expr_variable ; Deal with variable
jmp primary_expr_done ; Be done
primary_expr_num:
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; Make it useful
mov ebx, primary_expr_string_7 ; Using "0123456789"
call In_Set ; See if we have a match
cmp eax, 1 ; IF match
jne primary_expr_fail ; otherwise we failed hard
;; Deal with numbers
call primary_expr_number ; Collect the number
jmp primary_expr_done ; Be done
primary_expr_fail:
;; looks like we hit bad input
;; abort before it gets bad
call primary_expr_failure ; No match means failure
primary_expr_done:
pop ebx ; Restore EBX
ret
primary_expr_string_0: db "LOAD_IMMEDIATE_eax %0", 10, 0
primary_expr_string_1: db "SUBTRACT_eax_from_ebx_into_ebx", 10, "MOVE_ebx_to_eax", 10, 0
primary_expr_string_2: db "LOAD_IMMEDIATE_eax %1", 10, 0
primary_expr_string_3: db "XOR_ebx_eax_into_eax", 10, 0
primary_expr_string_4: db "NOT_eax", 10, 0
primary_expr_string_5: db "Error in Primary expression", 10, "Didn't get )", 10, 0
primary_expr_string_6: db "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", 0
primary_expr_string_7:db "0123456789", 0
;; 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
mov eax, [global_token] ; Using global_token
mov ecx, [eax+8] ; S = global_token->S
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, ecx ; Using S
mov ebx, [global_constant_list] ; Using global_constant_list
call sym_lookup ; sym_lookup(s, global_constant_list)
cmp eax, 0 ; IF NULL == sym_lookup(s, global_constant_list)
je primary_expr_variable_local ; Try locals next
;; Deal with constant load
mov ebx, [eax+16] ; a->ARGS
mov eax, primary_expr_variable_string_2 ; Using "LOAD_IMMEDIATE_eax %"
call emit_out ; Emit it
mov eax, [ebx+8] ; a->ARGS->S
call emit_out ; Emit it
mov eax, primary_expr_variable_string_1 ; Using "\n"
call emit_out ; Emit it
jmp primary_expr_variable_done ; Be done
primary_expr_variable_local:
mov eax, ecx ; Using S
mov ebx, [function] ; Using function
mov ebx, [ebx+4] ; function->locals
call sym_lookup ; sym_lookup(s, function->locals)
cmp eax, 0 ; IF NULL == sym_lookup(s, global_constant_list)
je primary_expr_variable_arguments ; try arguments next
;; Deal with local load
call variable_load ; Collect it
jmp primary_expr_variable_done ; Be done
primary_expr_variable_arguments:
mov eax, ecx ; Using S
mov ebx, [function] ; Using function
mov ebx, [ebx+16] ; function->args
call sym_lookup ; sym_lookup(s, function->args)
cmp eax, 0 ; IF NULL == sym_lookup(s, global_constant_list)
je primary_expr_variable_function ; try functions next
;; Deal with argument load
call variable_load ; Collect it
jmp primary_expr_variable_done ; Be done
primary_expr_variable_function:
mov eax, ecx ; Using S
mov ebx, [global_function_list] ; Using global_function_list
call sym_lookup ; sym_lookup(s, global_function_list)
cmp eax, 0 ; IF NULL == sym_lookup(s, global_function_list)
je primary_expr_variable_global ; try globals next
;; Deal with functions
call function_load ; Deal with the function
jmp primary_expr_variable_done ; Be done
primary_expr_variable_global:
mov eax, ecx ; Using S
mov ebx, [global_symbol_list] ; Using global_symbol_list
call sym_lookup ; sym_lookup(s, global_symbol_list)
cmp eax, 0 ; IF NULL == sym_lookup(s, global_symbol_list)
je primary_expr_variable_error ; Give up
;; Deal with globals
call global_load ; Collect that global
jmp primary_expr_variable_done ; Be done
primary_expr_variable_error:
mov eax, 2 ; Using standard error
mov [Output_file], eax ; write to standard error
; call line_error ; Write useful debug info
mov eax, ecx ; put S in the right place
call File_Print ; print it
mov eax, primary_expr_variable_string_0 ; Ending string
call File_Print ; print it
jmp Exit_Failure ; Abort Hard
primary_expr_variable_done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
primary_expr_variable_string_0: db " is not a defined symbol", 10, 0
primary_expr_variable_string_1: db 10, 0
primary_expr_variable_string_2: db "LOAD_IMMEDIATE_eax %", 0
;; 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
mov ecx, eax ; Put S in place
mov edx, ebx ; Put BOOL in place
mov esi, 0 ; PASSED = 0
mov eax, function_call_string_0 ; Using "ERROR in process_expression_list\nNo ( was found\n"
mov ebx, open_paren ; Using "("
call require_match ; Make sure we have it
mov eax, function_call_string_1 ; Using "PUSH_edi\t# Prevent overwriting in recursion\n"
call emit_out ; Emit it
mov eax, function_call_string_2 ; Using "PUSH_ebp\t# Protect the old base pointer\n"
call emit_out ; Emit it
mov eax, function_call_string_3 ; Using "COPY_esp_to_edi\t# Copy new base pointer\n"
call emit_out ; Emit it
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; Make it useful
cmp eax, 41 ; IF global_token->S[0] == ")"
je function_call_gen_done ; Then no arguments to send
;; looks like we have arguments to collect
call expression ; Collect the argument
mov eax, function_call_string_4 ; Using "PUSH_eax\t#_process_expression1\n"
call emit_out ; Emit it
mov esi, 1 ; PASSED = 1
function_call_gen_iter:
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; Make it useful
cmp eax, 44 ; IF global_token->S[0] == ","
jne function_call_gen_done ; Otherwise we are done
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
call expression ; Collect the argument
mov eax, function_call_string_5 ; Using "PUSH_eax\t#_process_expression2\n"
call emit_out ; Emit it
add esi, 1 ; PASSED = PASSED + 1
jmp function_call_gen_iter ; Keep trying
function_call_gen_done:
;; All is collected
mov eax, function_call_string_6 ; Using "ERROR in process_expression_list\nNo ) was found\n"
mov ebx, close_paren ; Using ")"
call require_match ; Make sure we have it
cmp edx, 0 ; IF(BOOL == TRUE)
jne function_call_static ; Otherwise it is a static call
;; Deal with a passed function pointer
mov eax, function_call_string_7 ; Using "LOAD_BASE_ADDRESS_eax %"
call emit_out ; Emit it
mov eax, ecx ; Using S
call emit_out ; Emit it
mov eax, function_call_string_8 ; Using "\nLOAD_INTEGER\n"
call emit_out ; Emit it
mov eax, function_call_string_9 ; Using "COPY_edi_to_ebp\n"
call emit_out ; Emit it
mov eax, function_call_string_10 ; Using "CALL_eax\n"
call emit_out ; Emit it
mov eax, function_call_string_13 ; Using "POP_ebx\t# _process_expression_locals\n"
jmp function_call_cleanup ; Clean up
function_call_static:
;; Deal with fixed function name
mov eax, function_call_string_9 ; Using "COPY_edi_to_ebp\n"
call emit_out ; Emit it
mov eax, function_call_string_11 ; Using "CALL_IMMEDIATE %FUNCTION_"
call emit_out ; Emit it
mov eax, ecx ; Using S
call emit_out ; Emit it
mov eax, function_call_string_12 ; Using "\n"
call emit_out ; Emit it
mov eax, function_call_string_13 ; Using "POP_ebx\t# _process_expression_locals\n"
function_call_cleanup:
cmp esi, 0 ; IF PASSED > 0
jle function_call_done ; Otherwise be done
;; The desired string is already in EAX
call emit_out ; Emit it
sub esi, 1 ; PASSED = PASSED - 1
jmp function_call_cleanup ; Keep going
function_call_done:
mov eax, function_call_string_14 ; Using "POP_ebp\t# Restore old base pointer\n"
call emit_out ; Emit it
mov eax, function_call_string_15 ; Using "POP_edi\t# Prevent overwrite\n"
call emit_out ; Emit it
pop esi ; Restore ESI
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
function_call_string_0: db "ERROR in process_expression_list", 10, "No ( was found", 10, 0
function_call_string_1: db "PUSH_edi", 9, "# Prevent overwriting in recursion", 10, 0
function_call_string_2: db "PUSH_ebp", 9, "# Protect the old base pointer", 10, 0
function_call_string_3: db "COPY_esp_to_edi", 9, "# Copy new base pointer", 10, 0
function_call_string_4: db "PUSH_eax", 9, "#_process_expression1", 10, 0
function_call_string_5: db "PUSH_eax", 9, "#_process_expression2", 10, 0
function_call_string_6: db "ERROR in process_expression_list", 10, "No ) was found", 10, 0
function_call_string_7: db "LOAD_BASE_ADDRESS_eax %", 0
function_call_string_8: db 10, "LOAD_INTEGER", 10, 0
function_call_string_9: db "COPY_edi_to_ebp", 10, 0
function_call_string_10: db "CALL_eax", 10, 0
function_call_string_11: db "CALL_IMMEDIATE %FUNCTION_", 0
function_call_string_12: db 10, 0
function_call_string_13: db "POP_ebx", 9, "# _process_expression_locals", 10, 0
function_call_string_14: db "POP_ebp", 9, "# Restore old base pointer", 10, 0
function_call_string_15: db "POP_edi", 9, "# Prevent overwrite", 10, 0
;; 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
mov ecx, eax ; Protect A
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, open_paren ; Using "("
call match ; IF global_token->S == "("
cmp eax, 0 ; Then it might be a function
jne variable_load_regular ; Otherwise it is regular
mov ebx, [ecx+12] ; A->TYPE
mov ebx, [ebx+24] ; A->TYPE->NAME
mov eax, type_function_name ; Using "FUNCTION"
call match ; IF A->TYPE->NAME == "FUNCTION"
cmp eax, 0 ; Then it must be a function
jne variable_load_regular ; otherwise just another regular
;; deal with function
mov eax, [ecx+16] ; A->DEPTH
call numerate_number ; Convert to string
mov ebx, 0 ; pass 0 for true
call function_call ; Create the function call
jmp variable_load_done ; Be done
variable_load_regular:
mov eax, [ecx+12] ; A->TYPE
mov [current_target], eax ; current_target = A->TYPE
mov eax, variable_load_string_0 ; Using "LOAD_BASE_ADDRESS_eax %"
call emit_out ; Emit it
mov eax, [ecx+16] ; A->DEPTH
call numerate_number ; Convert to string
call emit_out ; Emit it
mov eax, variable_load_string_1 ; Using "\n"
call emit_out ; Emit it
;; Check for special case of assignment
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, equal ; Using "="
call match ; IF global_token->S == "="
cmp eax, 0 ; Then we skip loading
je variable_load_done ; And be done
;; Deal with common case
mov eax, variable_load_string_2 ; Using "LOAD_INTEGER\n"
call emit_out ; Emit it
variable_load_done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
variable_load_string_0: db "LOAD_BASE_ADDRESS_eax %", 0
variable_load_string_1: db 10, 0
variable_load_string_2: db "LOAD_INTEGER", 10, 0
;; 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
mov eax, [eax+8] ; A->S
mov ecx, eax ; Protect A->S
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, open_paren ; Using "("
call match ; IF global_token->S == "("
cmp eax, 0 ; The we need to do a function call
jne function_load_regular ; Otherwise just load it's address
;; Deal with function call
mov eax, ecx ; Using A->S
mov ebx, 1 ; Using FALSE
call function_call ; Deal with it
jmp function_load_done ; Be done
function_load_regular:
mov eax, function_load_string_0 ; Using "LOAD_IMMEDIATE_eax &FUNCTION_"
call emit_out ; Emit it
mov eax, ecx ; Using A->S
call emit_out ; Emit it
mov eax, function_load_string_1 ; Using "\n"
call emit_out ; Emit it
function_load_done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
function_load_string_0: db "LOAD_IMMEDIATE_eax &FUNCTION_", 0
function_load_string_1: db 10, 0
;; global_load function
;; Receives struct token_list* A in EAX
;; Returns nothing
;; Uses EBX to hold A->S
global_load:
push ebx ; Protect EBX
mov ebx, eax ; Set as A
mov ebx, [ebx+8] ; Set as A->S
mov eax, [eax+12] ; A->TYPE
mov [current_target], eax ; current_target = A->TYPE
mov eax, global_load_string_0 ; Using "LOAD_IMMEDIATE_eax &GLOBAL_"
call emit_out ; Emit it
mov eax, ebx ; Using A->S
call emit_out ; Emit it
mov eax, global_load_string_1 ; Using "\n"
call emit_out ; Emit it
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, equal ; "="
call match ; IF global_token->S == "="
cmp eax, 0 ; We need to skip for assignment
je global_load_done ; and be done
;; Otherwise we are loading the contents
mov eax, global_load_string_2 ; Using "LOAD_INTEGER\n"
call emit_out ; Emit it
global_load_done:
pop ebx ; Restore EBX
ret
global_load_string_0: db "LOAD_IMMEDIATE_eax &GLOBAL_", 0
global_load_string_1: db 10, 0
global_load_string_2: db "LOAD_INTEGER", 10, 0
;; 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
mov ecx, ebx ; I = symbol_list
mov ebx, eax ; Put S in the right place
sym_lookup_iter:
cmp ecx, 0 ; IF NULL == I
je sym_lookup_done ; We failed to find match
mov eax, [ecx+8] ; Using I->S
call match ; IF I->S == S
cmp eax, 0 ; then be done
je sym_lookup_done ; Failed
mov ecx, [ecx] ; I = I->NEXT
jmp sym_lookup_iter ; otherwise keep looping
sym_lookup_done:
mov eax, ecx ; Return I
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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:
mov eax, primary_expr_number_string_0 ; Using "LOAD_IMMEDIATE_eax %"
call emit_out ; Emit it
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
call emit_out ; Emit it
mov eax, primary_expr_number_string_1 ; Using "\n"
call emit_out ; Emit it
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
ret
primary_expr_number_string_0: db "LOAD_IMMEDIATE_eax %", 0
primary_expr_number_string_1: db 10, 0
;; 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
mov ebx, [current_count] ; Using current_count
mov eax, ebx ; And putting it in the right place
call numerate_number ; Get the string
mov ecx, eax ; protect number_string
add ebx, 1 ; current_count + 1
mov [current_count], ebx ; current_count = current_count + 1
mov eax, primary_expr_string_string_0 ; Using "LOAD_IMMEDIATE_eax &STRING_"
call emit_out ; Emit it
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
mov ebx, ecx ; Put number_string in the right place
call uniqueID_out ; Make it unique
;; Generate the target
mov eax, primary_expr_string_string_1 ; Using ":STRING_"
mov ebx, [strings_list] ; Using strings_list
call emit ; Emit it
mov ebx, eax ; put new strings_list in place
mov eax, [function] ; Using function
mov eax, [eax+8] ; function->S
call uniqueID ; Make it unique
mov ebx, eax ; put new strings_list in place
;; Parse the string
mov eax, [global_token] ; Using global token
mov eax, [eax+8] ; global_token->S
call parse_string ; convert to useful form
call emit ; Emit it
mov [strings_list], eax ; Update Strings _list
mov eax, [global_token] ; Using global token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
primary_expr_string_string_0: db "LOAD_IMMEDIATE_eax &STRING_", 0
primary_expr_string_string_1: db ":STRING_", 0
;; 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
mov eax, primary_expr_char_string_0 ; Using "LOAD_IMMEDIATE_eax %"
call emit_out ; Emit it
mov eax, [global_token] ; Using global token
mov eax, [eax+8] ; global_token->S
add eax, 1 ; global_token->S + 1
call escape_lookup ; Get the char
call numerate_number ; Convert to string
call emit_out ; emit it
mov eax, primary_expr_char_string_1 ; Using "\n"
call emit_out ; Emit it
mov eax, [global_token] ; Using global token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
primary_expr_char_string_0: db "LOAD_IMMEDIATE_eax %", 0
primary_expr_char_string_1: db 10, 0
;; primary_expr_failure function
;; Receives nothing
;; Does not return but aborts hard
;; Complains about the bad input
primary_expr_failure:
; call line_error ; Get line of issue
mov eax, 2 ; Using Standard error
mov [Output_file], eax ; write to standard error
mov eax, primary_expr_failure_string_0 ; Using "Received "
call File_Print ; Print it
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
call File_Print ; Print it
mov eax, primary_expr_failure_string_1 ; Using " in primary_expr\n"
call File_Print ; Print it
jmp Exit_Failure ; Abort Hard
primary_expr_failure_string_0: db "Received ", 0
primary_expr_failure_string_1: db " in primary_expr", 10, 0
;; 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
mov ebp, eax ; Protect F
mov eax, ecx ; Put name in the right place
mov ecx, ebx ; Protect S
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
call match ; IF match(name, global_token->s)
cmp eax, 0 ; If true we do
jne general_recursion_done ; Otherwise skip it
;; Deal with the recursion
mov eax, ebp ; Put F in the right place
call common_recursion ; Recurse
mov eax, ecx ; Put S in the right place
call emit_out ; Emit it
mov eax, edx ; 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
ret
;; 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
cmp ebx, 0 ; IF NULL == A
je promote_type_done ; Just return A
mov ecx, eax ; Put A in place
mov edx, ebx ; Put B in place
mov eax, ebx ; IF NULL == A
cmp ecx, 0 ; Then we just return B
je promote_type_done ; Be done
;; Looks like we need to walk the list
mov ecx, [ecx+24] ; A->NAME
mov edx, [edx+24] ; B->NAME
mov eax, [global_types] ; I = global_types
promote_type_iter:
cmp eax, 0 ; IF NULL == I
je promote_type_done ; Just be done
mov ebx, [eax+24] ; I->NAME
cmp ebx, ecx ; IF(A->NAME == I->NAME)
je promote_type_done ; Be done
cmp ebx, edx ; IF(B->NAME == I->NAME)
je promote_type_done ; Be done
mov ebx, [eax+12] ; I->INDIRECT
mov ebx, [ebx+24] ; I->INDIRECT->NAME
cmp ebx, ecx ; IF(A->NAME == I->INDIRECT->NAME)
je promote_type_done ; Be done
cmp ebx, edx ; IF(B->NAME == I->INDIRECT->NAME)
je promote_type_done ; Be done
mov eax, [eax] ; I = I->NEXT
jmp promote_type_iter ; Keep going
promote_type_done:
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
mov ebx, eax ; Put FUNCTION F safely out of the way
mov eax, common_recursion_string_0 ; Using "PUSH_eax\t#_common_recursion\n"
call emit_out ; Emit it
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, ebx ; Prepare for function call
mov ebx, [current_target] ; Get last type
call eax ; F();
mov eax, [current_target] ; Get current_target
call promote_type ; get the right type
mov [current_target], eax ; Set new current_target
mov eax, common_recursion_string_1 ; Using "POP_ebx\t# _common_recursion\n"
call emit_out ; Emit it
pop ebx ; Restore EBX
ret
common_recursion_string_0: db "PUSH_eax", 9, "#_common_recursion", 10, 0
common_recursion_string_1: db "POP_ebx", 9, "# _common_recursion", 10, 0
;; 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
mov ecx, eax ; put the message somewhere safe
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
call match ; IF required == global_token->S
cmp eax, 0 ; we are fine
je require_match_good ; otherwise pain
;; Deal will bad times
; call line_error ; Tell user what went wrong
mov eax, 2 ; Using standard error
mov [Output_file], eax ; write to standard error
mov eax, ecx ; using our message
call File_Print ; Print it
jmp Exit_Failure ; Abort HARD
require_match_good:
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->next
mov [global_token], eax ; global_token = global_token->next
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
call emit ; emit(s, l)
mov ebx, eax ; Put L in correct place
mov eax, underline ; Usinf "_"
call emit ; emit("_", l)
mov ebx, eax ; Put L in correct place
mov eax, ecx ; Put num in correct place
call emit ; emit(num, l)
mov ebx, eax ; Put L in correct place
mov eax, uniqueID_string_0 ; Using "\n"
call emit ; emit("\n", l)
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
uniqueID_string_0: db 10, 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
mov ecx, ebx ; Put num in right spot
mov ebx, [output_list] ; Using output_list
call uniqueID ; Get updated list
mov [output_list], eax ; output_list = uniqueID(s, output_list, num)
pop ecx ; Restore ECX
pop ebx ; Restore EBX
pop eax ; Restore EAX
ret
;; 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
mov ebx, [output_list] ; Using output_list
call emit ; emit it
mov [output_list], eax ; update it
pop ebx ; Restore EBX
pop eax ; Restore EAX
ret
;; 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
mov ecx, eax ; put S out of the way
mov eax, 20 ; sizeof(struct token_list)
call malloc ; get T
mov [eax], ebx ; t->next = head;
mov [eax+8], ecx ; t->s = s;
pop ecx ; Restore ECX
ret
;; 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
mov ecx, eax ; Put char* C in safe place
mov al, [ecx] ; Load c[0]
movzx eax, al ; make it useful
cmp eax, 92 ; If '\\' != c[0]
jne escape_lookup_done ; Be done
mov ebx, ecx ; Prepare for walk
add ebx, 1 ; increment
mov bl, [ebx] ; load c[1]
movzx ebx, bl ; make it useful
cmp ebx, 120 ; Check if \x??
je escape_lookup_hex ; Deal with hex
;; Deal with \? escapes
mov eax, 10 ; Guess "\n"
cmp ebx, 110 ; If n
je escape_lookup_done ; Be done
mov eax, 9 ; Guess "\t"
cmp ebx, 116 ; If t
je escape_lookup_done ; Be done
mov eax, ebx ; "\\", "'" and '"' all encode as themselves
cmp ebx, 92 ; If "\\"
je escape_lookup_done ; Be done
cmp ebx, 39 ; IF "'"
je escape_lookup_done ; Be done
cmp ebx, 34 ; IF '"'
je escape_lookup_done ; Be done
mov eax, 13 ; Guess "\r"
cmp ebx, 114 ; IF r
je escape_lookup_done ; Be done
;; Looks like we have no clue what we are doing
;; Aborting hard
mov eax, 2 ; Using Standard error
mov [Output_file], eax ; write to standard error
mov eax, escape_lookup_string_0 ; Using "Unknown escape received: "
call File_Print ; Print it
mov eax, ecx ; Using C
call File_Print ; Print it
mov eax, escape_lookup_string_1 ; Using " Unable to process\n"
call File_Print ; Print it
jmp Exit_Failure ; Abort Hard
escape_lookup_done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
escape_lookup_hex:
;; Give up on C and just assume they know what they are doing
add ecx, 2 ; increment
mov al, [ecx] ; c[2]
movzx eax, al ; make it useful
add ecx, 1 ; increment
call char2hex ; Get the hex value
sal eax, 4 ; c << 4
mov bl, [ecx] ; c[3]
movzx ebx, bl ; make it useful
xchg eax, ebx ; protect c << 4
call char2hex ; Get the hex value
add eax, ebx ; hex(c[2]) << 4 + hex(c[3])
jmp escape_lookup_done ; Be done
escape_lookup_string_0: db "Unknown escape received: ", 0
escape_lookup_string_1: db " Unable to process", 10, 0
;; char2hex function
;; Receives char in EAX
;; Returns hex or aborts hard
char2hex:
sub eax, 48 ; Try 0-9
cmp eax, 10 ; Otherwise fun times
jl char2hex_done ; Be done
;; Deal with A-F
and eax, 0xDF ; Unset High bit turning a-f into A-F
sub eax, 7 ; Shift down into position
cmp eax, 10 ; Everything below A is bad
jl char2hex_fail ; And should fail
cmp eax, 16 ; Make sure we are below F
jl char2hex_done ; If so be done
char2hex_fail:
;; Time to fail hard
mov eax, 2 ; Using Standard error
mov [Output_file], eax ; write to standard error
mov eax, char2hex_string_0 ; Using "Tried to print non-hex number\n"
call File_Print ; Print it
jmp Exit_Failure ; Abort Hard
char2hex_done:
ret
char2hex_string_0: db "Tried to print non-hex number", 10, 0
;; parse_string function
;; Receives char* string in EAX
;; Returns cleaned up string
;; Protects char* string in EBX
parse_string:
push ebx ; Protect EBX
mov ebx, eax ; Protect char* string
call weird ; Determine if we have a weird string
cmp eax, 0 ; If weird
je parse_string_weird ; Deal with it
;; Dealing with regular string
mov eax, ebx ; Passing Char* string
call collect_regular_string ; Collect it
jmp parse_string_done ; Be done
parse_string_weird:
mov eax, ebx ; Passing Char* string
call collect_weird_string ; Collect it
parse_string_done:
pop ebx ; Restore EBX
ret
;; 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
mov ecx, eax ; Place string in safe place
add ecx, 1 ; increment past the "
weird_reset:
mov al, [ecx] ; Load a char
movzx eax, al ; Make it useful
cmp eax, 0 ; IF NULL == C
je weird_false ; Nothing weird found
cmp eax, 92 ; IF '\\'
jne weird_escaped ; Deal with escaping
;; Deal with escape
mov eax, ecx ; We are passing the string
call escape_lookup ; to look it up
add ecx, 1 ; string = string + 1
mov bl, [ecx] ; get string[1]
movzx ebx, bl ; make it useful
cmp ebx, 120 ; IF 'x' == string[1]
jne weird_escaped ; otherwise skip the gap
add ecx, 2 ; string = string + 2
weird_escaped:
push eax ; Protect C in case we need it
mov ebx, weird_string_0 ; Use "\t\n !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
call In_Set ; To find if weird
cmp eax, 1 ; IF TRUE
pop eax ; Restore C
jne weird_true ; Then not weird
add ecx, 1 ; string = string + 1
;; Last chance for weird
mov ebx, weird_string_1 ; Use "\t\n\r "
call In_Set ; Check for special case
cmp eax, 1 ; IF TRUE
jne weird_reset ; Otherwise not in the special case
;; Deal with possible special case
mov al, [ecx] ; Load string[1]
movzx eax, al ; Make it useful
cmp eax, 58 ; IF string[1] == ":"
je weird_true ; Then we hit the special case
jmp weird_reset ; Keep trying
weird_done:
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
weird_true:
mov eax, 0 ; Return true
jmp weird_done ; Be done
weird_false:
mov eax, 1 ; Return false
jmp weird_done ; Be done
weird_string_0: db 9, 10, " !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", 0
weird_string_1: db 9, 10, 13, 32, 0
;; 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
mov edx, eax ; Protect our passed string
mov eax, 256 ; We need 256bytes of storage
call malloc ; Get our new pointer
mov ecx, eax ; put it in place
push eax ; protect until done
collect_regular_string_reset:
mov al, [edx] ; string[0]
movzx eax, al ; Make it useful
cmp eax, 0 ; See if we hit the end
je collect_regular_string_done ; And be done
cmp eax, 92 ; IF string[0] == '\\'
je collect_regular_string_escaped ; Deal with that mess
;; deal with boring char
mov [ecx], al ; hold_string[index] = string[0]
add ecx, 1 ; Increment it
add edx, 1 ; Increment it
jmp collect_regular_string_reset ; And keep going
collect_regular_string_escaped:
mov eax, edx ; Using string
call escape_lookup ; Get the char
mov [ecx], al ; hold_string[index] = escape_lookup(string)
add edx, 1 ; Increment it
add ecx, 1 ; Increment it
mov al, [edx] ; string[0]
movzx eax, al ; Make it useful
add edx, 1 ; Increment it
cmp eax, 120 ; IF 'x' == string[1]
jne collect_regular_string_reset ; Otherwise keep going
add edx, 2 ; Increment it
jmp collect_regular_string_reset ; Keep going
collect_regular_string_done:
mov eax, 34 ; Using '"'
mov [ecx], al ; hold_string[index] = '"'
add ecx, 1 ; Increment it
mov eax, 10 ; Using "\n"
mov [ecx], al ; hold_string[index] = '\n'
pop eax ; Return our new string
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
mov edx, eax ; Protect our passed string
mov eax, 512 ; We need 512bytes of storage
call malloc ; Get our new pointer
mov ecx, eax ; put it in place
push eax ; protect until done
mov eax, 39 ; Using "'"
mov [ecx], al ; hold_string[index] = "'"
add ecx, 1 ; Increment it
add edx, 1 ; Increment it
collect_weird_string_reset:
mov al, [edx] ; Read a byte
movzx eax, al ; Make it useful
cmp eax, 0 ; IF NULL == string[0]
je collect_weird_string_done ; Be done
mov eax, 32 ; Using ' '
mov [ecx], al ; hold_string[index] = ' '
add ecx, 1 ; Increment it
mov eax, edx ; Using string
call escape_lookup ; Get the char
call hex8 ; Update ECX
mov al, [edx] ; Read a byte
movzx eax, al ; Make it useful
add edx, 1 ; Increment it
cmp eax, 92 ; IF string[0] == '\\'
jne collect_weird_string_reset ; Otherwise keep going
mov al, [edx] ; Read a byte
movzx eax, al ; Make it useful
add edx, 1 ; Increment it
cmp eax, 120 ; IF 'x' == string[1]
jne collect_weird_string_reset ; Otherwise keep going
add edx, 2 ; Increment it
jmp collect_weird_string_reset ; Keep going
collect_weird_string_done:
mov eax, 32 ; Using ' '
mov [ecx], al ; hold_string[index] = ' '
add ecx, 1 ; Increment it
mov eax, 48 ; Using '0'
mov [ecx], al ; hold_string[index] = '0'
add ecx, 1 ; Increment it
mov [ecx], al ; hold_string[index] = '0'
add ecx, 1 ; Increment it
mov eax, 39 ; Using "'"
mov [ecx], al ; hold_string[index] = "'"
add ecx, 1 ; Increment it
mov eax, 10 ; Using "\n"
mov [ecx], al ; hold_string[index] = '\n'
pop eax ; Return our new string
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
shr eax, 4 ; do high nibble first
call hex4 ; Store it
pop eax ; do low nibble
hex4:
and eax, 0xf ; isolate nibble
add al,'0' ; convert to ascii
cmp al,'9' ; valid digit?
jbe hex1 ; yes
add al,7 ; use alpha range
hex1:
mov [ecx], al ; store result
add ecx, 1 ; next position
ret
;; 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
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx+8] ; global_token->S
mov eax, struct ; Using "struct"
call match ; IF global_token->S == "struct"
mov ecx, eax ; Protect structure
cmp eax, 0 ; need to skip over "struct"
jne type_name_native ; otherwise keep going
;; Deal with possible STRUCTs
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx] ; global_token->next
mov [global_token], ebx ; global_token = global_token->next
mov eax, [ebx+8] ; global_token->S
mov ebx, [global_types] ; get all known types
call lookup_type ; Find type if possible
mov ecx, eax ; Set ret
cmp eax, 0 ; IF NULL == ret
jne type_name_common ; We have to create struct
;; Create a struct
call create_struct ; Create a new struct
mov ecx, 0 ; We wish to return NULL
jmp type_name_done ; be done
type_name_native:
;; Deal only with native types
mov eax, ebx ; Put global_token->S in the right place
mov ebx, [global_types] ; get all known types
call lookup_type ; Find the type if possible
mov ecx, eax ; Set ret
cmp eax, 0 ; IF NULL == ret
jne type_name_common ; We need to abort hard
;; Aborting hard
mov eax, 2 ; Using Standard error
mov [Output_file], eax ; write to standard error
mov eax, type_name_string_0 ; Print header
call File_Print ; Print it
mov eax, [global_token] ; Using global token
mov eax, [eax+8] ; global_token->S
call File_Print ; Print it
mov eax, type_name_string_1 ; Print footer
call File_Print ; Print it
; call line_error ; Give details
jmp Exit_Failure ; Abort
type_name_common:
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx] ; global_token->next
mov [global_token], ebx ; global_token = global_token->next
type_name_iter:
mov eax, [ebx+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; make it useful
cmp eax, 42 ; IF global_token->S[0] == '*'
jne type_name_done ; recurse
;; Deal with char**
mov ecx, [ecx+12] ; ret = ret->indirect
mov ebx, [global_token] ; Using global_token
mov ebx, [ebx] ; global_token->next
mov [global_token], ebx ; global_token = global_token->next
jmp type_name_iter ; keep looping
type_name_done:
mov eax, ecx ; put ret in the right place
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
type_name_string_0: db "Unknown type ", 0
type_name_string_1: db 10, 0
;; 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
mov ecx, ebx ; I = Start
mov ebx, eax ; Put S in place
lookup_type_iter:
cmp ecx, 0 ; Check if I == NULL
je lookup_type_done ; return NULL
mov eax, [ecx+24] ; I->NAME
call match ; Check if matching
cmp eax, 0 ; IF I->NAME == S
je lookup_type_done ; return it
mov ecx, [ecx] ; Otherwise I = I->NEXT
jmp lookup_type_iter ; And keep looping
lookup_type_done:
mov eax, ecx ; return either I or NULL
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
mov ecx, 0 ; OFFSET = 0
mov edi, 0 ; member_size = 0
mov eax, 28 ; sizeof(struct type)
call malloc ; malloc(sizeof(struct type))
mov edx, eax ; Set HEAD
mov eax, 28 ; sizeof(struct type)
call malloc ; malloc(sizeof(struct type))
mov ebp, eax ; Set I
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov [edx+24], eax ; HEAD->NAME = global_token->S
mov [ebp+24], eax ; I->NAME = global_token->S
mov [edx+12], ebp ; HEAD->INDIRECT = I
mov [ebp+12], edx ; I->INDIRECT = HEAD
mov eax, [global_types] ; Using global_types
mov [edx], eax ; HEAD->NEXT = global_types
mov [global_types], edx ; global_types = HEAD
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, 4 ; Using register size
mov [ebp+4], eax ; I->SIZE = register size
mov eax, create_struct_string_0 ; Using "ERROR in create_struct\n Missing {\n"
mov ebx, open_curly_brace ; Using "{"
call require_match ; Make sure we have it
mov esi, 0 ; LAST = NULL
create_struct_iter:
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; Make it useful
cmp eax, 125 ; IF global_token->S[0] == "}"
je create_struct_done ; be done
;; Looks like we are adding members
;; Lets see if it is a union
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov ebx, union ; Using "union"
call match ; IF match(global_token->s, "union")
cmp eax, 0 ; Deal with union
jne create_struct_single ; Otherwise deal with singles
;; Deal with union
mov eax, esi ; Put last in right place
mov ebx, ecx ; put offset in right place
call build_union ; ASSEMBLE
mov esi, eax ; last = build_union(last, offset)
add ecx, edi ; offset = offset + member_size
mov eax, create_struct_string_1 ; Using "ERROR in create_struct\n Missing ;\n"
mov ebx, semicolon ; Using ";"
call require_match ; Make sure we have it
jmp create_struct_iter ; keep going
create_struct_single:
;; deal with singles
mov eax, esi ; Put last in right place
mov ebx, ecx ; put offset in right place
call build_member ; ASSEMBLE
mov esi, eax ; last = build_union(last, offset)
add ecx, edi ; offset = offset + member_size
mov eax, create_struct_string_1 ; Using "ERROR in create_struct\n Missing ;\n"
mov ebx, semicolon ; Using ";"
call require_match ; Make sure we have it
jmp create_struct_iter ; keep going
create_struct_done:
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, create_struct_string_1 ; Using "ERROR in create_struct\n Missing ;\n"
mov ebx, semicolon ; Using ";"
call require_match ; Make sure we have it
mov [edx+4], ecx ; HEAD->SIZE = OFFSET
mov [edx+16], esi ; HEAD->MEMBERS = LAST
mov [ebp+16], esi ; 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
ret
create_struct_string_0: db "ERROR in create_struct", 10, " Missing {", 10, 0
create_struct_string_1: db "ERROR in create_struct", 10, " Missing ;", 10, 0
;; 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
mov edx, eax ; Protect Parent
mov ecx, [eax+16] ; struct type* I = parent->MEMBERS
lookup_member_iter:
cmp ecx, 0 ; IF I == NULL
je lookup_member_fail ; Abort HARD
mov eax, [ecx+24] ; Using I->NAME
call match ; IF I->NAME == NAME
cmp eax, 0 ; Then we have found the member
mov eax, ecx ; Prepare for return
mov ecx, [ecx+16] ; Prepare for loop I = I->MEMBERS
jne 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
ret
lookup_member_fail:
mov eax, 2 ; Using Standard error
mov [Output_file], eax ; write to standard error
mov eax, lookup_member_string_0 ; Using "ERROR in lookup_member "
call File_Print ; print it
mov eax, [edx+24] ; PARENT->NAME
call File_Print ; print it
mov eax, arrow_string ; Using "->"
call File_Print ; print it
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
call File_Print ; print it
mov eax, lookup_member_string_1 ; Using " does not exist\n"
call File_Print ; print it
; call line_error ; Write useful debug info
mov eax, lookup_member_string_2 ; Using "\n"
call File_Print ; print it
jmp Exit_Failure ; Abort Hard
lookup_member_string_0: db "ERROR in lookup_member ", 0
lookup_member_string_1: db " does not exist", 10, 0
lookup_member_string_2: db 10, 0
;; 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
mov edx, eax ; Put last out of the way
mov eax, 28 ; Allocate type
call malloc ; Get I
mov [eax+16], edx ; I->MEMBERS = LAST
mov [eax+8], ebx ; I->OFFSET = OFFSET
mov edx, eax ; Put I in place
call type_name ; Get member_type
mov ecx, eax ; Put in place
mov [edx+20], ecx ; I->TYPE = MEMBER_TYPE
mov eax, [global_token] ; Using global_token
mov ebx, [eax+8] ; global_token->S
mov [edx+24], ebx ; I->NAME = global_token->S
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
;; Check if we have an array
mov ebx, [eax+8] ; global_token->S
mov eax, open_bracket ; Using "["
call match ; IF global_token->S == "["
cmp eax, 0 ; Then we have to deal with arrays in our structs
je build_member_array ; So deal with that pain
;; Deal with non-array case
mov eax, [ecx+4] ; member_type->SIZE
mov [edx+4], eax ; I->SIZE = member_type->SIZE
jmp build_member_done ; Be done
build_member_array:
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, [eax+8] ; global_token->S
call numerate_string ; convert number
mov ebx, [ecx+20] ; member_type->TYPE
mov ebx, [ebx+4] ; member_type->TYPE->SIZE
imul eax, ebx ; member_type->type->size * numerate_string(global_token->s)
mov [edx+4], eax ; I->SIZE = member_type->type->size * numerate_string(global_token->s)
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, build_member_string_0 ; Using "Struct only supports [num] form\n"
mov ebx, close_bracket ; Using "]"
call require_match ; Make sure we have it
build_member_done:
mov edi, [edx+4] ; MEMBER_SIZE = I->SIZE
mov [edx+20], ecx ; I->TYPE = MEMBER_TYPE
mov eax, edx ; Return I
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
build_member_string_0: db "Struct only supports [num] form", 10, 0
;; 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
mov ecx, eax ; Put LAST in right spot
mov edx, ebx ; Put OFFSET in right spot
mov esi, 0 ; SIZE = 0
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, build_union_string_0 ; Using "ERROR in build_union\nMissing {\n"
mov ebx, open_curly_brace ; Using "{"
call require_match ; Make sure we have it
build_union_iter:
mov eax, [global_token] ; Using global_token
mov eax, [eax+8] ; global_token->S
mov al, [eax] ; global_token->S[0]
movzx eax, al ; make it useful
cmp eax, 125 ; IF global_token->S[0] == "}"
je build_union_done ; Be done
;; Collect union member
mov eax, ecx ; Passing LAST
mov ebx, edx ; Passing offset
call build_member ; build_member(last, offset)
mov ecx, eax ; last = build_member(last, offset)
cmp esi, edi ; IF member_size > size
jg build_union_size ; Then update size
;; deal with member_size > size
mov esi, edi ; SIZE = MEMBER_SIZE
build_union_size:
mov eax, build_union_string_1 ; Using "ERROR in build_union\nMissing ;\n"
mov ebx, semicolon ; Using ";"
call require_match ; Make sure we have it
jmp build_union_iter ; Keep going
build_union_done:
mov edi, esi ; MEMBER_SIZE = SIZE
mov eax, [global_token] ; Using global_token
mov eax, [eax] ; global_token->NEXT
mov [global_token], eax ; global_token = global_token->NEXT
mov eax, ecx ; Return last
pop esi ; Restore ESI
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
build_union_string_0: db "ERROR in build_union", 10, "Missing {", 10, 0
build_union_string_1: db "ERROR in build_union", 10, "Missing ;", 10, 0
;; 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
mov edx, eax ; Get char *S safely out of the way
mov eax, 20 ; Using sizeof(struct token_list)
call malloc ; Get pointer to A
mov [eax], ecx ; A->NEXT = LIST
mov [eax+8], edx ; A->S = S
mov [eax+12], ebx ; A->TYPE = T
pop edx ; Restore EDX
ret
;; 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
mov ecx, eax ; S1 in place
mov edx, ebx ; S2 in place
match_Loop:
mov al, [ecx] ; S1[0]
movzx eax, al ; Make it useful
mov bl, [edx] ; S2[0]
movzx ebx, bl ; Make it useful
cmp eax, ebx ; See if they match
jne match_False ; If not
add ecx, 1 ; S1 = S1 + 1
add edx, 1 ; S2 = S2 + 1
cmp eax, 0 ; If reached end of string
je match_Done ; Perfect match
jmp match_Loop ; Otherwise keep looping
match_False:
mov eax, 1 ; Return false
match_Done:
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; 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
mov ebp, eax ; Protect A
mov eax, 16 ; 16bytes
call malloc ; Get our pointer
push eax ; Protect our pointer
mov esi, eax ; put pointer in right place
mov ebx, 0x3B9ACA00 ; Set divisor to largest positive number that fits in 32bits
cmp ebp, 0 ; Deal with 0 case
je numerate_number_ZERO ; If it is
jg numerate_number_positive ; If it is positive
;; Deal with negative case
mov eax, 45 ; Using "-"
mov [esi], al ; Write it
add esi, 1 ; increment
imul ebp, -1 ; A = A * -1
numerate_number_positive:
mov edx, 0 ; Set top to 0
mov eax, ebp ; Using A as bottom
idiv ebx ; edx:eax % ebx -> edx + edx:eax / ebx -> eax [Even if we don't want it]
cmp eax, 0 ; IF 0 == (a / divisor)
jne numerate_number_iter ; Clean up those leading Zeros
mov edx, 0 ; Set top to 0
mov eax, ebx ; Using Divisor for bottom
mov 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]
mov ebx, eax ; Update divisor
jmp numerate_number_positive ; Keep collecting
numerate_number_iter:
cmp ebx, 0 ; IF DIVISOR < 0
jle numerate_number_done ; Be done
mov edx, 0 ; Set top to 0
mov eax, ebp ; Using A as bottom
idiv ebx ; edx:eax % ebx -> edx + edx:eax / ebx -> eax [Even if we don't want it]
add eax, 48 ; ((a / divisor) + 48)
mov [esi], al ; Write it
mov ebp, edx ; a = a % divisor
mov edx, 0 ; Set top to 0
mov eax, ebx ; Using Divisor for bottom
mov 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]
mov ebx, eax ; Update divisor
add esi, 1 ; increment
jmp 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
ret
numerate_number_ZERO:
mov eax, 48 ; Using '0'
mov [esi], al ; Write it
add esi, 1 ; increment
jmp 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
mov ebx, eax ; put S in correct place
mov eax, 0 ; Initialize to Zero
numerate_string_loop:
mov cl, [ebx+1] ; S[1]
movzx ecx, cl ; make it useful
cmp ecx, 120 ; IF 'x' == S[1]
je numerate_hex ; Deal with hex input
;; Assume decimal input
mov esi, 0 ; Assume no negation
mov cl, [ebx] ; S[0]
movzx ecx, cl ; make it useful
cmp ecx, 45 ; IF '-' == S[0]
jne numerate_decimal ; Skip negation
mov esi, 1 ; Set FLAG
add ebx, 1 ; S = S + 1
numerate_decimal:
mov cl, [ebx] ; S[0]
movzx ecx, cl ; make it useful
cmp ecx, 0 ; IF NULL == S[0]
je numerate_decimal_done ; We are done
imul eax, 10 ; VALUE = VALUE * 10
sub ecx, 48 ; CH = CH - '0'
cmp ecx, 9 ; Check for illegal
jg numerate_string_fail ; If CH > '9'
cmp ecx, 0 ; Check for illegal
jl numerate_string_fail ; IF CH < 0
add eax, ecx ; VALUE = VALUE + CH
add ebx, 1 ; S = S + 1
jmp numerate_decimal ; Keep looping
numerate_decimal_done:
cmp esi, 1 ; Check if need to negate
jne numerate_string_done ; Nope
imul eax, -1 ; VALUE = VALUE * -1
jmp numerate_string_done ; Done
numerate_hex:
add ebx, 2 ; S = S + 2
numerate_hex_loop:
mov cl, [ebx] ; S[0]
movzx ecx, cl ; make it useful
cmp ecx, 0 ; IF NULL == S[0]
je numerate_string_done ; We are done
shl eax, 4 ; VALUE = VALUE << 4
sub ecx, 48 ; CH = CH - '0'
cmp ecx, 10 ; IF 10 >= CH
jl numerate_hex_digit ; NO
sub ecx, 7 ; Push A-F into range
numerate_hex_digit:
cmp ecx, 15 ; Check for illegal
jg numerate_string_fail ; If CH > 'F'
cmp ecx, 0 ; Check for illegal
jl numerate_string_fail ; IF CH < 0
add eax, ecx ; VALUE = VALUE + CH
add ebx, 1 ; S = S + 1
jmp numerate_hex_loop ; Keep looping
numerate_string_fail:
mov eax, 0 ; return ZERO
numerate_string_done:
pop esi ; Restore ESI
pop edx ; Restore EDX
pop ecx ; Restore ECX
pop ebx ; Restore EBX
ret
;; Exit_Failure function
;; Receives nothing
;; And aborts hard
;; Does NOT return
Exit_Failure:
mov ebx, 1 ; All is wrong
mov eax, 1 ; put the exit syscall number in eax
int 0x80 ; Call it a bad day
;; Keywords
union: db "union", 0
struct: db "struct", 0
constant: db "CONSTANT", 0
main_string: db "main", 0
argc_string: db "argc", 0
argv_string: db "argv", 0
if_string: db "if", 0
else_string: db "else", 0
do_string: db "do", 0
while_string: db "while", 0
for_string: db "for", 0
asm_string: db "asm", 0
goto_string: db "goto", 0
return_string: db "return", 0
break_string: db "break", 0
continue_string: db "continue", 0
sizeof_string: db "sizeof", 0
plus_string: db "+", 0
minus_string: db "-", 0
multiply_string: db "*", 0
divide_string: db "/", 0
modulus_string: db "%", 0
left_shift_string: db "<<", 0
right_shift_string: db ">>", 0
less_than_string: db "<", 0
less_than_equal_string: db "<=", 0
greater_than_equal_string: db ">=", 0
greater_than_string: db ">", 0
equal_to_string: db "==", 0
not_equal_string: db "!=", 0
bitwise_and: db "&", 0
logical_and: db "&&", 0
bitwise_or: db "|", 0
logical_or: db "||", 0
bitwise_xor: db "^", 0
arrow_string: db "->", 0
;; Frequently Used strings
;; Generally used by require_match
open_curly_brace: db "{", 0
close_curly_brace: db "}", 0
open_paren: db "(", 0
close_paren: db ")", 0
open_bracket: db "[", 0
close_bracket: db "]", 0
comma: db ",", 0
semicolon: db ";", 0
equal: db "=", 0
percent: db "%", 0
newline: db "\n", 0
underline: db "_", 0
prim_types:
type_void:
dd type_int ; NEXT
dd 4 ; SIZE
dd 0 ; OFFSET
dd type_void ; INDIRECT
dd 0 ; MEMBERS
dd type_void ; TYPE
dd type_void_name ; NAME
type_void_name: db "void", 0
type_int:
dd type_char ; NEXT
dd 4 ; SIZE
dd 0 ; OFFSET
dd type_int ; INDIRECT
dd 0 ; MEMBERS
dd type_int ; TYPE
dd type_int_name ; NAME
type_int_name: db "int", 0
type_char:
dd type_file ; NEXT
dd 1 ; SIZE
dd 0 ; OFFSET
dd type_char_indirect ; INDIRECT
dd 0 ; MEMBERS
dd type_char ; TYPE
dd type_char_name ; NAME
type_char_name: db "char", 0
type_char_indirect:
dd type_file ; NEXT
dd 4 ; SIZE
dd 0 ; OFFSET
dd type_char_double_indirect ; INDIRECT
dd 0 ; MEMBERS
dd type_char_indirect ; TYPE
dd type_char_indirect_name ; NAME
type_char_indirect_name: db "char*", 0
type_char_double_indirect:
dd type_file ; NEXT
dd 4 ; SIZE
dd 0 ; OFFSET
dd type_char_double_indirect ; INDIRECT
dd 0 ; MEMBERS
dd type_char_indirect ; TYPE
dd type_char_double_indirect_name ; NAME
type_char_double_indirect_name: db "char**", 0
type_file:
dd type_function ; NEXT
dd 4 ; SIZE
dd 0 ; OFFSET
dd type_file ; INDIRECT
dd 0 ; MEMBERS
dd type_file ; TYPE
dd type_file_name ; NAME
type_file_name: db "FILE", 0
type_function:
dd type_unsigned ; NEXT
dd 4 ; SIZE
dd 0 ; OFFSET
dd type_function ; INDIRECT
dd 0 ; MEMBERS
dd type_function ; TYPE
dd type_function_name ; NAME
type_function_name: db "FUNCTION", 0
type_unsigned:
dd 0 ; NEXT
dd 4 ; SIZE
dd 0 ; OFFSET
dd type_unsigned ; INDIRECT
dd 0 ; MEMBERS
dd type_unsigned ; TYPE
dd type_unsigned_name ; NAME
type_unsigned_name: db "unsigned", 0
;; debug_list function
;; Receives struct token_list* in EAX
;; Prints contents of list and exits
;; Does NOT return
debug_list:
mov ebp, eax ; Protect the list pointer
mov eax, 2 ; Using Standard error
mov [Output_file], eax ; write to standard error
debug_list_iter:
;; Header
mov eax, debug_list_string0 ; Using our first string
call File_Print ; Print it
mov eax, ebp ; Use address of pointer
call numerate_number ; Convert it into string
call File_Print ; Print it
;; NEXT
mov eax, debug_list_string1 ; Using our second string
call File_Print ; Print it
mov eax, [ebp] ; Use address of pointer
call numerate_number ; Convert it into string
call File_Print ; Print it
;; PREV
mov eax, debug_list_string2 ; Using our third string
call File_Print ; Print it
mov eax, [ebp+4] ; Use address of pointer
call numerate_number ; Convert it into string
call File_Print ; Print it
;; S
mov eax, debug_list_string3 ; Using our fourth string
call File_Print ; Print it
mov eax, [ebp+8] ; Use address of pointer
call numerate_number ; Convert it into string
call File_Print ; Print it
;; S Contents
mov eax, debug_list_string4 ; Using our fifth string
call File_Print ; Print it
mov eax, [ebp+8] ; Use address of string
cmp eax, 0 ; IF NULL Pointer
jne debug_list_null ; otherwise display
mov eax, debug_list_string_null ; Give meaningful message instead
debug_list_null:
call File_Print ; Print it
;; TYPE
mov eax, debug_list_string5 ; Using our sixth string
call File_Print ; Print it
mov eax, [ebp+12] ; Use address of pointer
call numerate_number ; Convert it into string
call File_Print ; Print it
;; ARGS/DEPTH
mov eax, debug_list_string6 ; Using our third string
call File_Print ; Print it
mov eax, [ebp+16] ; Use address of pointer
call numerate_number ; Convert it into string
call File_Print ; Print it
mov eax, 10 ; Add "\n"
call fputc ; print it
call fputc ; print it
mov ebp, [ebp] ; TOKEN = TOKEN->NEXT
cmp ebp, 0 ; Check if NULL
jne debug_list_iter ; iterate otherwise
mov ebx, 666 ; All is HELL
mov eax, 1 ; put the exit syscall number in eax
int 0x80 ; Call it a bad day
debug_list_string0: db "Token_list node at address: ", 0
debug_list_string1: db 10, "NEXT address: ", 0
debug_list_string2: db 10, "PREV address: ", 0
debug_list_string3: db 10, "S address: ", 0
debug_list_string4: db 10, "The contents of S are: ", 0
debug_list_string5: db 10, "TYPE address: ", 0
debug_list_string6: db 10, "ARGUMENTS address: ", 0
debug_list_string_null: db ">::<NULL>::<", 0
section .data
Address_of: dd 0
C: dd 0
Input_file: dd 0
MALLOC: dd 0
Output_file: dd 0
Token: dd 0
break_frame: dd 0
break_target_func: dd 0
break_target_head: dd 0
break_target_num: dd 0
current_count: dd 0
current_target: dd 0
function: dd 0
global_constant_list: dd 0
global_function_list: dd 0
global_symbol_list: dd 0
global_token: dd 0
global_types: dd prim_types
globals_list: dd 0
output_list: dd 0
string_index: dd 0
strings_list: dd 0