stage0-uefi/amd64/Development/cc_amd64.S

4961 lines
183 KiB
ArmAsm

# Copyright (C) 2017 Jeremiah Orians
# Copyright (C) 2022 Andrius Štikonas
# 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/>.
.intel_syntax noprefix
.global _start
.text
# Register usage:
# RAX, RSI, RDI => Temps
# R13 => MALLOC
# R15 => stderr flag
# Struct TYPE format: (size 56)
# NEXT => 0
# SIZE => 8
# OFFSET => 16
# INDIRECT => 24
# MEMBERS => 32
# TYPE => 40
# NAME => 48
# Struct TOKEN_LIST format: (size 40)
# NEXT => 0
# LOCALS/PREV => 8
# S => 16
# TYPE => 24
# ARGS/DEPTH => 32
# efi_main(void *image_handle, struct efi_system_table *system)
_start:
# Save non-volatile registers
push rbp
push rbx
push rdi
push rsi
push r12
push r13
push r14
push r15
mov rbp, rsp # save stack pointer
mov [rip+image_handle], rcx # save image_handle
mov [rip+system], rdx # save system
mov r14, [rdx+96] # system->boot
# Open Loaded Image protocol
mov r9, [rip+image_handle] # arg4 = image_handle
lea rdx, [rip+LOADED_IMAGE_PROTOCOL] # guid = &LOADED_IMAGE_PROTOCOL
mov rcx, r9 # arg1 = image_handle
call open_protocol # open protocol
mov rdi, rax # save image
# Get root file system
mov r9, [rip+image_handle] # arg4 = image_handle
lea rdx, [rip+SIMPLE_FS_PROTOCOL] # guid = &SIMPLE_FS_PROTOCOL
mov rcx, [rdi+24] # arg1 = root_device = image->device
mov [rip+root_device], rcx # save root_device
call open_protocol # open protocol
mov rcx, rax # get rootfs
# Get root directory
lea rdx, [rip+rootdir] # arg2 = &rootdir
sub rsp, 40 # allocate shadow stack space for UEFI function
call [rcx+8] # rootfs->open_volume(rootfs, &rootdir)
add rsp, 40 # deallocate stack
# Push command line arguments onto stack
mov rbx, [rdi+56] # options = image->load_options
mov rdx, rbx # save beginning of load_options
add rbx, [rdi+48] # go to the end of load_options
push 0 # Save end of arguments (NULL) onto stack
loop_options:
cmp rbx, rdx # Check if we are done
je loop_options_done # We are done
sub rbx, 2 # --options
mov al, [rbx] # *options
cmp al, 0x20 # if *options != ' '
jne loop_options # then continue looping
mov BYTE PTR [rbx], 0 # zero it
add rbx, 2 # ++options
push rbx # push another argument onto stack
jmp loop_options # next argument
loop_options_done:
pop r8 # arg3 = in
pop r12 # file out
and rsp, -16 # align stack to 16 bytes
# Open file for reading
push 1 # Set exit code in case of failure
cmp r8, 0 # If NULL
je failed_input # then exit
lea rdx, [rip+fin] # arg2 = &fin
push 1 # arg5 = EFI_FILE_READ_ONLY
mov r9, 1 # arg4 = EFI_FILE_MODE_READ
mov rcx, [rip+rootdir] # arg1 = rootdir
sub rsp, 32 # allocate shadow stack space for UEFI function
call [rcx+8] # rootdir->open()
cmp rax, 0 # If failed to open
jne failed_input # then exit
add rsp, 48 # deallocate stack
# Open file for writing
mov r8, r12 # arg3 = out
push 1 # Set exit code in case of failure
cmp r8, 0 # If NULL
je failed_output # then exit
lea rdx, [rip+fout] # arg2 = &fout
push 0 # arg5 = 0
mov r9, 7 # to get 0x8000000000000003 we set the rightmost 3 bits and then do right rotation by 1
ror r9 # arg4 = EFI_FILE_MODE_CREATE| EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ
mov rcx, [rip+rootdir] # arg1 = rootdir
sub rsp, 32 # allocate shadow stack space for UEFI function
call [rcx+8] # rootdir->open()
add rsp, 48 # deallocate stack
# Allocate ourselves 64 MiB of memory
mov rdx, 0x4000000 # allocate 64 MiB of memory for malloc pool
call allocate_pool # allocate memory
mov [rip+malloc_pointer], rax # save malloc pointer
mov [rip+malloc_pool], rax # save the beginning of malloc pool
# Zero allocated memory buffer
add rax, 0x4000000 # end of malloc area
zero_loop:
dec rax # next byte
mov BYTE PTR [rax], 0 # zero it
cmp rax, [rip+malloc_pointer] # if we are not done yet
jne zero_loop # then continue looping
# cc_amd64 needs quite a lot of stack space when building M2-Planet
# which is not guaranteed to be available on UEFI (it guarantees at least 128 KiB).
# Therefore we will allocate an extra space on heap and use part of it for user stack
mov rax, 0x800000 # Allocate 8 MiB for user stack
call malloc
mov [rip+user_stack], rax # Set user stack
call exit_uefi_stack # Switch to user stack
mov r15, 0 # Not writing to stderr yet
call fix_types # Resolve relative addresses in types struct to absolute
mov rax, 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 [rip+global_token], rax # Set global_token
call program # Convert into program
lea rax, [rip+header_string1] # Our header string
call File_Print # Print it
mov rax, [rip+output_list] # Our output_list
call recursive_output # Print core program
# lea rax, [rip+header_string2] # Our Enable debug
# call File_Print # Print it
lea rax, [rip+header_string3] # Our second label
call File_Print # Print it
mov rax, [rip+globals_list] # Our globals
call recursive_output # Get them
lea rax, [rip+header_string4] # Our final header
call File_Print # Print it
mov rax, [rip+strings_list] # Our strings
call recursive_output # Get them
lea rax, [rip+header_string5] # Our final header
call File_Print # Print it
Done:
# program completed Successfully
mov rax, 0 # Set exit code 0
Done_1:
call enter_uefi_stack # Switch back to UEFI stack
push rax # save exit code
# Free pool
mov rcx, [rip+malloc_pool] # arg1 = malloc_pool
call free_pool # system->boot->free_pool(malloc_pool)
mov rcx, [rip+fout] # get fout
call close_file # close fout
failed_output:
mov rcx, [rip+fin] # get fin
call close_file # close fin
failed_input:
mov rcx, [rip+rootdir] # get rootdir
call close_file # close rootdir
mov r8, [rip+image_handle] # arg3 = image_handle
lea rdx, [rip+SIMPLE_FS_PROTOCOL] # guid = &SIMPLE_FS_PROTOCOL
mov rcx, [rip+root_device] # arg1 = root_device
call close_protocol # close protocol
mov r8, [rip+image_handle] # arg3 = image_handle
lea rdx, [rip+LOADED_IMAGE_PROTOCOL] # guid = &LOADED_IMAGE_PROTOCOL
mov rcx, r8 # arg1 = image_handle
call close_protocol # close protocol
pop rax # restore exit code
abort: # used for debugging only
# Restore non-volatile registers
mov rsp, rbp
pop r15
pop r14
pop r13
pop r12
pop rsi
pop rdi
pop rbx
pop rbp
ret # return to UEFI
header_string1: .asciz "\n# Core program\n"
header_string2: .asciz "\n:ELF_data\n"
header_string3: .asciz "\n# Program global variables\n"
header_string4: .asciz "\n# Program strings\n"
header_string5: .asciz "\n:ELF_end\n"
# Resolve relative addresses in types struct to absolute
# Uses RAX to store current type, RBX for temp
fix_types:
push rbx # Protect RBX
lea rax, [rip+prim_types] # Get address of prim_types
mov [rip+global_types], rax # Write it to global_types
fix_type:
mov rbx, [rax+48] # Get offset to NAME
add rbx, rax # Get NAME
mov [rax+48], rbx # Store NAME
mov rbx, [rax+40] # Get offset to TYPE
add rbx, rax # Get TYPE
mov [rax+40], rbx # Store TYPE
mov rbx, [rax+24] # Get offset to INDIRECT
add rbx, rax # Get INDIRECT
mov [rax+24], rbx # Store INDIRECT
mov rbx, [rax] # Get offset to NEXT
cmp rbx, 0 # If no more types
je fix_types_done # Then we are done
add rbx, rax # Get NEXT
mov [rax], rbx # Store NEXT
add rax, 56 # Go to next type
jmp fix_type
fix_types_done:
pop rbx # Restore RBX
ret
# read_all_tokens function
# Receives Token_List* in RAX
# Tokenizes all input and returns updated list in RAX
# Returns TOKEN in RAX
# Uses RAX for C
read_all_tokens:
mov [rip+Token], rax
call fgetc
read_all_tokens_loop:
cmp rax, -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 rax, [rip+Token]
ret
# get_token function
# Receives INT in RAX
# Makes a list of TOKEN_LIST
# C and STRING_INDEX are stored in memory, RCX is used for S and RDX is used for current
# Returns C in RAX
get_token:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
mov [rip+C], rax # Set C
mov rax, 40 # Malloc CURRENT
call malloc # Get Pointer
mov rdx, rax # Set CURRENT
mov rax, 256 # Malloc the string
call malloc # Get pointer to S
mov rcx, rax # Set S
mov [rdx+16], rcx # CURRENT->S = S
reset:
mov [rip+string_index], rcx # S[0]
mov rax, [rip+C] # Using C
call clear_white_space # Clear WhiteSpace
mov [rip+C], rax # Set C
cmp rax, -4 # Check for EOF
je get_token_abort # if EOF abort
cmp rax, 35 # Check for '#'
jne get_token_alpha # Nope
# Deal with # line comments
call purge_macro # Let it handle it
mov [rip+C], rax # Set C
jmp reset # Try again
get_token_alpha:
mov rax, [rip+C] # Send C
lea rbx, [rip+alphas] # Get alphanumerics
call In_Set # See if in set
cmp rax, 1 # IF TRUE
jne get_token_symbol # Otherwise
# Store keywords
mov rax, [rip+C] # Send C
call preserve_keyword # Store
mov [rip+C], rax # Set C
jmp get_token_done # Be done with this token
get_token_symbol:
mov rax, [rip+C] # Send C
lea rbx, [rip+symbols] # Get symbols
call In_Set # See if in set
cmp rax, 1 # IF TRUE
jne get_token_strings # Otherwise
# Store symbols
mov rax, [rip+C] # Send C
call preserve_symbol # Store
mov [rip+C], rax # Set C
jmp get_token_done # Be done with this token
get_token_strings:
mov rax, [rip+C] # Send C
lea rbx, [rip+strings] # Get strings
call In_Set # See if in set
cmp rax, 1 # IF TRUE
jne get_token_comment # Otherwise
# Store String
mov rax, [rip+C] # Send C
call consume_word # Store
mov [rip+C], rax # Set C
jmp get_token_done # Be done with this token
get_token_comment:
mov rax, [rip+C] # Send C
cmp rax, 47 # IF '/' == C
jne get_token_else # Otherwise
call consume_byte # Hope it just is '/'
mov [rip+C], rax # Set C
cmp rax, 42 # IF '*' we have '/*'
jne get_token_comment_line # Check for '//'
# Deal with /* block comments */
call fgetc # get next C
mov [rip+C], rax # Set C
get_token_comment_block_outer:
mov rax, [rip+C] # Using C
cmp rax, 47 # IF '/' != C
je get_token_comment_block_done # be done
get_token_comment_block_inner:
mov rax, [rip+C] # Using C
cmp rax, 42 # IF '*' != C
je get_token_comment_block_iter # jump over
# Deal with inner loop
call fgetc # get next C
mov [rip+C], rax # Set C
jmp get_token_comment_block_inner # keep going
get_token_comment_block_iter:
call fgetc # get next C
mov [rip+C], rax # Set C
jmp get_token_comment_block_outer
get_token_comment_block_done:
call fgetc # get next C
mov [rip+C], rax # Set C
jmp reset # throw away, try again
get_token_comment_line:
cmp rax, 47 # IF '/' we have //
jne get_token_done # keep if just '/'
# Deal with // line comment
call fgetc # drop to match
mov [rip+C], rax # Set C
jmp reset # throw away, try again
get_token_else:
mov rax, [rip+C] # Send C
call consume_byte
mov [rip+C], rax # Set C
get_token_done:
mov rax, [rip+Token] # TOKEN
mov [rdx+8], rax # CURRENT->PREV = TOKEN
mov [rdx], rax # CURRENT->NEXT = TOKEN
mov [rip+Token], rdx # TOKEN = CURRENT
get_token_abort:
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
mov rax, [rip+C] # Return C
ret
# Malloc isn't actually reserving memory here.
# It just updates the pointer in our already reserved storage pool.
malloc:
push rbx # Protect RBX
mov rbx, [rip+malloc_pointer] # Get malloc pointer
xchg rax, rbx # Put it in place
add rbx, rax # Request number of desired bytes
mov [rip+malloc_pointer], rbx # Save malloc_pointer
pop rbx # Restore RBX
ret
# clear_white_space function
# Receives INT C in RAX and FILE* in R15
# Returns first non-whitespace char in RAX
clear_white_space:
cmp rax, 32 # Check for ' '
je clear_white_space_wipe # wipe it out
cmp rax, 10 # Check for '\n'
je clear_white_space_wipe # wipe it output
cmp rax, 9 # Check for '\t'
jne clear_white_space_done # looks like non-whitespace
clear_white_space_wipe:
call fgetc # Read a new byte
cmp rax, -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 RAX and CHAR* in RBX
# Returns 1 if true, zero if false in RAX
In_Set:
push rbx # Protect RBX
push rcx # Protect RCX
In_Set_loop:
mov cl, [rbx] # Read char
movzx rcx, cl # Zero extend it
cmp rax, rcx # See if they match
je In_Set_True # return true
cmp rcx, 0 # Check for NULL
je In_Set_False # return false
add rbx, 1 # s = s + 1
jmp In_Set_loop # Keep looping
In_Set_True:
mov rax, 1 # Set True
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
In_Set_False:
mov rax, 0 # Set FALSE
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
alphas: .asciz "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
symbols: .asciz "<=>|&!-"
strings: .byte 34, 39, 0
# purge_macro function
# Receives CH in RAX
# Reads chars until Line feed is read
# returns line feed
purge_macro:
call fgetc # read next char
cmp rax, 10 # Check for '\n'
jne purge_macro # Keep going
ret
# preserve_keyword function
# Receives INT C in RAX
# collects all chars in keyword
# Returns C in RAX
# Uses RCX for INT C
preserve_keyword:
push rbx # Protect RBX
push rcx # Protect RCX
mov rcx, rax # Setup C
lea rbx, [rip+alphas] # Concerning ourselves with "abc.."
preserve_keyword_loop:
call In_Set # Check if alphanumerics
cmp rax, 1 # IF TRUE
jne preserve_keyword_label # Otherwise check for label
mov rax, rcx # Pass C
call consume_byte # consume that byte
mov rcx, rax # Update C
jmp preserve_keyword_loop # keep looping
preserve_keyword_label:
mov rax, rcx # Fix return
cmp rax, 58 # Check for ':'
jne preserve_keyword_done # be done
# Fix our goto label
call fixup_label # Fix the label
mov rax, 32 # Return Whitespace
preserve_keyword_done:
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# preserve_symbol function
# Receives INT C in RAX
# collects all chars in symbol
# Returns C in RAX
# Uses RCX for INT C
preserve_symbol:
push rbx # Protect RBX
push rcx # Protect RCX
mov rcx, rax # Setup C
lea rbx, [rip+symbols] # Concerning ourselves with "<=>.."
preserve_symbol_loop:
call In_Set # Check if symbol
cmp rax, 1 # IF TRUE
jne preserve_symbol_done # Otherwise be done
mov rax, rcx # Pass C
call consume_byte # consume that byte
mov rcx, rax # Update C
jmp preserve_symbol_loop # keep looping
preserve_symbol_done:
mov rax, rcx # Fix return
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# consume_word function
# receives INT C in RAX
# returns INT C in RAX
# Uses RAX for C, RBX for FREQ and RCX for ESCAPE
consume_word:
push rbx # Protect RBX
push rcx # Protect RCX
mov rbx, rax # FREQ = C
mov rcx, 0 # ESCAPE = FALSE
consume_word_loop:
cmp rcx, 0 # IF !ESCAPE
jne consume_word_escape # Enable escape
cmp rax, 92 # if '\\'
jne consume_word_iter # keep state
mov rcx, 1 # ESCAPE = TRUE
jmp consume_word_iter # keep going
consume_word_escape:
mov rcx, 0 # ESCAPE = FALSE
consume_word_iter:
call consume_byte # read next char
cmp rcx, 0 # IF ESCAPE
jne consume_word_loop # keep looping
cmp rax, rbx # IF C != FREQ
jne consume_word_loop # keep going
call fgetc # return next char
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# consume_byte function
# Receives INT C in RAX
# Inserts C into string S, updates String S
# Returns Next char in RAX
consume_byte:
push rbx # Protect RBX
mov rbx, [rip+string_index] # S[0]
mov [rbx], al # S[0] = C
add rbx, 1 # S = S + 1
mov [rip+string_index], rbx # Update S
call fgetc
pop rbx # Restore RBX
ret
# fixup_label function
# Receives S in RCX
# prepends ':' to string and returns registers unchanged
# Uses RAX for HOLD, RBX for PREV and RCX for S[0]
fixup_label:
push rax # Protect RAX
push rbx # Protect RBX
push rcx # Protect RCX
mov rax, 58 # HOLD = ':'
mov rcx, [rdx+16] # HOLD_STRING[0]
fixup_label_loop:
mov rbx, rax # PREV = HOLD
mov al, [rcx] # HOLD = HOLD_STRING[I]
movzx rax, al # make useful
mov [rcx], bl # HOLD_STRING[I] = PREV
add rcx, 1 # I = I + 1
cmp rax, 0 # IF NULL == HOLD
jne fixup_label_loop # Keep looping
pop rcx # Restore RCX
pop rbx # Restore RBX
pop rax # Restore RAX
ret
# fgetc function
# Returns -4 (EOF) or char in RAX
fgetc:
push rcx # Protect RCX
push rdx # Protect RDX
mov rcx, [rip+fin] # arg1 = fin
push 1 # size = 1
mov rdx, rsp # arg2 = &size
push 0 # zero rsi
mov r8, rsp # arg3 = &input
push rsp # align stack to 16 bytes
push [rsp] # align stack to 16 bytes
and rsp, -16 # align stack to 16 bytes
sub rsp, 32 # allocate shadow stack space for UEFI function
call [rcx+32] # fin->read()
mov rsp, [rsp+40] # deallocate stack
pop rax # save input to rax
pop rsi # save size to rsi
# If the file ended (0 bytes read) return EOF
cmp rsi, 0 # if size == 0
jne fgetc_1
mov rax, -4 # Put EOF in rax
fgetc_1:
pop rdx # Restore RDX
pop rcx # Restore RCX
ret # return
# Reverse_List function
# Receives List in RAX
# Returns the list reversed in RAX
Reverse_List:
push rbx # Protect RBX
push rcx # Protect RCX
mov rbx, rax # Set HEAD
mov rax, 0 # ROOT = NULL
Reverse_List_Loop:
cmp rbx, 0 # WHILE HEAD != NULL
je Reverse_List_Done # Stop otherwise
mov rcx, [rbx] # NEXT = HEAD->NEXT
mov [rbx], rax # HEAD->NEXT = ROOT
mov rax, rbx # ROOT = HEAD
mov rbx, rcx # HEAD = NEXT
jmp Reverse_List_Loop # Keep Going
Reverse_List_Done:
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# recursive_output function
# Receives list in RAX
# walks the list and prints the I->S for all nodes backwards
# Uses RBX for I
recursive_output:
push rbx # Protect RBX
push rcx # Protect RCX
cmp rax, 0 # Check for NULL
je recursive_output_done # Skip the work
mov rbx, rax # I = Head
mov rax, [rbx] # Iterate to next Token
call recursive_output # Recurse
mov rax, [rbx+16] # Using S
call File_Print # Print it
recursive_output_done:
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# File_Print function
# Receives CHAR* in RAX
# calls fputc for every non-null char
File_Print:
push rbx # Protect RBX
push rcx # Protect RCX
mov rbx, rax # Protect S
cmp rax, 0 # Protect against nulls
je File_Print_Done # Simply don't try to print them
File_Print_Loop:
mov al, [rbx] # Read byte
movzx rax, al # zero extend
cmp rax, 0 # Check for NULL
je File_Print_Done # Stop at NULL
call fputc # write it
add rbx, 1 # S = S + 1
jmp File_Print_Loop # Keep going
File_Print_Done:
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# fputc function
# receives CHAR in RAX
# writes char and returns
fputc:
push rcx # Protect RCX
push rdx # Protect RDX
cmp r15, 2 # Check if printing to system error
jne fputc_file # Else print to file
# Print to stderr
cmp rax, '\n' # If we have LF, we need to append CR
pushf # Protect condition
mov rcx, [rip+system] # get system
mov rcx, [rcx+64] # system->out (system->err doesn't print anything for some reason)
mov [rip+WCHAR], rax # Convert to WCHAR
lea rdx, [rip+WCHAR] # arg3 = *WCHAR
push rsp # align stack to 16 bytes
push [rsp] # align stack to 16 bytes
and rsp, -16 # align stack to 16 bytes
sub rsp, 32 # allocate shadow stack space for UEFI function
call [rcx+8] # system->err->output_string(system->err, WCHAR*)
mov rsp, [rsp+40] # deallocate stack
popf # Restore condition
jne fputc_done # We are done if not LF
mov rax, '\r' # Carriage return
call fputc # Print it
jmp fputc_done # We are done
fputc_file:
push rsp # align stack to 16 bytes
push [rsp] # align stack to 16 bytes
and rsp, -16 # align stack to 16 bytes
mov rcx, [rip+fout] # arg1 = fout
push 1 # set size
mov rdx, rsp # arg2 = &size
push rax # allocate stack
mov r8, rsp # arg3 = &output
sub rsp, 32 # allocate shadow stack space for UEFI function
call [rcx+40] # fout->write()
mov rsp, [rsp+56] # deallocate stack
fputc_done:
pop rdx # Restore RDX
pop rcx # Restore RCX
ret # return
# program function
# receives nothing, returns nothing
# Uses RAX for type_size
program:
# The binary initialized the globals to null, so we can skip those steps
push rbx # Protect RBX
push rcx # Protect RCX
new_type:
mov rax, [rip+global_token] # Using global_token
cmp rax, 0 # Check if NULL
je program_done # Be done if null
mov rbx, [rax+16] # GLOBAL_TOKEN->S
lea rax, [rip+constant] # "CONSTANT"
call match # IF GLOBAL_TOKEN->S == "CONSTANT"
cmp rax, 0 # If true
jne program_else # Looks like not a constant
# Deal with minimal constant case
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
mov rax, [rax+16] # global_token->S
mov rbx, 0 # NULL
mov rcx, [rip+global_constant_list] # global_constant_list
call sym_declare # Declare that constant
mov [rip+global_constant_list], rax # global_constant_list = sym_declare(global_token->s, NULL, global_constant_list);
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx] # global_token->next
mov [rax+32], rbx # global_constant_list->arguments = global_token->next
mov rbx, [rbx] # global_token->next->next
mov [rip+global_token], rbx # global_token = global_token->next->next;
jmp new_type # go around again
program_else:
call type_name # Figure out the type_size
cmp rax, 0 # IF NULL == type_size
je new_type # it was a new type
# Add to global symbol table
mov rbx, rax # put type_size in the right spot
mov rax, [rip+global_token] # Using global token
mov rax, [rax+16] # global_token->S
mov rcx, [rip+global_symbol_list] # Using global_symbol_list
call sym_declare # Declare symbol
mov [rip+global_symbol_list], rax # global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list);
mov rbx, [rip+global_token] # Using global token
mov rbx, [rbx] # global_token->next
mov [rip+global_token], rbx # global_token = global_token->next
mov rbx, [rip+global_token] # Using global token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+semicolon] # ";"
call match # if(match(";", global_token->s))
cmp rax, 0 # If true
jne program_function # looks like not a match
# Deal with the global variable
mov rbx, [rip+globals_list] # Using globals_list
lea rax, [rip+program_string_0] # ":GLOBAL_"
call emit # Emit it
mov rbx, rax # update globals_list
mov rax, [rip+global_token] # Using global token
mov rax, [rax+8] # global token->prev
mov rax, [rax+16] # global token->prev->s
call emit # Emit it
mov rbx, rax # update globals_list
lea rax, [rip+program_string_1] # "\nNULL\n"
call emit # Emit it
mov [rip+globals_list], rax # update globals_list
mov rax, [rip+global_token] # Using global token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
jmp new_type # go around again
program_function:
mov rbx, [rip+global_token] # Using global token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+open_paren] # "("
call match # if(match(";", global_token->s))
cmp rax, 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 rcx # Restore RCX
pop rbx # Restore RBX
ret
# Strings needed by the program function
program_string_0: .asciz ":GLOBAL_"
program_string_1: .asciz "\nNULL\n"
# declare_function function
# Receives nothing and returns nothing
# Sets current function and adds it to the global function list
declare_function:
push rbx # Protect RBX
push rcx # Protect RCX
mov rax, 0 # Using NULL
mov [rip+current_count], rax # current_count = 0
mov rax, [rip+global_token] # Using global token
mov rax, [rax+8] # global token->prev
mov rax, [rax+16] # global token->prev->s
mov rbx, 0 # NULL
mov rcx, [rip+global_function_list] # global_function_list
call sym_declare # sym_declare(global_token->prev->s, NULL, global_function_list);
mov [rip+function], rax # function = sym_declare(global_token->prev->s, NULL, global_function_list);
mov [rip+global_function_list], rax # global_function_list = function
call collect_arguments # collect all of the function arguments
mov rax, [rip+global_token] # Using global token
mov rax, [rax+16] # global token->s
lea rbx, [rip+semicolon] # ";"
call match # IF global token->s == ";"
cmp rax, 0 # If true
jne declare_function_full # It was a prototype
# Deal with prototypes
mov rax, [rip+global_token] # Using global token
mov rax, [rax] # global token->next
mov [rip+global_token], rax # global token = global token->next
jmp declare_function_done # Move on
declare_function_full:
# Deal with full function definitions
lea rax, [rip+declare_function_string_0] # "# Defining function "
call emit_out # emit it
mov rax, [rip+function] # function
mov rax, [rax+16] # function->s
call emit_out # emit it
lea rax, [rip+declare_function_string_1] # "\n:FUNCTION_"
call emit_out # emit it
mov rax, [rip+function] # function
mov rax, [rax+16] # function->s
call emit_out # emit it
lea rax, [rip+declare_function_string_3] # "\n"
call emit_out # emit it
call statement # Recursively get the function pieces
mov rax, [rip+output_list] # output
mov rax, [rax+16] # output->s
lea rbx, [rip+declare_function_string_2] # "RETURN\n"
call match # IF output->s == "RETURN\n"
cmp rax, 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;
lea rax, [rip+declare_function_string_2] # "RETURN\n"
call emit_out # emit it
declare_function_done:
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
declare_function_string_0: .asciz "# Defining function "
declare_function_string_1: .asciz "\n:FUNCTION_"
declare_function_string_2: .asciz "RETURN\n"
declare_function_string_3: .byte 10, 0
# collect_arguments function
# Receives nothing
# Returns Nothing
# Adds arguments to the function definition
# holds struct type* type_size in RCX, then replace with struct token_list* a in RCX when type_size is used
collect_arguments:
push rbx # Protect RBX
push rcx # Protect RCX
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
collect_arguments_loop:
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+close_paren] # ")"
call match # IF global_token->S == ")"
cmp rax, 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 rcx, rax # put type_size safely out of the way
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+close_paren] # ")"
call match # IF global_token->S == ")"
cmp rax, 0 # is a foo(int, char,void) case
je collect_arguments_common # deal with commas
# Trying second else
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+comma] # ","
call match # IF global_token->S == ","
cmp rax, 0 # then deal with the common
je collect_arguments_common # case of commas between arguments
# deal with foo(int a, char b)
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov rbx, rcx # put type_size in the right place
mov rcx, [rip+function] # Using function
mov rcx, [rcx+32] # function->args
call sym_declare # sym_declare(global_token->s, type_size, function->arguments);
mov rcx, rax # put a in a safe place
mov rax, [rip+function] # Using function
mov rax, [rax+32] # function->args
cmp rax, 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 rax, -8 # -8
mov [rcx+32], rax # a->depth = -8
jmp collect_arguments_next # get to next
collect_arguments_another:
# deal with the case of non-first arguments
mov rax, [rip+function] # Using function
mov rax, [rax+32] # function->args
mov rax, [rax+32] # function->args->depth
sub rax, 8 # function->args->depth - 8
mov [rcx+32], rax # a->depth = function->args->depth - 8
collect_arguments_next:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
mov rax, [rip+function] # Using function
mov [rax+32], rcx # function->args = a
collect_arguments_common:
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+comma] # ","
call match # IF global_token->S == ","
cmp rax, 0 # then deal with the comma
jne collect_arguments_loop # otherwise loop
# keep foo(bar(), 1) expressions working
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
jmp collect_arguments_loop # keep going
collect_arguments_done:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# statement function
# Receives nothing
# Returns nothing
# Walks down global_token recursively to collect the contents of the function
statement:
push rbx # Protect RBX
push rcx # Protect RCX
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+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, [rbx] # global_token->S[0]
movzx rax, al # make it useful
cmp rax, 58 # IF global_token->S == ':'
jne statement_local # otherwise try locals
# deal with labels
mov rax, rbx # put global_token->S in the right spot
call emit_out # emit it
lea rax, [rip+statement_string_0] # Using "\t#C goto label\n"
call emit_out # emit it
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
jmp statement_done # be done
statement_local:
mov rax, rbx # put global_token->S in the right place
lea rbx, [rip+prim_types] # pointer to primative types
call lookup_type # See if found
cmp rax, 0 # IF NULL == lookup_type(global_token->S, prim_types)
jne statement_local_success # Sweet a new local
# Second chance
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+struct] # "struct"
call match # IF global_token->S == "struct"
cmp rax, 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:
lea rax, [rip+if_string] # Using "if"
call match # IF global_token->S == "if"
cmp rax, 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:
lea rax, [rip+do_string] # Using "do"
call match # IF global_token->S == "do"
cmp rax, 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:
lea rax, [rip+while_string] # Using "while"
call match # IF global_token->S == "while"
cmp rax, 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:
lea rax, [rip+for_string] # Using "for"
call match # IF global_token->S == "for"
cmp rax, 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:
lea rax, [rip+asm_string] # Using "asm"
call match # IF global_token->S == "asm"
cmp rax, 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:
lea rax, [rip+goto_string] # Using "goto"
call match # IF global_token->S == "goto"
cmp rax, 0 # then we have a goto statement
jne statement_return # Otherwise try RETURN
# Deal with GOTO statement
lea rax, [rip+statement_string_1] # Using "JUMP %"
call emit_out # emit it
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
mov rax, [rax+16] # global_token->S
call emit_out # emit it
lea rax, [rip+statement_string_2] # Using "\n"
call emit_out # emit it
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
lea rax, [rip+statement_string_4] # Using "ERROR in statement\nMissing ;\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Make sure it has the required
jmp statement_done # Be done
statement_return:
lea rax, [rip+return_string] # Using "return"
call match # IF global_token->S == "return"
cmp rax, 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:
lea rax, [rip+break_string] # Using "break"
call match # IF global_token->S == "break"
cmp rax, 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:
lea rax, [rip+continue_string] # Using "continue"
call match # IF global_token->S == "continue"
cmp rax, 0 # then we have a continue statement
jne statement_else # Otherwise we are punting to an expression
# Deal with CONTINUE statement
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
lea rax, [rip+statement_string_3] # Using "\n#continue statement\n"
call emit_out # emit it
lea rax, [rip+statement_string_4] # Using "ERROR in statement\nMissing ;\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Don't forget the ";"
jmp statement_done # Be done
statement_else:
call expression # Collect expression
lea rax, [rip+statement_string_4] # Using "ERROR in statement\nMissing ;\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # make sure we have it
statement_done:
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
statement_string_0: .asciz "\t#C goto label\n"
statement_string_1: .asciz "JUMP %"
statement_string_2: .byte 10, 0
statement_string_3: .asciz "\n#continue statement\n"
statement_string_4: .asciz "ERROR in statement\nMissing ;\n"
# recursive_statement function
# Receives nothing
# Returns nothing
# Walks the global_token list to build the contents of statements
# Uses struct token_list* frame in RCX
recursive_statement:
push rbx # Protect RBX
push rcx # Protect RCX
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
mov rcx, [rip+function] # Using function
mov rcx, [rcx+8] # frame = function->locals
recursive_statement_loop:
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+close_curly_brace] # Using "}"
call match # IF global_token->S == "}"
cmp rax, 0 # Then we are done recursing
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 rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
lea rax, [rip+recursive_statement_string_0] # Using "RETURN\n"
mov rbx, [rip+output_list] # Using output
mov rbx, [rbx+16] # output->S
call match # IF output->S == "RETURN\n"
cmp rax, 0 # Then we can skip the clean up
je recursive_statement_done # and be done
# Deal with cleanup
mov rbx, [rip+function] # Using function
mov rbx, [rbx+8] # i = function->locals
lea rax, [rip+recursive_statement_string_1] # Using "POP_RBX\t# _recursive_statement_locals\n"
recursive_statement_locals:
cmp rbx, rcx # IF frame != i
je recursive_statement_done # Otherwise be done
# Lets emit
call emit_out # emit it
mov rbx, [rbx] # i = i->next
jmp recursive_statement_locals # keep going
recursive_statement_done:
mov rax, [rip+function] # Using function
mov [rax+8], rcx # function->locals = frame
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
recursive_statement_string_0: .asciz "RETURN\n"
recursive_statement_string_1: .asciz "POP_RBX\t# _recursive_statement_locals\n"
# return_result function
# Receives nothing
# Returns nothing
# Cleans up function and generates return
# Also handles returning expressions
return_result:
push rbx # Protect RBX
push rcx # Protect RCX
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # make it useful
cmp rax, 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:
lea rax, [rip+return_result_string_0] # Using "ERROR in return_result\nMISSING ;\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Make sure we have it
mov rbx, [rip+function] # Using function
mov rbx, [rbx+8] # function->locals
lea rax, [rip+return_result_string_1] # Using "POP_RBX\t# _return_result_locals\n"
return_result_locals:
cmp rbx, 0 # IF NULL == i
je return_result_done # Be done
call emit_out # Emit out pop
mov rbx, [rbx] # i = i->NEXT
jmp return_result_locals # Keep going
return_result_done:
lea rax, [rip+return_result_string_2] # Using "RETURN\n"
call emit_out # Emit it
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
return_result_string_0: .asciz "ERROR in return_result\nMISSING ;\n"
return_result_string_1: .asciz "POP_RBX\t# _return_result_locals\n"
return_result_string_2: .asciz "RETURN\n"
# collect_local function
# Receives nothing
# Returns nothing
# Walks global_token list to create function locals
# Uses RCX for struct token_list* A
collect_local:
push rbx # Protect RBX
push rcx # Protect RCX
call type_name # Get the local's type
mov rbx, rax # Put struct type* type_size in the right place
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov rcx, [rip+function] # Using function
mov rcx, [rcx+8] # function->locals
call sym_declare # Declare it
mov rcx, rax # put it away safely
# Try for main
lea rax, [rip+main_string] # Using "main"
mov rbx, [rip+function] # Using function
mov rbx, [rbx+16] # function->S
call match # IF match("main", function->s)
cmp rax, 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 rax, [rip+function] # Using function
mov rax, [rax+8] # function->locals
cmp rax, 0 # IF NULL == function->locals
jne collect_local_fresh # try to see if fresh function
# Sweet we are in a fresh main
mov rax, -40 # We start at -40
mov [rcx+32], rax # a->DEPTH = -40
jmp collect_local_common # Go to the commons
collect_local_fresh:
mov rax, [rip+function] # Using function
mov rax, [rax+32] # function->args
cmp rax, 0 # IF NULL == function->args
jne collect_local_first # Otherwise see if first
mov rax, [rip+function] # Using function
mov rax, [rax+8] # function->locals
cmp rax, 0 # IF NULL == function->locals
jne collect_local_first # Otherwise try first
# Sweet we are in a fresh function
mov rax, -16 # We start at -16
mov [rcx+32], rax # a->DEPTH = -16
jmp collect_local_common # Go to the commons
collect_local_first:
mov rax, [rip+function] # Using function
mov rax, [rax+8] # function->locals
cmp rax, 0 # IF NULL == function->locals
jne collect_local_else # Looks like we are just another local
# Ok we are the first local
mov rax, [rip+function] # Using function
mov rax, [rax+32] # function->args
mov rax, [rax+32] # function->args->depth
sub rax, 16 # function->arguments->depth - 16
mov [rcx+32], rax # a->DEPTH = function->arguments->depth - 16
jmp collect_local_common # Go to the commons
collect_local_else:
# Always the last to know
mov rax, [rip+function] # Using function
mov rax, [rax+8] # function->locals
mov rax, [rax+32] # function->locals->depth
sub rax, 8 # function->locals->depth - 8
mov [rcx+32], rax # a->DEPTH = function->locals->depth - 8
collect_local_common:
mov rax, [rip+function] # Using function
mov [rax+8], rcx # function->locals = a
mov rcx, [rcx+16] # a->S
lea rax, [rip+collect_local_string_0] # Using "# Defining local "
call emit_out # emit it
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
call emit_out # emit it
lea rax, [rip+collect_local_string_1] # Using "\n"
call emit_out # emit it
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
mov rbx, [rax+16] # global_token->S
lea rax, [rip+equal] # Using "="
call match # IF match("=", global_token->s)
cmp rax, 0 # Deal with assignment
jne collect_local_done # Otherwise finish it
# Deal with assignment
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
call expression # Recurse
collect_local_done:
lea rax, [rip+collect_local_string_2] # Using "ERROR in collect_local\nMissing ;\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Make sure we have it
lea rax, [rip+collect_local_string_3] # Using "PUSH_RAX\t#"
call emit_out # emit it
mov rax, rcx # put A->S where it belongs
call emit_out # emit it
lea rax, [rip+collect_local_string_1] # Using "\n"
call emit_out # emit it
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
collect_local_string_0: .asciz "# Defining local "
collect_local_string_1: .byte 10, 0
collect_local_string_2: .asciz "ERROR in collect_local\nMissing ;\n"
collect_local_string_3: .asciz "PUSH_RAX\t#"
# process_asm function
# Receives nothing
# Returns nothing
# Simply inlines the asm statements
# Uses RBX for global_token temp storage
process_asm:
push rbx # Protect RBX
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
lea rax, [rip+process_asm_string_0] # Using "ERROR in process_asm\nMISSING (\n"
lea rbx, [rip+open_paren] # Using "("
call require_match # Make sure we have it
mov rbx, [rip+global_token] # Using global_token
process_asm_iter:
mov rax, [rbx+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # Make it useful
cmp rax, 34 # IF global_token->S[0] == '"'
jne process_asm_done # Otherwise be done
mov rax, [rbx+16] # global_token->S
add rax, 1 # global_token->S + 1
call emit_out # Emit it
lea rax, [rip+process_asm_string_1] # Using "\n"
call emit_out # Emit it
mov rbx, [rbx] # global_token->NEXT
mov [rip+global_token], rbx # global_token = global_token->NEXT
jmp process_asm_iter # keep going
process_asm_done:
lea rax, [rip+process_asm_string_2] # Using "ERROR in process_asm\nMISSING )\n"
lea rbx, [rip+close_paren] # Using ")"
call require_match # Make sure we have it
lea rax, [rip+process_asm_string_3] # Using "ERROR in process_asm\nMISSING ;\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Make sure we have it
pop rbx # Restore RBX
ret
process_asm_string_0: .asciz "ERROR in process_asm\nMISSING (\n"
process_asm_string_1: .byte 10, 0
process_asm_string_2: .asciz "ERROR in process_asm\nMISSING )\n"
process_asm_string_3: .asciz "ERROR in process_asm\nMISSING ;\n"
# process_if function
# Receives nothing
# Returns Nothing
# Increments current_count recurses into expression + statement
# Uses RCX for char* NUMBER_STRING
process_if:
push rbx # Protect RBX
push rcx # Protect RCX
mov rax, [rip+current_count] # Using current count
mov rbx, rax # Preparing for update
add rbx, 1 # current_count + 1
mov [rip+current_count], rbx # current_count = current_count + 1
call numerate_number # convert to string
mov rcx, rax # put NUMBER_STRING in place
lea rax, [rip+process_if_string_0] # Using "# IF_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
lea rax, [rip+process_if_string_1] # Using "ERROR in process_if\nMISSING (\n"
lea rbx, [rip+open_paren] # Using "("
call require_match # Make sure we have it
call expression # Recurse to get the IF(...) part
lea rax, [rip+process_if_string_2] # Using "TEST\nJUMP_EQ %ELSE_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_if_string_3] # Using "ERROR in process_if\nMISSING )\n"
lea rbx, [rip+close_paren] # Using ")"
call require_match # Make sure we have it
call statement # Recursive to get the IF(){...} part
lea rax, [rip+process_if_string_4] # Using "JUMP %_END_IF_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_if_string_5] # Using ":ELSE_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
call uniqueID_out # uniqueID_out(function->s, number_string)
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+else_string] # Using "else"
call match # IF global_token->S == "else"
cmp rax, 0 # Then we need to collect the else too
jne process_if_done # Otherwise finish up
# deal with else statement
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
call statement # Recurse to get the ELSE {...} part
process_if_done:
lea rax, [rip+process_if_string_6] # Using ":_END_IF_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
process_if_string_0: .asciz "# IF_"
process_if_string_1: .asciz "ERROR in process_if\nMISSING (\n"
process_if_string_2: .asciz "TEST\nJUMP_EQ %ELSE_"
process_if_string_3: .asciz "ERROR in process_if\nMISSING )\n"
process_if_string_4: .asciz "JUMP %_END_IF_"
process_if_string_5: .asciz ":ELSE_"
process_if_string_6: .asciz ":_END_IF_"
# save_break_frame microfunction
# Overwrites RAX and RBX
# Saves break frame on stack
# Returns to caller
save_break_frame:
pop rbx # Save return Address
mov rax, [rip+break_frame] # Get break_frame
push rax # Store as nested_locals
mov rax, [rip+break_target_head] # Get break_target_head
push rax # Store as nested_break_head
mov rax, [rip+break_target_func] # Get break_target_func
push rax # Store as nested_break_func
mov rax, [rip+break_target_num] # Get break_target_num
push rax # Store as nested_break_num
push rbx # Put return back in place
ret # Return to caller
# restore_break_frame microfunction
# Overwrites RAX and RBX
# Restores break frame from stack
# Returns to caller
restore_break_frame:
pop rbx # Save return Address
pop rax # Get nested_break_num
mov [rip+break_target_num], rax # Restore break_target_num
pop rax # Get nested_break_func
mov [rip+break_target_func], rax # Restore break_target_func
pop rax # Get nested_break_head
mov [rip+break_target_head], rax # Restore break_target_head
pop rax # Get nested_locals
mov [rip+break_frame], rax # Restore break_frame
push rbx # Put return back in place
ret # Return to caller
# set_break_frame microfunction
# Receives char* head in RAX and char* num in RBX
# Overwrites RAX and RBX
# Returns to calling function
set_break_frame:
mov [rip+break_target_head], rax # update break_target_head
mov [rip+break_target_num], rbx # update break_target_num
mov rbx, [rip+function] # Using function
mov rax, [rbx+8] # function->LOCALS
mov [rip+break_frame], rax # break_frame = function->LOCALS
mov rax, [rbx+16] # function->S
mov [rip+break_target_func], rax # 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 RCX for char* NUMBER_STRING
process_do:
push rbx # Protect RBX
push rcx # Protect RCX
call save_break_frame # Save the frame
mov rax, [rip+current_count] # Using current count
mov rbx, rax # Preparing for update
add rbx, 1 # current_count + 1
mov [rip+current_count], rbx # current_count = current_count + 1
call numerate_number # convert to string
mov rcx, rax # put NUMBER_STRING in place
lea rax, [rip+process_do_string_0] # Using "DO_END_"
mov rbx, rcx # Passing NUMBER_STRING
call set_break_frame # Set the frame
lea rax, [rip+process_do_string_1] # Using ":DO_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
call statement # Do the DO {...} part
lea rax, [rip+process_do_string_2] # Using "ERROR in process_do\nMISSING while\n"
lea rbx, [rip+while_string] # Using "while"
call require_match # Make sure we have it
lea rax, [rip+process_do_string_3] # Using "ERROR in process_do\nMISSING (\n"
lea rbx, [rip+open_paren] # Using "("
call require_match # Make sure we have it
call expression # Do the WHILE (...) part
lea rax, [rip+process_do_string_4] # Using "ERROR in process_do\nMISSING )\n"
lea rbx, [rip+close_paren] # Using ")"
call require_match # Make sure we have it
lea rax, [rip+process_do_string_5] # Using "ERROR in process_do\nMISSING ;\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Make sure we have it
lea rax, [rip+process_do_string_6] # Using "TEST\nJUMP_NE %DO_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_do_string_7] # Using ":DO_END_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
call uniqueID_out # uniqueID_out(function->s, number_string)
call restore_break_frame # Restore the old break frame
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
process_do_string_0: .asciz "DO_END_"
process_do_string_1: .asciz ":DO_"
process_do_string_2: .asciz "ERROR in process_do\nMISSING while\n"
process_do_string_3: .asciz "ERROR in process_do\nMISSING (\n"
process_do_string_4: .asciz "ERROR in process_do\nMISSING )\n"
process_do_string_5: .asciz "ERROR in process_do\nMISSING ;\n"
process_do_string_6: .asciz "TEST\nJUMP_NE %DO_"
process_do_string_7: .asciz ":DO_END_"
# process_while function
# Receives nothing
# Returns nothing
# Increments current_count and leverages save/restore_break_frame pieces
# Uses RCX for char* NUMBER_STRING
process_while:
push rbx # Protect RBX
push rcx # Protect RCX
call save_break_frame # Save break_frame
mov rax, [rip+current_count] # Using current count
mov rbx, rax # Preparing for update
add rbx, 1 # current_count + 1
mov [rip+current_count], rbx # current_count = current_count + 1
call numerate_number # convert to string
mov rcx, rax # put NUMBER_STRING in place
lea rax, [rip+process_while_string_0] # Using "END_WHILE_"
mov rbx, rcx # Passing NUMBER_STRING
call set_break_frame # Set it and forget it
lea rax, [rip+process_while_string_1] # Using ":WHILE_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
lea rax, [rip+process_while_string_2] # Using "ERROR in process_while\nMISSING (\n"
lea rbx, [rip+open_paren] # Using "("
call require_match # Make sure we have it
call expression # Deal with the WHILE (...) part
lea rax, [rip+process_while_string_3] # Using "TEST\nJUMP_EQ %END_WHILE_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_while_string_4] # Using "# THEN_while_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_while_string_5] # Using "ERROR in process_while\nMISSING )\n"
lea rbx, [rip+close_paren] # Using ")"
call require_match # Make sure we have it
call statement # Deal with the {....} part
lea rax, [rip+process_while_string_6] # Using "JUMP %WHILE_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_while_string_7] # Using ":END_WHILE_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
call uniqueID_out # uniqueID_out(function->s, number_string)
call restore_break_frame # Restore the old break frame
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
process_while_string_0: .asciz "END_WHILE_"
process_while_string_1: .asciz ":WHILE_"
process_while_string_2: .asciz "ERROR in process_while\nMISSING (\n"
process_while_string_3: .asciz "TEST\nJUMP_EQ %END_WHILE_"
process_while_string_4: .asciz "# THEN_while_"
process_while_string_5: .asciz "ERROR in process_while\nMISSING )\n"
process_while_string_6: .asciz "JUMP %WHILE_"
process_while_string_7: .asciz ":END_WHILE_"
# process_for function
# Receives Nothing
# Returns Nothing
# Increments current_count and leverages save/restore_break_frame pieces
# Uses RCX for char* NUMBER_STRING
process_for:
push rbx # Protect RBX
push rcx # Protect RCX
call save_break_frame # Save the frame
mov rax, [rip+current_count] # Using current count
mov rbx, rax # Preparing for update
add rbx, 1 # current_count + 1
mov [rip+current_count], rbx # current_count = current_count + 1
call numerate_number # convert to string
mov rcx, rax # put NUMBER_STRING in place
lea rax, [rip+process_for_string_0] # Using "FOR_END_"
mov rbx, rcx # Passing NUMBER_STRING
call set_break_frame # Set it and forget it
lea rax, [rip+process_for_string_1] # Using "# FOR_initialization_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
lea rax, [rip+process_for_string_2] # Using "ERROR in process_for\nMISSING (\n"
lea rbx, [rip+open_paren] # Using "("
call require_match # Make Sure we have it
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+semicolon] # Using ";"
call match # IF global_token->S == ";"
cmp rax, 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:
lea rax, [rip+process_for_string_3] # Using ":FOR_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_for_string_4] # Using "ERROR in process_for\nMISSING ;1\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Make sure we have it
call expression # Get the FOR ( ; ... ; Part
lea rax, [rip+process_for_string_5] # Using "TEST\nJUMP_EQ %FOR_END_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_for_string_6] # Using "JUMP %FOR_THEN_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_for_string_7] # Using ":FOR_ITER_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_for_string_8] # Using "ERROR in process_for\nMISSING ;2\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Make sure we have it
call expression # Get the FOR (;;...) part
lea rax, [rip+process_for_string_9] # Using "JUMP %FOR_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_for_string_10] # Using ":FOR_THEN_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_for_string_11] # Using "ERROR in process_for\nMISSING )\n"
lea rbx, [rip+close_paren] # Using ")"
call require_match # Make sure we have it
call statement # Get FOR (;;) {...} part
lea rax, [rip+process_for_string_12] # Using "JUMP %FOR_ITER_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Passing NUMBER_STRING
call uniqueID_out # uniqueID_out(function->s, number_string)
lea rax, [rip+process_for_string_13] # Using ":FOR_END_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
call uniqueID_out # uniqueID_out(function->s, number_string)
call restore_break_frame # Restore the old break frame
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
process_for_string_0: .asciz "FOR_END_"
process_for_string_1: .asciz "# FOR_initialization_"
process_for_string_2: .asciz "ERROR in process_for\nMISSING (\n"
process_for_string_3: .asciz ":FOR_"
process_for_string_4: .asciz "ERROR in process_for\nMISSING ;1\n"
process_for_string_5: .asciz "TEST\nJUMP_EQ %FOR_END_"
process_for_string_6: .asciz "JUMP %FOR_THEN_"
process_for_string_7: .asciz ":FOR_ITER_"
process_for_string_8: .asciz "ERROR in process_for\nMISSING ;2\n"
process_for_string_9: .asciz "JUMP %FOR_"
process_for_string_10: .asciz ":FOR_THEN_"
process_for_string_11: .asciz "ERROR in process_for\nMISSING )\n"
process_for_string_12: .asciz "JUMP %FOR_ITER_"
process_for_string_13: .asciz ":FOR_END_"
# process_break function
# Receives nothing
# Returns nothing
# Handles the break out of loops case
# Uses RBX for struct token_list* break_frame and RCX for struct token_list* I
process_break:
push rbx # Protect RBX
push rcx # Protect RCX
mov rax, [rip+break_target_head] # Catch big error
cmp rax, 0 # IF(NULL == break_target_head)
je process_break_bad # I'm sorry Mr White but you have stage-3 lung cancer
mov rax, [rip+function] # Using function
mov rcx, [rax+8] # I = function->LOCALS
mov rbx, [rip+break_frame] # Put break_frame in the right spot
lea rax, [rip+process_break_string_1] # Using "POP_RBX\t# break_cleanup_locals\n"
process_break_iter:
cmp rcx, 0 # IF (NULL == I)
je process_break_cleaned # We are done
cmp rbx, rcx # IF I != break_frame
je process_break_cleaned # We are done
call emit_out # Emit it
mov rcx, [rcx] # I = I->NEXT
jmp process_break_iter # Keep looping
process_break_cleaned:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
lea rax, [rip+process_break_string_2] # Using "JUMP %"
call emit_out # Emit it
mov rax, [rip+break_target_head] # Get what we are in
call emit_out # Emit it
mov rax, [rip+break_target_func] # Get what function we are in
call emit_out # Emit it
lea rax, [rip+underline] # Using "_"
call emit_out # Emit it
mov rax, [rip+break_target_num] # Get dem digits
call emit_out # Emit it
lea rax, [rip+process_break_string_3] # Using "\n"
call emit_out # Emit it
lea rax, [rip+process_break_string_4] # Using "ERROR in break statement\nMissing ;\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Make sure we have it
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
process_break_bad:
# Breaking badly
mov r15, 2 # write to standard error
# call line_error # Write useful debug info
mov rax, rcx # put S in the right place
call File_Print # print it
lea rax, [rip+process_break_string_0] # Ending string
call File_Print # print it
jmp Exit_Failure # Abort Hard
process_break_string_0: .asciz "Not inside of a loop or case statement"
process_break_string_1: .asciz "POP_RBX\t# break_cleanup_locals\n"
process_break_string_2: .asciz "JUMP %"
process_break_string_3: .byte 10, 0
process_break_string_4: .asciz "ERROR in break statement\nMissing ;\n"
# expression function
# Receives Nothing
# Returns Nothing
# Walks global_token and updates output_list
# Uses RAX and RBX for match and RCX for char* store
expression:
push rbx # Protect RBX
push rcx # Protect RCX
call bitwise_expr # Collect bitwise expressions
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+equal] # "="
call match # IF global_token->S == "="
cmp rax, 0 # We have to deal with assignment
jne expression_done # Looks like nope
# Deal with possible assignment
lea rcx, [rip+expression_string_1] # Assume "STORE_CHAR\n" by default
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+8] # global_token->PREV
mov rbx, [rbx+16] # global_token->PREV->S
lea rax, [rip+close_bracket] # Using "]"
call match # IF global_token->S == "]"
cmp rax, 0 # Then we might have a char
jne expression_int # Otherwise INT
mov rbx, [rip+current_target] # Using current_target
mov rbx, [rbx+48] # current_target->NAME
lea rax, [rip+type_char_indirect_name] # Using "char*"
call match # Intentional inefficiency because I feel like it
cmp rax, 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:
lea rcx, [rip+expression_string_0] # Use "STORE_INTEGER\n"
expression_common:
lea rax, [rip+expression] # Passing expression
call common_recursion # Recurse
mov rax, rcx # Using Store
call emit_out # Emit it
mov rax, 0 # Using NULL
mov [rip+current_target], rax # current_target = NULL
expression_done:
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
expression_string_0: .asciz "STORE_INTEGER\n"
expression_string_1: .asciz "STORE_CHAR\n"
# 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 RAX, RBX, RCX and RDX for passing constants to general recursion
bitwise_expr_stub:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
lea rax, [rip+relational_expr] # Using relational_expr
lea rbx, [rip+bitwise_expr_stub_string_0] # Using "AND_rax_rbx\n"
lea rcx, [rip+bitwise_and] # Using "&"
lea rdx, [rip+bitwise_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+relational_expr] # Using relational_expr
lea rbx, [rip+bitwise_expr_stub_string_0] # Using "AND_rax_rbx\n"
lea rcx, [rip+logical_and] # Using "&&"
lea rdx, [rip+bitwise_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+relational_expr] # Using relational_expr
lea rbx, [rip+bitwise_expr_stub_string_1] # Using "OR_rax_rbx\n"
lea rcx, [rip+bitwise_or] # Using "|"
lea rdx, [rip+bitwise_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+relational_expr] # Using relational_expr
lea rbx, [rip+bitwise_expr_stub_string_1] # Using "OR_rax_rbx\n"
lea rcx, [rip+logical_or] # Using "||"
lea rdx, [rip+bitwise_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+relational_expr] # Using relational_expr
lea rbx, [rip+bitwise_expr_stub_string_2] # Using "XOR_rbx_rax_into_rax\n"
lea rcx, [rip+bitwise_xor] # Using "^"
lea rdx, [rip+bitwise_expr_stub] # And recurse
call general_recursion # Hit it
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
bitwise_expr_stub_string_0: .asciz "AND_rax_rbx\n"
bitwise_expr_stub_string_1: .asciz "OR_rax_rbx\n"
bitwise_expr_stub_string_2: .asciz "XOR_rbx_rax_into_rax\n"
# 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 RAX, RBX, RCX and RDX for passing constants to general recursion
relational_expr_stub:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
lea rax, [rip+additive_expr] # Using additive_expr
lea rbx, [rip+relational_expr_stub_string_0] # Using "CMP\nSETL\nMOVEZX\n"
lea rcx, [rip+less_than_string] # Using "<"
lea rdx, [rip+relational_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+additive_expr] # Using additive_expr
lea rbx, [rip+relational_expr_stub_string_1] # Using "CMP\nSETLE\nMOVEZX\n"
lea rcx, [rip+less_than_equal_string] # Using "<="
lea rdx, [rip+relational_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+additive_expr] # Using additive_expr
lea rbx, [rip+relational_expr_stub_string_2] # Using "CMP\nSETGE\nMOVEZX\n"
lea rcx, [rip+greater_than_equal_string] # Using ">="
lea rdx, [rip+relational_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+additive_expr] # Using additive_expr
lea rbx, [rip+relational_expr_stub_string_3] # Using "CMP\nSETG\nMOVEZX\n"
lea rcx, [rip+greater_than_string] # Using ">"
lea rdx, [rip+relational_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+additive_expr] # Using additive_expr
lea rbx, [rip+relational_expr_stub_string_4] # Using "CMP\nSETE\nMOVEZX\n"
lea rcx, [rip+equal_to_string] # Using "=="
lea rdx, [rip+relational_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+additive_expr] # Using additive_expr
lea rbx, [rip+relational_expr_stub_string_5] # Using "CMP\nSETNE\nMOVEZX\n"
lea rcx, [rip+not_equal_string] # Using "!="
lea rdx, [rip+relational_expr_stub] # And recurse
call general_recursion # Hit it
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
relational_expr_stub_string_0: .asciz "CMP\nSETL\nMOVEZX\n"
relational_expr_stub_string_1: .asciz "CMP\nSETLE\nMOVEZX\n"
relational_expr_stub_string_2: .asciz "CMP\nSETGE\nMOVEZX\n"
relational_expr_stub_string_3: .asciz "CMP\nSETG\nMOVEZX\n"
relational_expr_stub_string_4: .asciz "CMP\nSETE\nMOVEZX\n"
relational_expr_stub_string_5: .asciz "CMP\nSETNE\nMOVEZX\n"
# 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 RAX, RBX, RCX and RDX for passing constants to general recursion
additive_expr_stub:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
lea rax, [rip+postfix_expr] # Using postfix_expr
lea rbx, [rip+additive_expr_stub_string_0] # Using "ADD_rbx_to_rax\n"
lea rcx, [rip+plus_string] # Using "+"
lea rdx, [rip+additive_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+postfix_expr] # Using postfix_expr
lea rbx, [rip+additive_expr_stub_string_1] # Using "SUBTRACT_rax_from_rbx_into_rbx\nMOVE_rbx_to_rax\n"
lea rcx, [rip+minus_string] # Using "-"
lea rdx, [rip+additive_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+postfix_expr] # Using postfix_expr
lea rbx, [rip+additive_expr_stub_string_2] # Using "MULTIPLY_rax_by_rbx_into_rax\n"
lea rcx, [rip+multiply_string] # Using "*"
lea rdx, [rip+additive_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+postfix_expr] # Using postfix_expr
lea rbx, [rip+additive_expr_stub_string_3] # Using "XCHG_rax_rbx\nLOAD_IMMEDIATE_rdx %0\nDIVIDE_rax_by_rbx_into_rax\n"
lea rcx, [rip+divide_string] # Using "/"
lea rdx, [rip+additive_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+postfix_expr] # Using postfix_expr
lea rbx, [rip+additive_expr_stub_string_4] # Using "XCHG_rax_rbx\nLOAD_IMMEDIATE_rdx %0\nMODULUS_rax_from_rbx_into_rbx\nMOVE_rdx_to_rax\n"
lea rcx, [rip+modulus_string] # Using "%"
lea rdx, [rip+additive_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+postfix_expr] # Using postfix_expr
lea rbx, [rip+additive_expr_stub_string_5] # Using "COPY_rax_to_rcx\nCOPY_rbx_to_rax\nSAL_rax_cl\n"
lea rcx, [rip+left_shift_string] # Using "<<"
lea rdx, [rip+additive_expr_stub] # And recurse
call general_recursion # Hit it
lea rax, [rip+postfix_expr] # Using postfix_expr
lea rbx, [rip+additive_expr_stub_string_6] # Using "COPY_rax_to_rcx\nCOPY_rbx_to_rax\nSAR_rax_cl\n"
lea rcx, [rip+right_shift_string] # Using ">>"
lea rdx, [rip+additive_expr_stub] # And recurse
call general_recursion # Hit it
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
additive_expr_stub_string_0: .asciz "ADD_rbx_to_rax\n"
additive_expr_stub_string_1: .asciz "SUBTRACT_rax_from_rbx_into_rbx\nMOVE_rbx_to_rax\n"
additive_expr_stub_string_2: .asciz "MULTIPLY_rax_by_rbx_into_rax\n"
additive_expr_stub_string_3: .asciz "XCHG_rax_rbx\nLOAD_IMMEDIATE_rdx %0\nDIVIDE_rax_by_rbx_into_rax\n"
additive_expr_stub_string_4: .asciz "XCHG_rax_rbx\nLOAD_IMMEDIATE_rdx %0\nMODULUS_rax_from_rbx_into_rbx\nMOVE_rdx_to_rax\n"
additive_expr_stub_string_5: .asciz "COPY_rax_to_rcx\nCOPY_rbx_to_rax\nSAL_rax_cl\n"
additive_expr_stub_string_6: .asciz "COPY_rax_to_rcx\nCOPY_rbx_to_rax\nSAR_rax_cl\n"
# 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 RAX, RBX, RCX and RDX for passing constants to general recursion
postfix_expr_stub:
push rbx # Protect RBX
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+open_bracket] # Using "["
call match # IF global_token->S == "["
cmp rax, 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:
lea rax, [rip+arrow_string] # Using "->"
call match # IF global_token->S == "->"
cmp rax, 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 rbx # Restore RBX
ret
# unary_expr_sizeof function
# Receives nothing
# Returns nothing
# Uses RCX for A->SIZE
unary_expr_sizeof:
push rbx # Protect RBX
push rcx # Protect RCX
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
lea rax, [rip+unary_expr_sizeof_string_0] # Using "ERROR in unary_expr\nMissing (\n"
lea rbx, [rip+open_paren] # Using "("
call require_match # Make sure we have it
call type_name # Get the type
mov rcx, [rax+8] # Set A->TYPE
lea rax, [rip+unary_expr_sizeof_string_1] # Using "ERROR in unary_expr\nMissing )\n"
lea rbx, [rip+close_paren] # Using ")"
call require_match # Make sure we have it
lea rax, [rip+unary_expr_sizeof_string_2] # Using "LOAD_IMMEDIATE_rax %"
call emit_out # Emit it
mov rax, rcx # Put A->SIZE in the right place
call numerate_number # Turn into string
call emit_out # Emit it
lea rax, [rip+unary_expr_sizeof_string_3] # Using "\n"
call emit_out # Emit it
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
unary_expr_sizeof_string_0: .asciz "ERROR in unary_expr\nMissing (\n"
unary_expr_sizeof_string_1: .asciz "ERROR in unary_expr\nMissing )\n"
unary_expr_sizeof_string_2: .asciz "LOAD_IMMEDIATE_rax %"
unary_expr_sizeof_string_3: .byte 10, 0
# postfix_expr_array function
# Receives Nothing
# Returns Nothing
# Uses RBX for struct type* ARRAY and RCX for char* ASSIGN
postfix_expr_array:
push rbx # Protect RBX
push rcx # Protect RCX
mov rax, [rip+current_target] # ARRAY = current_target
push rax # Protect it
lea rax, [rip+expression] # Using expression
call common_recursion # Recurse
pop rbx # Restore array
mov [rip+current_target], rbx # current_target = ARRAY
lea rcx, [rip+postfix_expr_array_string_0] # ASSIGN = "LOAD_INTEGER\n"
lea rax, [rip+type_char_indirect_name] # Using "char*"
mov rbx, [rbx+48] # current_target->NAME
call match # IF current_target->NAME == "char*"
cmp rax, 0 # load a byte
jne postfix_expr_array_large # Otherwise adjust
# Deal with loading byte
lea rcx, [rip+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
lea rax, [rip+postfix_expr_array_string_2] # Using "SAL_rax_Immediate8 !"
call emit_out # Emit it
mov rax, [rip+current_target] # Using current_target
mov rax, [rax+24] # current_target->INDIRECT
mov rax, [rax+8] # 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
lea rax, [rip+postfix_expr_array_string_3] # Using "\n"
call emit_out # Emit it
postfix_expr_array_common:
lea rax, [rip+postfix_expr_array_string_4] # Using "ADD_rbx_to_rax\n"
call emit_out # Emit it
lea rax, [rip+postfix_expr_array_string_5] # Using "ERROR in postfix_expr\nMissing ]\n"
lea rbx, [rip+close_bracket] # Using "]"
call require_match # Make sure we have it
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+equal] # Using "="
call match # IF global_token->S == "="
cmp rax, 0 # We need to preserve address
jne postfix_expr_array_done # Otherwise be done
# Clearing out assign
lea rcx, [rip+postfix_expr_array_string_6] # ASSIGN = ""
postfix_expr_array_done:
mov rax, rcx # Using ASSIGN
call emit_out # Emit it
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
postfix_expr_array_string_0: .asciz "LOAD_INTEGER\n"
postfix_expr_array_string_1: .asciz "LOAD_BYTE\n"
postfix_expr_array_string_2: .asciz "SAL_rax_Immediate8 !"
postfix_expr_array_string_3: .byte 10, 0
postfix_expr_array_string_4: .asciz "ADD_rbx_to_rax\n"
postfix_expr_array_string_5: .asciz "ERROR in postfix_expr\nMissing ]\n"
postfix_expr_array_string_6: .byte 0
# ceil_log2 function
# Receives int a in RAX
# Performs log2 on A and
# Returns result in RAX
# Uses RBX for INT A and RCX for INT RESULT
ceil_log2:
push rbx # Protect RBX
push rcx # Protect RCX
mov rcx, 0 # RESULT = 0
mov rbx, rax # put A in right place
sub rax, 1 # (A - 1)
and rax, rbx # A & (A - 1)
cmp rax, 0 # IF 0 == (A & (A - 1))
jne ceil_log2_iter # Starting from -1
mov rcx, -1 # RESULT = -1
ceil_log2_iter:
cmp rbx, 0 # IF A > 0
jle ceil_log2_done # Otherwise be done
add rcx, 1 # RESULT = RESULT + 1
shr rbx # A = A >> 1
jmp ceil_log2_iter # Keep looping
ceil_log2_done:
mov rax, rcx # Return RESULT
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# postfix_expr_arrow function
# Receives nothing
# Returns nothing
# Emits a bunch and updates current_target
# Uses RBX for struct type* I
postfix_expr_arrow:
push rbx # Protect RBX
lea rax, [rip+postfix_expr_arrow_string_0] # Using "# looking up offset\n"
call emit_out # Emit it
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
mov rbx, [rax+16] # Using global_token->S
mov rax, [rip+current_target] # Using current_target
call lookup_member # lookup_member(current_target, global_token->s)
mov rbx, rax # struct type* I = lookup_member(current_target, global_token->s)
mov rax, [rax+40] # I->TYPE
mov [rip+current_target], rax # current_target = I->TYPE
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
mov rax, [rbx+16] # I->OFFSET
cmp rax, 0 # IF 0 != I->OFFSET
je postfix_expr_arrow_first # Then we don't need to do an offset
# Deal with needing an offset
lea rax, [rip+postfix_expr_arrow_string_1] # Using "# -> offset calculation\nLOAD_IMMEDIATE_rbx %"
call emit_out # Emit it
mov rax, [rbx+16] # I->OFFSET
call numerate_number # Convert to string
call emit_out # Emit it
lea rax, [rip+postfix_expr_arrow_string_2] # Using "\nADD_rbx_to_rax\n"
call emit_out # Emit it
postfix_expr_arrow_first:
mov rax, [rbx+8] # I->SIZE
cmp rax, 4 # IF I->SIZE >= 4
jl postfix_expr_arrow_done # Otherwise be done
# Last chance for load
mov rax, [rip+global_token] # Using global_token
mov rbx, [rax+16] # global_token->S
lea rax, [rip+equal] # Using "="
call match # IF global_token->S == "="
cmp rax, 0 # Then we have assignment and should not load
je postfix_expr_arrow_done # Be done
# Deal with load case
lea rax, [rip+postfix_expr_arrow_string_3] # Using "LOAD_INTEGER\n"
call emit_out # Emit it
postfix_expr_arrow_done:
pop rbx # Restore RBX
ret
postfix_expr_arrow_string_0: .asciz "# looking up offset\n"
postfix_expr_arrow_string_1: .asciz "# -> offset calculation\nLOAD_IMMEDIATE_rbx %"
postfix_expr_arrow_string_2: .asciz "\nADD_rbx_to_rax\n"
postfix_expr_arrow_string_3: .asciz "LOAD_INTEGER\n"
# primary_expr function
# Receives nothing
# Returns nothing
primary_expr:
push rbx # Protect RBX
mov rax, [rip+global_token] # Using global_token
mov rbx, [rax+16] # global_token->S
lea rax, [rip+sizeof_string] # Using "sizeof"
call match # See if match
cmp rax, 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 rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # Make it useful
cmp rax, 45 # IF global_token->S[0] == "-"
jne primary_expr_not # Otherwise try logical NOT
# Deal with negative numbers
lea rax, [rip+primary_expr_string_0] # Using "LOAD_IMMEDIATE_rax %0\n"
call emit_out # Emit it
lea rax, [rip+postfix_expr] # Passing postfix_expr
call common_recursion # Get what it is notting
lea rax, [rip+primary_expr_string_1] # Using "SUBTRACT_rax_from_rbx_into_rbx\nMOVE_rbx_to_rax\n"
call emit_out # Emit it
jmp primary_expr_done # Be done
primary_expr_not:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # Make it useful
cmp rax, 33 # IF global_token->S[0] == "!"
jne primary_expr_bin # Otherwise try '~'
# Deal with logical not
lea rax, [rip+primary_expr_string_2] # Using "LOAD_IMMEDIATE_rax %1\n"
call emit_out # Emit it
lea rax, [rip+postfix_expr] # Passing postfix_expr
call common_recursion # Get what it is notting
lea rax, [rip+primary_expr_string_3] # Using "XOR_rbx_rax_into_rax\n"
call emit_out # Emit it
jmp primary_expr_done # Be done
primary_expr_bin:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # Make it useful
cmp rax, 126 # IF global_token->S[0] == "~"
jne primary_expr_paren # Otherwise try paren
# Deal with binary NOT
lea rax, [rip+postfix_expr] # Passing postfix_expr
call common_recursion # Get what it is notting
lea rax, [rip+primary_expr_string_4] # Using "NOT_rax\n"
call emit_out # Emit it
jmp primary_expr_done # Be done
primary_expr_paren:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # Make it useful
cmp rax, 40 # IF global_token->S[0] == "("
jne primary_expr_ch # Otherwise try char
# deal with nesting
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
call expression # Lets recurse
lea rax, [rip+primary_expr_string_5] # Using "Error in Primary expression\nDidn't get )\n"
lea rbx, [rip+close_paren] # Using ")"
call require_match # Make sure we have it
jmp primary_expr_done # Be done
primary_expr_ch:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # Make it useful
cmp rax, 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 rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # Make it useful
cmp rax, 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 rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # Make it useful
lea rbx, [rip+primary_expr_string_6] # Using "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
call In_Set # See if we have a match
cmp rax, 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 rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # Make it useful
lea rbx, [rip+primary_expr_string_7] # Using "0123456789"
call In_Set # See if we have a match
cmp rax, 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 rbx # Restore RBX
ret
primary_expr_string_0: .asciz "LOAD_IMMEDIATE_rax %0\n"
primary_expr_string_1: .asciz "SUBTRACT_rax_from_rbx_into_rbx\nMOVE_rbx_to_rax\n"
primary_expr_string_2: .asciz "LOAD_IMMEDIATE_rax %1\n"
primary_expr_string_3: .asciz "XOR_rbx_rax_into_rax\n"
primary_expr_string_4: .asciz "NOT_rax\n"
primary_expr_string_5: .asciz "Error in Primary expression\nDidn't get )\n"
primary_expr_string_6: .asciz "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
primary_expr_string_7: .asciz "0123456789"
# primary_expr_variable function
# Receives nothing
# Returns nothing
# Walks global and updates output
# Uses RAX for struct token_list* a and RCX for char* S
primary_expr_variable:
push rbx # Protect RBX
push rcx # Protect RCX
mov rax, [rip+global_token] # Using global_token
mov rcx, [rax+16] # S = global_token->S
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
mov rax, rcx # Using S
mov rbx, [rip+global_constant_list] # Using global_constant_list
call sym_lookup # sym_lookup(s, global_constant_list)
cmp rax, 0 # IF NULL == sym_lookup(s, global_constant_list)
je primary_expr_variable_local # Try locals next
# Deal with constant load
mov rbx, [rax+32] # a->ARGS
lea rax, [rip+primary_expr_variable_string_2] # Using "LOAD_IMMEDIATE_rax %"
call emit_out # Emit it
mov rax, [rbx+16] # a->ARGS->S
call emit_out # Emit it
lea rax, [rip+primary_expr_variable_string_1] # Using "\n"
call emit_out # Emit it
jmp primary_expr_variable_done # Be done
primary_expr_variable_local:
mov rax, rcx # Using S
mov rbx, [rip+function] # Using function
mov rbx, [rbx+8] # function->locals
call sym_lookup # sym_lookup(s, function->locals)
cmp rax, 0 # IF NULL == sym_lookup(s, function->locals)
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 rax, rcx # Using S
mov rbx, [rip+function] # Using function
mov rbx, [rbx+32] # function->args
call sym_lookup # sym_lookup(s, function->args)
cmp rax, 0 # IF NULL == sym_lookup(s, function->args)
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 rax, rcx # Using S
mov rbx, [rip+global_function_list] # Using global_function_list
call sym_lookup # sym_lookup(s, global_function_list)
cmp rax, 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 rax, rcx # Using S
mov rbx, [rip+global_symbol_list] # Using global_symbol_list
call sym_lookup # sym_lookup(s, global_symbol_list)
cmp rax, 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 r15, 2 # write to standard error
# call line_error # Write useful debug info
mov rax, rcx # put S in the right place
call File_Print # print it
lea rax, [rip+primary_expr_variable_string_0] # Ending string
call File_Print # print it
jmp Exit_Failure # Abort Hard
primary_expr_variable_done:
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
primary_expr_variable_string_0: .asciz " is not a defined symbol\n"
primary_expr_variable_string_1: .byte 10, 0
primary_expr_variable_string_2: .asciz "LOAD_IMMEDIATE_rax %"
# function_call function
# Receives char* S in RAX and int BOOL in RBX
# Builds stack frames before and tears them down after function calls
# Uses RCX for char* S, RDX for int BOOL, RSI for PASSED
function_call:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
push rsi # Protect RSI
mov rcx, rax # Put S in place
mov rdx, rbx # Put BOOL in place
mov rsi, 0 # PASSED = 0
lea rax, [rip+function_call_string_0] # Using "ERROR in process_expression_list\nNo ( was found\n"
lea rbx, [rip+open_paren] # Using "("
call require_match # Make sure we have it
lea rax, [rip+function_call_string_1] # Using "PUSH_RDI\t# Prevent overwriting in recursion\n"
call emit_out # Emit it
lea rax, [rip+function_call_string_2] # Using "PUSH_RBP\t# Protect the old base pointer\n"
call emit_out # Emit it
lea rax, [rip+function_call_string_3] # Using "COPY_RSP_to_RDI\t# Copy new base pointer\n"
call emit_out # Emit it
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # Make it useful
cmp rax, 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
lea rax, [rip+function_call_string_4] # Using "PUSH_RAX\t#_process_expression1\n"
call emit_out # Emit it
mov rsi, 1 # PASSED = 1
function_call_gen_iter:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # Make it useful
cmp rax, 44 # IF global_token->S[0] == ","
jne function_call_gen_done # Otherwise we are done
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
call expression # Collect the argument
lea rax, [rip+function_call_string_5] # Using "PUSH_RAX\t#_process_expression2\n"
call emit_out # Emit it
add rsi, 1 # PASSED = PASSED + 1
jmp function_call_gen_iter # Keep trying
function_call_gen_done:
# All is collected
lea rax, [rip+function_call_string_6] # Using "ERROR in process_expression_list\nNo ) was found\n"
lea rbx, [rip+close_paren] # Using ")"
call require_match # Make sure we have it
cmp rdx, 0 # IF(BOOL == TRUE)
jne function_call_static # Otherwise it is a static call
# Deal with a passed function pointer
lea rax, [rip+function_call_string_7] # Using "LOAD_BASE_ADDRESS_rax %"
call emit_out # Emit it
mov rax, rcx # Using S
call emit_out # Emit it
lea rax, [rip+function_call_string_8] # Using "\nLOAD_INTEGER\n"
call emit_out # Emit it
lea rax, [rip+function_call_string_9] # Using "COPY_rdi_to_rbp\n"
call emit_out # Emit it
lea rax, [rip+function_call_string_10] # Using "CALL_rax\n"
call emit_out # Emit it
lea rax, [rip+function_call_string_13] # Using "POP_RBX\t# _process_expression_locals\n"
jmp function_call_cleanup # Clean up
function_call_static:
# Deal with fixed function name
lea rax, [rip+function_call_string_9] # Using "COPY_rdi_to_rbp\n"
call emit_out # Emit it
lea rax, [rip+function_call_string_11] # Using "CALL_IMMEDIATE %FUNCTION_"
call emit_out # Emit it
mov rax, rcx # Using S
call emit_out # Emit it
lea rax, [rip+function_call_string_12] # Using "\n"
call emit_out # Emit it
lea rax, [rip+function_call_string_13] # Using "POP_RBX\t# _process_expression_locals\n"
function_call_cleanup:
cmp rsi, 0 # IF PASSED > 0
jle function_call_done # Otherwise be done
# The desired string is already in RAX
call emit_out # Emit it
sub rsi, 1 # PASSED = PASSED - 1
jmp function_call_cleanup # Keep going
function_call_done:
lea rax, [rip+function_call_string_14] # Using "POP_rbp\t# Restore old base pointer\n"
call emit_out # Emit it
lea rax, [rip+function_call_string_15] # Using "POP_rdi\t# Prevent overwrite\n"
call emit_out # Emit it
pop rsi # Restore RSI
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
function_call_string_0: .asciz "ERROR in process_expression_list\nNo ( was found\n"
function_call_string_1: .asciz "PUSH_RDI\t# Prevent overwriting in recursion\n"
function_call_string_2: .asciz "PUSH_RBP\t# Protect the old base pointer\n"
function_call_string_3: .asciz "COPY_RSP_to_RDI\t# Copy new base pointer\n"
function_call_string_4: .asciz "PUSH_RAX\t#_process_expression1\n"
function_call_string_5: .asciz "PUSH_RAX\t#_process_expression2\n"
function_call_string_6: .asciz "ERROR in process_expression_list\nNo ) was found\n"
function_call_string_7: .asciz "LOAD_BASE_ADDRESS_rax %"
function_call_string_8: .asciz "\nLOAD_INTEGER\n"
function_call_string_9: .asciz "COPY_rdi_to_rbp\n"
function_call_string_10: .asciz "CALL_rax\n"
function_call_string_11: .asciz "CALL_IMMEDIATE %FUNCTION_"
function_call_string_12: .byte 10, 0
function_call_string_13: .asciz "POP_RBX\t# _process_expression_locals\n"
function_call_string_14: .asciz "POP_RBP\t# Restore old base pointer\n"
function_call_string_15: .asciz "POP_RDI\t# Prevent overwrite\n"
# variable_load function
# Receives struct token_list* A in RAX
# Returns nothing
# Updates output and current_target
# Uses RCX for A
variable_load:
push rbx # Protect RBX
push rcx # Protect RCX
mov rcx, rax # Protect A
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+open_paren] # Using "("
call match # IF global_token->S == "("
cmp rax, 0 # Then it might be a function
jne variable_load_regular # Otherwise it is regular
mov rbx, [rcx+24] # A->TYPE
mov rbx, [rbx+48] # A->TYPE->NAME
lea rax, [rip+type_function_name] # Using "FUNCTION"
call match # IF A->TYPE->NAME == "FUNCTION"
cmp rax, 0 # Then it must be a function
jne variable_load_regular # otherwise just another regular
# deal with function
mov rax, [rcx+32] # A->DEPTH
call numerate_number # Convert to string
mov rbx, 0 # pass 0 for true
call function_call # Create the function call
jmp variable_load_done # Be done
variable_load_regular:
mov rax, [rcx+24] # A->TYPE
mov [rip+current_target], rax # current_target = A->TYPE
lea rax, [rip+variable_load_string_0] # Using "LOAD_BASE_ADDRESS_rax %"
call emit_out # Emit it
mov rax, [rcx+32] # A->DEPTH
call numerate_number # Convert to string
call emit_out # Emit it
lea rax, [rip+variable_load_string_1] # Using "\n"
call emit_out # Emit it
# Check for special case of assignment
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+equal] # Using "="
call match # IF global_token->S == "="
cmp rax, 0 # Then we skip loading
je variable_load_done # And be done
# Deal with common case
lea rax, [rip+variable_load_string_2] # Using "LOAD_INTEGER\n"
call emit_out # Emit it
variable_load_done:
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
variable_load_string_0: .asciz "LOAD_BASE_ADDRESS_rax %"
variable_load_string_1: .byte 10, 0
variable_load_string_2: .asciz "LOAD_INTEGER\n"
# function_load function
# Receives struct token_list* a in RAX
# Returns nothing
# Uses RCX to hold A->S
function_load:
push rbx # Protect RBX
push rcx # Protect RCX
mov rax, [rax+16] # A->S
mov rcx, rax # Protect A->S
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+open_paren] # Using "("
call match # IF global_token->S == "("
cmp rax, 0 # The we need to do a function call
jne function_load_regular # Otherwise just load it's address
# Deal with function call
mov rax, rcx # Using A->S
mov rbx, 1 # Using FALSE
call function_call # Deal with it
jmp function_load_done # Be done
function_load_regular:
lea rax, [rip+function_load_string_0] # Using "LOAD_IMMEDIATE_rax &FUNCTION_"
call emit_out # Emit it
mov rax, rcx # Using A->S
call emit_out # Emit it
lea rax, [rip+function_load_string_1] # Using "\n"
call emit_out # Emit it
function_load_done:
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
function_load_string_0: .asciz "LOAD_IMMEDIATE_rax &FUNCTION_"
function_load_string_1: .byte 10, 0
# global_load function
# Receives struct token_list* A in RAX
# Returns nothing
# Uses RBX to hold A->S
global_load:
push rbx # Protect RBX
mov rbx, rax # Set as A
mov rbx, [rbx+16] # Set as A->S
mov rax, [rax+24] # A->TYPE
mov [rip+current_target], rax # current_target = A->TYPE
lea rax, [rip+global_load_string_0] # Using "LOAD_IMMEDIATE_rax &GLOBAL_"
call emit_out # Emit it
mov rax, rbx # Using A->S
call emit_out # Emit it
lea rax, [rip+global_load_string_1] # Using "\n"
call emit_out # Emit it
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+equal] # "="
call match # IF global_token->S == "="
cmp rax, 0 # We need to skip for assignment
je global_load_done # and be done
# Otherwise we are loading the contents
lea rax, [rip+global_load_string_2] # Using "LOAD_INTEGER\n"
call emit_out # Emit it
global_load_done:
pop rbx # Restore RBX
ret
global_load_string_0: .asciz "LOAD_IMMEDIATE_rax &GLOBAL_"
global_load_string_1: .byte 10, 0
global_load_string_2: .asciz "LOAD_INTEGER\n"
# sym_lookup function
# Receives char* S in RAX and struct token_list* symbol_list in RBX
# Uses I->S in RAX, S in RBX and I in RCX
# Returns match or NULL
sym_lookup:
push rbx # Protect RBX
push rcx # Protect RCX
mov rcx, rbx # I = symbol_list
mov rbx, rax # Put S in the right place
sym_lookup_iter:
cmp rcx, 0 # IF NULL == I
je sym_lookup_done # We failed to find match
mov rax, [rcx+16] # Using I->S
call match # IF I->S == S
cmp rax, 0 # then be done
je sym_lookup_done # Failed
mov rcx, [rcx] # I = I->NEXT
jmp sym_lookup_iter # otherwise keep looping
sym_lookup_done:
mov rax, rcx # Return I
pop rcx # Restore RCX
pop rbx # Restore RBX
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:
lea rax, [rip+primary_expr_number_string_0] # Using "LOAD_IMMEDIATE_rax %"
call emit_out # Emit it
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
call emit_out # Emit it
lea rax, [rip+primary_expr_number_string_1] # Using "\n"
call emit_out # Emit it
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
ret
primary_expr_number_string_0: .asciz "LOAD_IMMEDIATE_rax %"
primary_expr_number_string_1: .byte 10, 0
# primary_expr_string function
# receives nothing
# Returns nothing
# creates entries for string and calls to generate string output
# uses RCX for char* number_string
primary_expr_string:
push rbx # Protect RBX
push rcx # Protect RCX
mov rbx, [rip+current_count] # Using current_count
mov rax, rbx # And putting it in the right place
call numerate_number # Get the string
mov rcx, rax # protect number_string
add rbx, 1 # current_count + 1
mov [rip+current_count], rbx # current_count = current_count + 1
lea rax, [rip+primary_expr_string_string_0] # Using "LOAD_IMMEDIATE_rax &STRING_"
call emit_out # Emit it
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
mov rbx, rcx # Put number_string in the right place
call uniqueID_out # Make it unique
# Generate the target
lea rax, [rip+primary_expr_string_string_1] # Using ":STRING_"
mov rbx, [rip+strings_list] # Using strings_list
call emit # Emit it
mov rbx, rax # put new strings_list in place
mov rax, [rip+function] # Using function
mov rax, [rax+16] # function->S
call uniqueID # Make it unique
mov rbx, rax # put new strings_list in place
# Parse the string
mov rax, [rip+global_token] # Using global token
mov rax, [rax+16] # global_token->S
call parse_string # convert to useful form
call emit # Emit it
mov [rip+strings_list], rax # Update Strings _list
mov rax, [rip+global_token] # Using global token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
primary_expr_string_string_0: .asciz "LOAD_IMMEDIATE_rax &STRING_"
primary_expr_string_string_1: .asciz ":STRING_"
# primary_expr_char function
# Receives nothing
# Returns nothing
# Updates output_list using global_token
primary_expr_char:
push rbx # Protect RBX
push rcx # Protect RCX
lea rax, [rip+primary_expr_char_string_0] # Using "LOAD_IMMEDIATE_rax %"
call emit_out # Emit it
mov rax, [rip+global_token] # Using global token
mov rax, [rax+16] # global_token->S
add rax, 1 # global_token->S + 1
call escape_lookup # Get the char
call numerate_number # Convert to string
call emit_out # emit it
lea rax, [rip+primary_expr_char_string_1] # Using "\n"
call emit_out # Emit it
mov rax, [rip+global_token] # Using global token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
primary_expr_char_string_0: .asciz "LOAD_IMMEDIATE_rax %"
primary_expr_char_string_1: .byte 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 r15, 2 # write to standard error
lea rax, [rip+primary_expr_failure_string_0] # Using "Received "
call File_Print # Print it
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
call File_Print # Print it
lea rax, [rip+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: .asciz "Received "
primary_expr_failure_string_1: .asciz " in primary_expr\n"
# general_recursion function
# Receives FUNCTION F in RAX, char* S in RBX, char* name in RCX and FUNCTION iterate in RDX
# Returns nothing
# Uses RCX for char* S, RDX for FUNCTION iterate and RBP for FUNCTION F
# But generally recurses a shitload
general_recursion:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
push rbp # Protect RBP
mov rbp, rax # Protect F
mov rax, rcx # Put name in the right place
mov rcx, rbx # Protect S
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
call match # IF match(name, global_token->s)
cmp rax, 0 # If true we do
jne general_recursion_done # Otherwise skip it
# Deal with the recursion
mov rax, rbp # Put F in the right place
call common_recursion # Recurse
mov rax, rcx # Put S in the right place
call emit_out # Emit it
mov rax, rdx # Put iterate in the right place
call rax # Down the rabbit hole
general_recursion_done:
pop rbp # Restore RBP
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# promote_type function
# Receives struct type* a in RAX and struct type* b in RBX
# Returns the most recent type in RAX
# Uses RAX for struct type* I, RCX for struct type* A and RDX for struct type* B
promote_type:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
cmp rbx, 0 # IF NULL == B
je promote_type_done # Just return A
mov rcx, rax # Put A in place
mov rdx, rbx # Put B in place
mov rax, rbx # IF NULL == A
cmp rcx, 0 # Then we just return B
je promote_type_done # Be done
# Looks like we need to walk the list
mov rcx, [rcx+48] # A->NAME
mov rdx, [rdx+48] # B->NAME
mov rax, [rip+global_types] # I = global_types
promote_type_iter:
cmp rax, 0 # IF NULL == I
je promote_type_done # Just be done
mov rbx, [rax+48] # I->NAME
cmp rbx, rcx # IF(A->NAME == I->NAME)
je promote_type_done # Be done
cmp rbx, rdx # IF(B->NAME == I->NAME)
je promote_type_done # Be done
mov rbx, [rax+24] # I->INDIRECT
mov rbx, [rbx+48] # I->INDIRECT->NAME
cmp rbx, rcx # IF(A->NAME == I->INDIRECT->NAME)
je promote_type_done # Be done
cmp rbx, rdx # IF(B->NAME == I->INDIRECT->NAME)
je promote_type_done # Be done
mov rax, [rax] # I = I->NEXT
jmp promote_type_iter # Keep going
promote_type_done:
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# common_recursion function
# Receives FUNCTION F in RAX
# Returns Nothing
# Walks global_token list and update output_list
# Updates current_target
# Uses RBX to hold FUNCTION F and struct type* last_type
common_recursion:
push rbx # Protect RBX
mov rbx, rax # Put FUNCTION F safely out of the way
lea rax, [rip+common_recursion_string_0] # Using "PUSH_RAX\t#_common_recursion\n"
call emit_out # Emit it
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
mov rax, rbx # Prepare for function call
mov rbx, [rip+current_target] # Get last type
call rax # F();
mov rax, [rip+current_target] # Get current_target
call promote_type # get the right type
mov [rip+current_target], rax # Set new current_target
lea rax, [rip+common_recursion_string_1] # Using "POP_RBX\t# _common_recursion\n"
call emit_out # Emit it
pop rbx # Restore RBX
ret
common_recursion_string_0: .asciz "PUSH_RAX\t#_common_recursion\n"
common_recursion_string_1: .asciz "POP_RBX\t# _common_recursion\n"
# require_match function
# Receives char* message in RAX and char* required in RBX
# Returns nothing
# Uses RCX to hold message and updates global_token
require_match:
push rbx # Protect RBX
push rcx # Protect RCX
mov rcx, rax # put the message somewhere safe
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
call match # IF required == global_token->S
cmp rax, 0 # we are fine
je require_match_good # otherwise pain
# Deal with bad times
# call line_error # Tell user what went wrong
mov r15, 2 # write to standard error
mov rax, rcx # using our message
call File_Print # Print it
jmp Exit_Failure # Abort HARD
require_match_good:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->next
mov [rip+global_token], rax # global_token = global_token->next
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# uniqueID Function
# Receives char *S in RAX, struct token_list* l in RBX and char* num in RCX
# Returns updated struct token_list* L in RAX
uniqueID:
push rbx # Protect RBX
push rcx # Protect RCX
call emit # emit(s, l)
mov rbx, rax # Put L in correct place
lea rax, [rip+underline] # Using "_"
call emit # emit("_", l)
mov rbx, rax # Put L in correct place
mov rax, rcx # Put num in correct place
call emit # emit(num, l)
mov rbx, rax # Put L in correct place
lea rax, [rip+uniqueID_string_0] # Using "\n"
call emit # emit("\n", l)
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
uniqueID_string_0: .byte 10, 0
# uniqueID_out function
# Receives char* S in RAX and char* num in RBX
# Returns nothing
uniqueID_out:
push rax # Protect RAX
push rbx # Protect RBX
push rcx # Protect RCX
mov rcx, rbx # Put num in right spot
mov rbx, [rip+output_list] # Using output_list
call uniqueID # Get updated list
mov [rip+output_list], rax # output_list = uniqueID(s, output_list, num)
pop rcx # Restore RCX
pop rbx # Restore RBX
pop rax # Restore RAX
ret
# emit_out function
# Receives char* S in RAX
# Returns nothing
# Updates output_list
# MUST NOT ALTER REGISTERS
emit_out:
push rax # Protect RAX
push rbx # Protect RBX
mov rbx, [rip+output_list] # Using output_list
call emit # emit it
mov [rip+output_list], rax # update it
pop rbx # Restore RBX
pop rax # Restore RAX
ret
# emit function
# Receives char *s in RAX and struct token_list* head in RBX
# Returns struct token_list* T in RAX
emit:
push rcx # Protect RCX
mov rcx, rax # put S out of the way
mov rax, 40 # sizeof(struct token_list)
call malloc # get T
mov [rax], rbx # t->next = head;
mov [rax+16], rcx # t->s = s;
pop rcx # Restore RCX
ret
# escape_lookup function
# Receives char* c in RAX
# Returns integer value of char in RAX
# Aborts hard if unknown escape is received
# Uses RCX to hold char* C
escape_lookup:
push rbx # Protect RBX
push rcx # Protect RCX
mov rcx, rax # Put char* C in safe place
mov al, [rcx] # Load c[0]
movzx rax, al # make it useful
cmp rax, 92 # If '\\' != c[0]
jne escape_lookup_done # Be done
mov rbx, rcx # Prepare for walk
add rbx, 1 # increment
mov bl, [rbx] # load c[1]
movzx rbx, bl # make it useful
cmp rbx, 120 # Check if \x??
je escape_lookup_hex # Deal with hex
# Deal with \? escapes
mov rax, 10 # Guess "\n"
cmp rbx, 110 # If n
je escape_lookup_done # Be done
mov rax, 9 # Guess "\t"
cmp rbx, 116 # If t
je escape_lookup_done # Be done
mov rax, rbx # "\\", "'" and '"' all encode as themselves
cmp rbx, 92 # If "\\"
je escape_lookup_done # Be done
cmp rbx, 39 # IF "'"
je escape_lookup_done # Be done
cmp rbx, 34 # IF '"'
je escape_lookup_done # Be done
mov rax, 13 # Guess "\r"
cmp rbx, 114 # IF r
je escape_lookup_done # Be done
# Looks like we have no clue what we are doing
# Aborting hard
mov r15, 2 # write to standard error
lea rax, [rip+escape_lookup_string_0] # Using "Unknown escape received: "
call File_Print # Print it
mov rax, rcx # Using C
call File_Print # Print it
lea rax, [rip+escape_lookup_string_1] # Using " Unable to process\n"
call File_Print # Print it
jmp Exit_Failure # Abort Hard
escape_lookup_done:
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
escape_lookup_hex:
# Give up on C and just assume they know what they are doing
add rcx, 2 # increment
mov al, [rcx] # c[2]
movzx rax, al # make it useful
add rcx, 1 # increment
call char2hex # Get the hex value
sal rax, 4 # c << 4
mov bl, [rcx] # c[3]
movzx rbx, bl # make it useful
xchg rax, rbx # protect c << 4
call char2hex # Get the hex value
add rax, rbx # hex(c[2]) << 4 + hex(c[3])
jmp escape_lookup_done # Be done
escape_lookup_string_0: .asciz "Unknown escape received: "
escape_lookup_string_1: .asciz " Unable to process\n"
# char2hex function
# Receives char in RAX
# Returns hex or aborts hard
char2hex:
sub rax, 48 # Try 0-9
cmp rax, 10 # Otherwise fun times
jl char2hex_done # Be done
# Deal with A-F
and rax, 0xDF # Unset High bit turning a-f into A-F
sub rax, 7 # Shift down into position
cmp rax, 10 # Everything below A is bad
jl char2hex_fail # And should fail
cmp rax, 16 # Make sure we are below F
jl char2hex_done # If so be done
char2hex_fail:
# Time to fail hard
mov r15, 2 # write to standard error
lea rax, [rip+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: .asciz "Tried to print non-hex number\n"
# parse_string function
# Receives char* string in RAX
# Returns cleaned up string
# Protects char* string in RBX
parse_string:
push rbx # Protect RBX
mov rbx, rax # Protect char* string
call weird # Determine if we have a weird string
cmp rax, 0 # If weird
je parse_string_weird # Deal with it
# Dealing with regular string
mov rax, rbx # Passing Char* string
call collect_regular_string # Collect it
jmp parse_string_done # Be done
parse_string_weird:
mov rax, rbx # Passing Char* string
call collect_weird_string # Collect it
parse_string_done:
pop rbx # Restore RBX
ret
# weird function
# Receives char* string in RAX
# Returns true(0) or false(1) in RAX
# Uses RCX to hold char* string
weird:
push rbx # Protect RBX
push rcx # Protect RCX
mov rcx, rax # Place string in safe place
add rcx, 1 # increment past the "
weird_reset:
mov al, [rcx] # Load a char
movzx rax, al # Make it useful
cmp rax, 0 # IF NULL == C
je weird_false # Nothing weird found
cmp rax, 92 # IF '\\'
jne weird_escaped # Deal with escaping
# Deal with escape
mov rax, rcx # We are passing the string
call escape_lookup # to look it up
add rcx, 1 # string = string + 1
mov bl, [rcx] # get string[1]
movzx rbx, bl # make it useful
cmp rbx, 120 # IF 'x' == string[1]
jne weird_escaped # otherwise skip the gap
add rcx, 2 # string = string + 2
weird_escaped:
push rax # Protect C in case we need it
lea rbx, [rip+weird_string_0] # Use "\t\n !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
call In_Set # To find if weird
cmp rax, 1 # IF TRUE
pop rax # Restore C
jne weird_true # Then not weird
add rcx, 1 # string = string + 1
# Last chance for weird
lea rbx, [rip+weird_string_1] # Use "\t\n\r "
call In_Set # Check for special case
cmp rax, 1 # IF TRUE
jne weird_reset # Otherwise not in the special case
# Deal with possible special case
mov al, [rcx] # Load string[1]
movzx rax, al # Make it useful
cmp rax, 58 # IF string[1] == ":"
je weird_true # Then we hit the special case
jmp weird_reset # Keep trying
weird_done:
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
weird_true:
mov rax, 0 # Return true
jmp weird_done # Be done
weird_false:
mov rax, 1 # Return false
jmp weird_done # Be done
weird_string_0: .byte 9, 10
.asciz " !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
weird_string_1: .byte 9, 10, 13, 32, 0
# collect_regular_string function
# Receives char* string in RAX
# Malloc and creates new string to return in RAX
# Uses RCX for return string and RDX for passed string
collect_regular_string:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
mov rdx, rax # Protect our passed string
mov rax, 256 # We need 256 bytes of storage
call malloc # Get our new pointer
mov rcx, rax # put it in place
push rax # protect until done
collect_regular_string_reset:
mov al, [rdx] # string[0]
movzx rax, al # Make it useful
cmp rax, 0 # See if we hit the end
je collect_regular_string_done # And be done
cmp rax, 92 # IF string[0] == '\\'
je collect_regular_string_escaped # Deal with that mess
# deal with boring char
mov [rcx], al # hold_string[index] = string[0]
add rcx, 1 # Increment it
add rdx, 1 # Increment it
jmp collect_regular_string_reset # And keep going
collect_regular_string_escaped:
mov rax, rdx # Using string
call escape_lookup # Get the char
mov [rcx], al # hold_string[index] = escape_lookup(string)
add rdx, 1 # Increment it
add rcx, 1 # Increment it
mov al, [rdx] # string[0]
movzx rax, al # Make it useful
add rdx, 1 # Increment it
cmp rax, 120 # IF 'x' == string[1]
jne collect_regular_string_reset # Otherwise keep going
add rdx, 2 # Increment it
jmp collect_regular_string_reset # Keep going
collect_regular_string_done:
mov rax, 34 # Using '"'
mov [rcx], al # hold_string[index] = '"'
add rcx, 1 # Increment it
mov rax, 10 # Using "\n"
mov [rcx], al # hold_string[index] = '\n'
pop rax # Return our new string
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# collect_weird_string function
# Receives char* string in RAX
# Mallocs and returns char* hold in RAX
# Uses RCX for char* hold and RDX for char* string
collect_weird_string:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
mov rdx, rax # Protect our passed string
mov rax, 512 # We need 512 bytes of storage
call malloc # Get our new pointer
mov rcx, rax # put it in place
push rax # protect until done
mov rax, 39 # Using "'"
mov [rcx], al # hold_string[index] = "'"
add rcx, 1 # Increment it
add rdx, 1 # Increment it
collect_weird_string_reset:
mov al, [rdx] # Read a byte
movzx rax, al # Make it useful
cmp rax, 0 # IF NULL == string[0]
je collect_weird_string_done # Be done
mov rax, 32 # Using ' '
mov [rcx], al # hold_string[index] = ' '
add rcx, 1 # Increment it
mov rax, rdx # Using string
call escape_lookup # Get the char
call hex8 # Update RCX
mov al, [rdx] # Read a byte
movzx rax, al # Make it useful
add rdx, 1 # Increment it
cmp rax, 92 # IF string[0] == '\\'
jne collect_weird_string_reset # Otherwise keep going
mov al, [rdx] # Read a byte
movzx rax, al # Make it useful
add rdx, 1 # Increment it
cmp rax, 120 # IF 'x' == string[1]
jne collect_weird_string_reset # Otherwise keep going
add rdx, 2 # Increment it
jmp collect_weird_string_reset # Keep going
collect_weird_string_done:
mov rax, 32 # Using ' '
mov [rcx], al # hold_string[index] = ' '
add rcx, 1 # Increment it
mov rax, 48 # Using '0'
mov [rcx], al # hold_string[index] = '0'
add rcx, 1 # Increment it
mov [rcx], al # hold_string[index] = '0'
add rcx, 1 # Increment it
mov rax, 39 # Using "'"
mov [rcx], al # hold_string[index] = "'"
add rcx, 1 # Increment it
mov rax, 10 # Using "\n"
mov [rcx], al # hold_string[index] = '\n'
pop rax # Return our new string
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# HEX to ascii routine
# Receives INT in RAX and CHAR* in RCX
# Stores ascii of INT in CHAR*
# Returns only modifying RAX and RCX
hex8:
push rax # Protect bottom nibble
shr rax, 4 # do high nibble first
call hex4 # Store it
pop rax # do low nibble
hex4:
and rax, 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 [rcx], al # store result
add rcx, 1 # next position
ret
# type_name function
# Receives nothing
# Returns type_size in RAX
# Uses RCX for STRUCT TYPE* RET
type_name:
push rbx # Protect RBX
push rcx # Protect RCX
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx+16] # global_token->S
lea rax, [rip+struct] # Using "struct"
call match # IF global_token->S == "struct"
mov rcx, rax # Protect structure
cmp rax, 0 # need to skip over "struct"
jne type_name_native # otherwise keep going
# Deal with possible STRUCTs
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx] # global_token->next
mov [rip+global_token], rbx # global_token = global_token->next
mov rax, [rbx+16] # global_token->S
mov rbx, [rip+global_types] # get all known types
call lookup_type # Find type if possible
mov rcx, rax # Set ret
cmp rax, 0 # IF NULL == ret
jne type_name_common # We have to create struct
# Create a struct
call create_struct # Create a new struct
mov rcx, 0 # We wish to return NULL
jmp type_name_done # be done
type_name_native:
# Deal only with native types
mov rax, rbx # Put global_token->S in the right place
mov rbx, [rip+global_types] # get all known types
call lookup_type # Find the type if possible
mov rcx, rax # Set ret
cmp rax, 0 # IF NULL == ret
jne type_name_common # We need to abort hard
# Aborting hard
mov r15, 2 # write to standard error
lea rax, [rip+type_name_string_0] # Print header
call File_Print # Print it
mov rax, [rip+global_token] # Using global token
mov rax, [rax+16] # global_token->S
call File_Print # Print it
lea rax, [rip+type_name_string_1] # Print footer
call File_Print # Print it
# call line_error # Give details
jmp Exit_Failure # Abort
type_name_common:
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx] # global_token->next
mov [rip+global_token], rbx # global_token = global_token->next
type_name_iter:
mov rax, [rbx+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # make it useful
cmp rax, 42 # IF global_token->S[0] == '*'
jne type_name_done # recurse
# Deal with char**
mov rcx, [rcx+24] # ret = ret->indirect
mov rbx, [rip+global_token] # Using global_token
mov rbx, [rbx] # global_token->next
mov [rip+global_token], rbx # global_token = global_token->next
jmp type_name_iter # keep looping
type_name_done:
mov rax, rcx # put ret in the right place
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
type_name_string_0: .asciz "Unknown type "
type_name_string_1: .byte 10, 0
# lookup_type function
# Receives char* s in RAX and struct type* start in RBX
# Returns struct type* in RAX
# Uses RBX for S and RCX for I
lookup_type:
push rbx # Protect RBX
push rcx # Protect RCX
mov rcx, rbx # I = Start
mov rbx, rax # Put S in place
lookup_type_iter:
cmp rcx, 0 # Check if I == NULL
je lookup_type_done # return NULL
mov rax, [rcx+48] # I->NAME
call match # Check if matching
cmp rax, 0 # IF I->NAME == S
je lookup_type_done # return it
mov rcx, [rcx] # Otherwise I = I->NEXT
jmp lookup_type_iter # And keep looping
lookup_type_done:
mov rax, rcx # return either I or NULL
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# create_struct function
# Receives nothing
# Returns nothing
# Uses global_token to malloc a struct's definition
# Uses RCX for int OFFSET, RDX for struct type* head, RBP for struct type* I,
# RDI for member_size (Which is passed) and RSI for LAST
# RAX and RBX are used for scratch
create_struct:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
push rbp # Protect RBP
push rdi # Protect RDI
push rsi # Protect RSI
mov rcx, 0 # OFFSET = 0
mov rdi, 0 # member_size = 0
mov rax, 56 # sizeof(struct type)
call malloc # malloc(sizeof(struct type))
mov rdx, rax # Set HEAD
mov rax, 56 # sizeof(struct type)
call malloc # malloc(sizeof(struct type))
mov rbp, rax # Set I
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov [rdx+48], rax # HEAD->NAME = global_token->S
mov [rbp+48], rax # I->NAME = global_token->S
mov [rdx+24], rbp # HEAD->INDIRECT = I
mov [rbp+24], rdx # I->INDIRECT = HEAD
mov rax, [rip+global_types] # Using global_types
mov [rdx], rax # HEAD->NEXT = global_types
mov [rip+global_types], rdx # global_types = HEAD
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
mov rax, 8 # Using register size
mov [rbp+8], rax # I->SIZE = register size
lea rax, [rip+create_struct_string_0] # Using "ERROR in create_struct\n Missing {\n"
lea rbx, [rip+open_curly_brace] # Using "{"
call require_match # Make sure we have it
mov rsi, 0 # LAST = NULL
create_struct_iter:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # Make it useful
cmp rax, 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 rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
lea rbx, [rip+union] # Using "union"
call match # IF match(global_token->s, "union")
cmp rax, 0 # Deal with union
jne create_struct_single # Otherwise deal with singles
# Deal with union
mov rax, rsi # Put last in right place
mov rbx, rcx # put offset in right place
call build_union # ASSEMBLE
mov rsi, rax # last = build_union(last, offset)
add rcx, rdi # offset = offset + member_size
lea rax, [rip+create_struct_string_1] # Using "ERROR in create_struct\n Missing ;\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Make sure we have it
jmp create_struct_iter # keep going
create_struct_single:
# deal with singles
mov rax, rsi # Put last in right place
mov rbx, rcx # put offset in right place
call build_member # ASSEMBLE
mov rsi, rax # last = build_union(last, offset)
add rcx, rdi # offset = offset + member_size
lea rax, [rip+create_struct_string_1] # Using "ERROR in create_struct\n Missing ;\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Make sure we have it
jmp create_struct_iter # keep going
create_struct_done:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
lea rax, [rip+create_struct_string_1] # Using "ERROR in create_struct\n Missing ;\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Make sure we have it
mov [rdx+8], rcx # HEAD->SIZE = OFFSET
mov [rdx+32], rsi # HEAD->MEMBERS = LAST
mov [rbp+32], rsi # I->MEMBERS = LAST
pop rsi # Restore RSI
pop rdi # Restore RDI
pop rbp # Restore RBP
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
create_struct_string_0: .asciz "ERROR in create_struct\n Missing {\n"
create_struct_string_1: .asciz "ERROR in create_struct\n Missing ;\n"
# lookup_member function
# Receives struct type* parent in RAX and char* name in RBX
# Returns struct type* I in RAX
# Uses char* NAME in RBX, RCX for struct type* I and RDX to hold parent for errors
# Aborts hard if not found
lookup_member:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
mov rdx, rax # Protect Parent
mov rcx, [rax+32] # struct type* I = parent->MEMBERS
lookup_member_iter:
cmp rcx, 0 # IF I == NULL
je lookup_member_fail # Abort HARD
mov rax, [rcx+48] # Using I->NAME
call match # IF I->NAME == NAME
cmp rax, 0 # Then we have found the member
mov rax, rcx # Prepare for return
mov rcx, [rcx+32] # Prepare for loop I = I->MEMBERS
jne lookup_member_iter # Looks like we are looping
# I is already in RAX
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
lookup_member_fail:
mov r15, 2 # write to standard error
lea rax, [rip+lookup_member_string_0] # Using "ERROR in lookup_member "
call File_Print # print it
mov rax, [rdx+48] # PARENT->NAME
call File_Print # print it
lea rax, [rip+arrow_string] # Using "->"
call File_Print # print it
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
call File_Print # print it
lea rax, [rip+lookup_member_string_1] # Using " does not exist\n"
call File_Print # print it
# call line_error # Write useful debug info
lea rax, [rip+lookup_member_string_2] # Using "\n"
call File_Print # print it
jmp Exit_Failure # Abort Hard
lookup_member_string_0: .asciz "ERROR in lookup_member "
lookup_member_string_1: .asciz " does not exist\n"
lookup_member_string_2: .byte 10, 0
# build_member function
# Receives struct type* last in RAX, int offset in RBX and global member_size in RDI
# Updates member_size in RDI and returns struct type* I in RAX
# Uses RCX for struct type* member_type and RDX for struct type* I
build_member:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
mov rdx, rax # Put last out of the way
mov rax, 56 # Allocate type
call malloc # Get I
mov [rax+32], rdx # I->MEMBERS = LAST
mov [rax+16], rbx # I->OFFSET = OFFSET
mov rdx, rax # Put I in place
call type_name # Get member_type
mov rcx, rax # Put in place
mov [rdx+40], rcx # I->TYPE = MEMBER_TYPE
mov rax, [rip+global_token] # Using global_token
mov rbx, [rax+16] # global_token->S
mov [rdx+48], rbx # I->NAME = global_token->S
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
# Check if we have an array
mov rbx, [rax+16] # global_token->S
lea rax, [rip+open_bracket] # Using "["
call match # IF global_token->S == "["
cmp rax, 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 rax, [rcx+8] # member_type->SIZE
mov [rdx+8], rax # I->SIZE = member_type->SIZE
jmp build_member_done # Be done
build_member_array:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
mov rax, [rax+16] # global_token->S
call numerate_string # convert number
mov rbx, [rcx+40] # member_type->TYPE
mov rbx, [rbx+8] # member_type->TYPE->SIZE
imul rax, rbx # member_type->type->size * numerate_string(global_token->s)
mov [rdx+8], rax # I->SIZE = member_type->type->size * numerate_string(global_token->s)
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
lea rax, [rip+build_member_string_0] # Using "Struct only supports [num] form\n"
lea rbx, [rip+close_bracket] # Using "]"
call require_match # Make sure we have it
build_member_done:
mov rdi, [rdx+8] # MEMBER_SIZE = I->SIZE
mov [rdx+40], rcx # I->TYPE = MEMBER_TYPE
mov rax, rdx # Return I
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
build_member_string_0: .asciz "Struct only supports [num] form\n"
# build_union function
# Receives struct type* last in RAX, int offset in RBX and global member_size in RDI
# Updates member_size in RDI and returns struct type* LAST in RAX
# Uses RCX for struct type* last, RDX for int offset, RSI for int size and RDI for int member_size
build_union:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
push rsi # Protect RSI
mov rcx, rax # Put LAST in right spot
mov rdx, rbx # Put OFFSET in right spot
mov rsi, 0 # SIZE = 0
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
lea rax, [rip+build_union_string_0] # Using "ERROR in build_union\nMissing {\n"
lea rbx, [rip+open_curly_brace] # Using "{"
call require_match # Make sure we have it
build_union_iter:
mov rax, [rip+global_token] # Using global_token
mov rax, [rax+16] # global_token->S
mov al, [rax] # global_token->S[0]
movzx rax, al # make it useful
cmp rax, 125 # IF global_token->S[0] == "}"
je build_union_done # Be done
# Collect union member
mov rax, rcx # Passing LAST
mov rbx, rdx # Passing offset
call build_member # build_member(last, offset)
mov rcx, rax # last = build_member(last, offset)
cmp rsi, rdi # IF member_size > size
jg build_union_size # Then update size
# deal with member_size > size
mov rsi, rdi # SIZE = MEMBER_SIZE
build_union_size:
lea rax, [rip+build_union_string_1] # Using "ERROR in build_union\nMissing ;\n"
lea rbx, [rip+semicolon] # Using ";"
call require_match # Make sure we have it
jmp build_union_iter # Keep going
build_union_done:
mov rdi, rsi # MEMBER_SIZE = SIZE
mov rax, [rip+global_token] # Using global_token
mov rax, [rax] # global_token->NEXT
mov [rip+global_token], rax # global_token = global_token->NEXT
mov rax, rcx # Return last
pop rsi # Restore RSI
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
build_union_string_0: .asciz "ERROR in build_union\nMissing {\n"
build_union_string_1: .asciz "ERROR in build_union\nMissing ;\n"
# sym_declare function
# Receives char *s in RAX, struct type* t in RBX, and struct token_list* list in RCX
# Returns struct token_list* in RAX
# Uses RAX for A
sym_declare:
push rdx # Protect RDX
mov rdx, rax # Get char *S safely out of the way
mov rax, 40 # Using sizeof(struct token_list)
call malloc # Get pointer to A
mov [rax], rcx # A->NEXT = LIST
mov [rax+16], rdx # A->S = S
mov [rax+24], rbx # A->TYPE = T
pop rdx # Restore RDX
ret
# match function
# Receives CHAR* in RAX and CHAR* in RBX
# Returns 0 (TRUE) or 1 (FALSE) in RAX
match:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
mov rcx, rax # S1 in place
mov rdx, rbx # S2 in place
match_Loop:
mov al, [rcx] # S1[0]
movzx rax, al # Make it useful
mov bl, [rdx] # S2[0]
movzx rbx, bl # Make it useful
cmp rax, rbx # See if they match
jne match_False # If not
add rcx, 1 # S1 = S1 + 1
add rdx, 1 # S2 = S2 + 1
cmp rax, 0 # If reached end of string
je match_Done # Perfect match
jmp match_Loop # Otherwise keep looping
match_False:
mov rax, 1 # Return false
match_Done:
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# numerate_number function
# Receives an INT A in RAX
# Returns char* result in RAX
# Allocates 16 bytes of memory
# Behaves badly when given a negative number too large
# Uses RAX for temp, RBX for DIVISOR, RDX for mod/0, RSI for result[i] and RBP for A
numerate_number:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
push rsi # Protect RSI
push rbp # Protect RBP
mov rbp, rax # Protect A
mov rax, 16 # 16 bytes
call malloc # Get our pointer
push rax # Protect our pointer
mov rsi, rax # put pointer in right place
mov rbx, 1000000000 # Set divisor to largest positive number that fits in 32bits
cmp rbp, 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 rax, 45 # Using "-"
mov [rsi], al # Write it
add rsi, 1 # increment
imul rbp, -1 # A = A * -1
numerate_number_positive:
mov rdx, 0 # Set top to 0
mov rax, rbp # Using A as bottom
idiv rbx # rdx:rax % rbx -> rdx + rdx:rax / rbx -> rax [Even if we don't want it]
cmp rax, 0 # IF 0 == (a / divisor)
jne numerate_number_iter # Clean up those leading Zeros
mov rdx, 0 # Set top to 0
mov rax, rbx # Using Divisor for bottom
mov rbx, 10 # Make this shit work because idiv 10 doesn't work
idiv rbx # rdx:rax % 10 -> rdx + rdx:rax / 10 -> rax [Even if we don't want it]
mov rbx, rax # Update divisor
jmp numerate_number_positive # Keep collecting
numerate_number_iter:
cmp rbx, 0 # IF DIVISOR < 0
jle numerate_number_done # Be done
mov rdx, 0 # Set top to 0
mov rax, rbp # Using A as bottom
idiv rbx # rdx:rax % rbx -> rdx + rdx:rax / rbx -> rax [Even if we don't want it]
add rax, 48 # ((a / divisor) + 48)
mov [rsi], al # Write it
mov rbp, rdx # a = a % divisor
mov rdx, 0 # Set top to 0
mov rax, rbx # Using Divisor for bottom
mov rbx, 10 # Make this shit work because idiv 10 doesn't work
idiv rbx # rdx:rax % 10 -> rdx + rdx:rax / 10 -> rax [Even if we don't want it]
mov rbx, rax # Update divisor
add rsi, 1 # increment
jmp numerate_number_iter # Keep going
numerate_number_done:
pop rax # Restore our result
pop rbp # Restore RBP
pop rsi # Restore RSI
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
numerate_number_ZERO:
mov rax, 48 # Using '0'
mov [rsi], al # Write it
add rsi, 1 # increment
jmp numerate_number_done # Be done
# numerate_string function
# Receives CHAR* in RAX
# Returns value of CHAR* in RAX
# Uses RAX for VALUE, RBX for S, RCX for CH and RSI for NEGATIVE?
numerate_string:
push rbx # Protect RBX
push rcx # Protect RCX
push rdx # Protect RDX
push rsi # Protect RSI
mov rbx, rax # put S in correct place
mov rax, 0 # Initialize to Zero
numerate_string_loop:
mov cl, [rbx+1] # S[1]
movzx rcx, cl # make it useful
cmp rcx, 120 # IF 'x' == S[1]
je numerate_hex # Deal with hex input
# Assume decimal input
mov rsi, 0 # Assume no negation
mov cl, [rbx] # S[0]
movzx rcx, cl # make it useful
cmp rcx, 45 # IF '-' == S[0]
jne numerate_decimal # Skip negation
mov rsi, 1 # Set FLAG
add rbx, 1 # S = S + 1
numerate_decimal:
mov cl, [rbx] # S[0]
movzx rcx, cl # make it useful
cmp rcx, 0 # IF NULL == S[0]
je numerate_decimal_done # We are done
imul rax, 10 # VALUE = VALUE * 10
sub rcx, 48 # CH = CH - '0'
cmp rcx, 9 # Check for illegal
jg numerate_string_fail # If CH > '9'
cmp rcx, 0 # Check for illegal
jl numerate_string_fail # IF CH < 0
add rax, rcx # VALUE = VALUE + CH
add rbx, 1 # S = S + 1
jmp numerate_decimal # Keep looping
numerate_decimal_done:
cmp rsi, 1 # Check if need to negate
jne numerate_string_done # Nope
imul rax, -1 # VALUE = VALUE * -1
jmp numerate_string_done # Done
numerate_hex:
add rbx, 2 # S = S + 2
numerate_hex_loop:
mov cl, [rbx] # S[0]
movzx rcx, cl # make it useful
cmp rcx, 0 # IF NULL == S[0]
je numerate_string_done # We are done
shl rax, 4 # VALUE = VALUE << 4
sub rcx, 48 # CH = CH - '0'
cmp rcx, 10 # IF 10 >= CH
jl numerate_hex_digit # NO
sub rcx, 7 # Push A-F into range
numerate_hex_digit:
cmp rcx, 15 # Check for illegal
jg numerate_string_fail # If CH > 'F'
cmp rcx, 0 # Check for illegal
jl numerate_string_fail # IF CH < 0
add rax, rcx # VALUE = VALUE + CH
add rbx, 1 # S = S + 1
jmp numerate_hex_loop # Keep looping
numerate_string_fail:
mov rax, 0 # return ZERO
numerate_string_done:
pop rsi # Restore RSI
pop rdx # Restore RDX
pop rcx # Restore RCX
pop rbx # Restore RBX
ret
# Exit_Failure function
# Receives nothing
# And aborts hard
# Does NOT return
Exit_Failure:
mov rax, 1 # All is wrong
jmp Done_1 # Exit gracefully
# rcx: file handle
close_file:
push rsp # align stack to 16 bytes
push [rsp] # align stack to 16 bytes
and rsp, -16 # align stack to 16 bytes
sub rsp, 32 # allocate shadow stack space for UEFI function
call [rcx+16] # file_handle->close(file_handle)
mov rsp, [rsp+40] # deallocate stack
ret
# rcx: handle
# rdx: &guid
# r9: agent_handle
# returns interface
open_protocol:
push rax # allocate stack for interface
mov r8, rsp # arg3 = &interface
push rsp # align stack to 16 bytes
push [rsp] # align stack to 16 bytes
and rsp, -16 # align stack to 16 bytes
push 1 # arg6 = EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
push 0 # arg5 = NULL
sub rsp, 32 # allocate shadow stack space for UEFI function
call [r14+280] # system->boot->open_protocol(handle, &guid, &interface, agent_handle, 0, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL)
mov rsp, [rsp+56] # deallocate stack
pop rax # get interface
ret
# rcx: handle
# rdx: &guid
# r8: agent_handle
close_protocol:
push rsp # align stack to 16 bytes
push [rsp] # align stack to 16 bytes
and rsp, -16 # align stack to 16 bytes
mov r9, 0 # arg4 = NULL
sub rsp, 32 # allocate shadow stack space for UEFI function
call [r14+288] # system->boot->close_protocol(handle, &guid, agent_handle, 0)
mov rsp, [rsp+40] # deallocate stack
ret
# rdx: number of bytes to allocate
# r14: system->boot
# returns pointer in rax
allocate_pool:
push rdx # allocate stack for pool pointer
mov r8, rsp # arg3 = &pool
mov rcx, 2 # arg1 = EFI_LOADER_DATA
push rsp # align stack to 16 bytes
push [rsp] # align stack to 16 bytes
and rsp, -16 # align stack to 16 bytes
sub rsp, 32 # allocate shadow stack space for UEFI function
call [r14+64] # system->boot->allocate_pool(EFI_LOADER_DATA, 2048, &pool)
mov rsp, [rsp+40] # deallocate stack
pop rax # get pool
ret
# rcx: memory pool
# r14: system->boot
free_pool:
push rsp # align stack to 16 bytes
push [rsp] # align stack to 16 bytes
and rsp, -16 # align stack to 16 bytes
sub rsp, 32 # allocate shadow stack space for UEFI function
call [r14+72] # system->boot->free_pool(pool)
mov rsp, [rsp+40] # deallocate stack
ret
# Switch to uefi stack
# does not change any other registers
enter_uefi_stack:
mov [rip+temp_rax], rax # save RAX
pop rax # Save return address
mov [rip+user_stack], rsp # save user stack
mov rsp, [rip+uefi_stack] # restore system stack
push rax # Restore return address
mov rax, [rip+temp_rax] # restore RAX
ret
# Switch to user stack
# does not change any other registers
exit_uefi_stack:
mov [rip+temp_rax], rax # save RAX
pop rax # Save return address
mov [rip+uefi_stack], rsp # save system stack
mov rsp, [rip+user_stack] # restore user stack
push rax # Restore return address
mov rax, [rip+temp_rax] # restore RAX
ret
# debug_list function
# Receives struct token_list* in RAX
# Prints contents of list and exits
# Does NOT return
debug_list:
mov r12, rax # Protect the list pointer
mov r15, 2 # write to standard error
debug_list_iter:
# Header
lea rax, [rip+debug_list_string0] # Using our first string
call File_Print # Print it
mov rax, r12 # Use address of pointer
call numerate_number # Convert it into string
call File_Print # Print it
# NEXT
lea rax, [rip+debug_list_string1] # Using our second string
call File_Print # Print it
mov rax, [r12] # Use address of pointer
call numerate_number # Convert it into string
call File_Print # Print it
# PREV
lea rax, [rip+debug_list_string2] # Using our third string
call File_Print # Print it
mov rax, [r12+8] # Use address of pointer
call numerate_number # Convert it into string
call File_Print # Print it
# S
lea rax, [rip+debug_list_string3] # Using our fourth string
call File_Print # Print it
mov rax, [r12+16] # Use address of pointer
call numerate_number # Convert it into string
call File_Print # Print it
# S Contents
lea rax, [rip+debug_list_string4] # Using our fifth string
call File_Print # Print it
mov rax, [r12+16] # Use address of string
cmp rax, 0 # IF NULL Pointer
jne debug_list_null # otherwise display
lea rax, [rip+debug_list_string_null] # Give meaningful message instead
debug_list_null:
call File_Print # Print it
# TYPE
lea rax, [rip+debug_list_string5] # Using our sixth string
call File_Print # Print it
mov rax, [r12+24] # Use address of pointer
call numerate_number # Convert it into string
call File_Print # Print it
# ARGS/DEPTH
lea rax, [rip+debug_list_string6] # Using our seventh string
call File_Print # Print it
mov rax, [r12+32] # Use address of pointer
call numerate_number # Convert it into string
call File_Print # Print it
mov rax, 10 # Add "\n"
call fputc # print it
call fputc # print it
mov r12, [r12] # TOKEN = TOKEN->NEXT
cmp r12, 0 # Check if NULL
jne debug_list_iter # iterate otherwise
mov rax, 666 # All is HELL
jmp abort # Call it a bad day
.data
debug_list_string0: .asciz "Token_list node at address: "
debug_list_string1: .asciz "\nNEXT address: "
debug_list_string2: .asciz "\nPREV address: "
debug_list_string3: .asciz "\nS address: "
debug_list_string4: .asciz "\nThe contents of S are: "
debug_list_string5: .asciz "\nTYPE address: "
debug_list_string6: .asciz "\nARGUMENTS address: "
debug_list_string_null: .asciz ">::<NULL>::<"
# Keywords
union: .asciz "union"
struct: .asciz "struct"
constant: .asciz "CONSTANT"
main_string: .asciz "main"
argc_string: .asciz "argc"
argv_string: .asciz "argv"
if_string: .asciz "if"
else_string: .asciz "else"
do_string: .asciz "do"
while_string: .asciz "while"
for_string: .asciz "for"
asm_string: .asciz "asm"
goto_string: .asciz "goto"
return_string: .asciz "return"
break_string: .asciz "break"
continue_string: .asciz "continue"
sizeof_string: .asciz "sizeof"
plus_string: .asciz "+"
minus_string: .asciz "-"
multiply_string: .asciz "*"
divide_string: .asciz "/"
modulus_string: .asciz "%"
left_shift_string: .asciz "<<"
right_shift_string: .asciz ">>"
less_than_string: .asciz "<"
less_than_equal_string: .asciz "<="
greater_than_equal_string: .asciz ">="
greater_than_string: .asciz ">"
equal_to_string: .asciz "=="
not_equal_string: .asciz "!="
bitwise_and: .asciz "&"
logical_and: .asciz "&&"
bitwise_or: .asciz "|"
logical_or: .asciz "||"
bitwise_xor: .asciz "^"
arrow_string: .asciz "->"
# Frequently Used strings
# Generally used by require_match
open_curly_brace: .asciz "{"
close_curly_brace: .asciz "}"
open_paren: .asciz "("
close_paren: .asciz ")"
open_bracket: .asciz "["
close_bracket: .asciz "]"
comma: .asciz ","
semicolon: .asciz ";"
equal: .asciz "="
percent: .asciz "%"
underline: .asciz "_"
prim_types:
type_void:
.quad type_int - type_void # NEXT
.quad 8 # SIZE
.quad 0 # OFFSET
.quad type_void - type_void # INDIRECT
.quad 0 # MEMBERS
.quad type_void - type_void # TYPE
.quad type_void_name - type_void # NAME
type_int:
.quad type_char - type_int # NEXT
.quad 8 # SIZE
.quad 0 # OFFSET
.quad type_int - type_int # INDIRECT
.quad 0 # MEMBERS
.quad type_int - type_int # TYPE
.quad type_int_name - type_int # NAME
type_char:
.quad type_file - type_char # NEXT
.quad 1 # SIZE
.quad 0 # OFFSET
.quad type_char_indirect - type_char # INDIRECT
.quad 0 # MEMBERS
.quad type_char - type_char # TYPE
.quad type_char_name - type_char # NAME
type_char_indirect:
.quad type_file - type_char_indirect # NEXT
.quad 8 # SIZE
.quad 0 # OFFSET
.quad type_char_double_indirect - type_char_indirect # INDIRECT
.quad 0 # MEMBERS
.quad type_char_indirect - type_char_indirect # TYPE
.quad type_char_indirect_name - type_char_indirect # NAME
type_char_double_indirect:
.quad type_file - type_char_double_indirect # NEXT
.quad 8 # SIZE
.quad 0 # OFFSET
.quad type_char_double_indirect - type_char_double_indirect # INDIRECT
.quad 0 # MEMBERS
.quad type_char_indirect - type_char_double_indirect # TYPE
.quad type_char_double_indirect_name - type_char_double_indirect # NAME
type_file:
.quad type_function - type_file # NEXT
.quad 8 # SIZE
.quad 0 # OFFSET
.quad type_file - type_file # INDIRECT
.quad 0 # MEMBERS
.quad type_file - type_file # TYPE
.quad type_file_name - type_file # NAME
type_function:
.quad type_unsigned - type_function # NEXT
.quad 8 # SIZE
.quad 0 # OFFSET
.quad type_function - type_function # INDIRECT
.quad 0 # MEMBERS
.quad type_function - type_function # TYPE
.quad type_function_name - type_function # NAME
type_unsigned:
.quad type_long - type_unsigned # NEXT
.quad 8 # SIZE
.quad 0 # OFFSET
.quad type_unsigned - type_unsigned # INDIRECT
.quad 0 # MEMBERS
.quad type_unsigned - type_unsigned # TYPE
.quad type_unsigned_name - type_unsigned # NAME
type_long:
.quad 0 # NEXT
.quad 8 # SIZE
.quad 0 # OFFSET
.quad type_long - type_long # INDIRECT
.quad 0 # MEMBERS
.quad type_long - type_long # TYPE
.quad type_long_name - type_long # NAME
type_void_name: .asciz "void"
type_int_name: .asciz "int"
type_char_name: .asciz "char"
type_char_indirect_name: .asciz "char*"
type_char_double_indirect_name: .asciz "char**"
type_file_name: .asciz "FILE"
type_function_name: .asciz "FUNCTION"
type_unsigned_name: .asciz "unsigned"
type_long_name: .asciz "long"
Address_of: .quad 0
C: .quad 0
Token: .quad 0
break_frame: .quad 0
break_target_func: .quad 0
break_target_head: .quad 0
break_target_num: .quad 0
current_count: .quad 0
current_target: .quad 0
function: .quad 0
global_constant_list: .quad 0
global_function_list: .quad 0
global_symbol_list: .quad 0
global_token: .quad 0
global_types: .quad 0
globals_list: .quad 0
output_list: .quad 0
string_index: .quad 0
strings_list: .quad 0
# Protocol GUIDs
LOADED_IMAGE_PROTOCOL:
.long 0x5b1b31a1
.short 0x9562
.short 0x11d2
.byte 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b
SIMPLE_FS_PROTOCOL:
.long 0x964e5b22
.short 0x6459
.short 0x11d2
.byte 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b
malloc_pool:
.quad 0
malloc_pointer:
.quad 0
uefi_stack:
.quad 0
user_stack:
.quad 0
temp_rax:
.quad 0
fin:
.quad 0
fout:
.quad 0
rootdir:
.quad 0
system:
.quad 0
image_handle:
.quad 0
root_device:
.quad 0
WCHAR:
.long 0 # WCHAR with null terminator