4914 lines
181 KiB
ArmAsm
4914 lines
181 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:
|
|
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, 16 # allocate shadow stack space for UEFI function
|
|
call [rcx+8] # rootfs->open_volume(rootfs, &rootdir)
|
|
add rsp, 16 # 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:
|
|
|
|
# Open file for reading
|
|
pop r8 # arg3 = in
|
|
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
|
|
pop r8 # 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
|
|
mov rsp, rbp # restore stack
|
|
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
|
|
sub rsp, 24 # allocate shadow stack space for UEFI function
|
|
call [rcx+32] # fin->read()
|
|
add rsp, 24 # 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
|
|
sub rsp, 16 # allocate shadow stack space for UEFI function
|
|
call [rcx+8] # system->err->output_string(system->err, WCHAR*)
|
|
add rsp, 16 # 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:
|
|
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, 24 # allocate shadow stack space for UEFI function
|
|
call [rcx+40] # fout->write()
|
|
add rsp, 40 # 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 rax # allocate shadow stack space for UEFI function
|
|
call [rcx+16] # file_handle->close(file_handle)
|
|
pop rax # 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 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)
|
|
add rsp, 48 # deallocate stack
|
|
pop rax # get interface
|
|
ret
|
|
|
|
# rcx: handle
|
|
# rdx: &guid
|
|
# r8: agent_handle
|
|
close_protocol:
|
|
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)
|
|
add rsp, 32 # 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
|
|
sub rsp, 24 # allocate shadow stack space for UEFI
|
|
call [r14+64] # system->boot->allocate_pool(EFI_LOADER_DATA, 2048, &pool)
|
|
add rsp, 24 # deallocate stack
|
|
pop rax # get pool
|
|
ret
|
|
|
|
# rcx: memory pool
|
|
# r14: system->boot
|
|
free_pool:
|
|
push rax # allocate shadow stack space for UEFI function
|
|
call [r14+72] # system->boot->free_pool(pool)
|
|
pop rax # 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
|