asm_dip/toolchain/fasm2/source/assembler.inc

3384 lines
79 KiB
PHP
Raw Normal View History

2024-11-24 21:04:53 -08:00
; general note:
; with the ability to run in various environments in mind, the entire assembler core
; does not use ebp register and never directly touches esp; the stack is managed
; with push and pop instructions, occasionally [esp] may be used to access the top value,
; but no other assumptions about the stack layout are made
struct Workspace
memory_start dd ?
memory_end dd ?
ends
struct SourceContext
number_of_entries dd ?
; entries SourceEntry[]
ends
struct SourceEntry
type db ? ; SOURCE_#
flags db ? ; SRCF_#
saved_result db ?
reserved db ?
name dd ?
name_length dd ?
text dd ?
offset dd ?
line_number dd ?
number_of_attached_lines dd ?
line_offset dd ?
local_namespace dd ?
ends
struct RecognitionContext
base_namespace dd ? ; SymbolTree_Root
base_label dd ? ; SymbolTree_Foliage
ends
struct LineEmbedding
previous_pointer dd ?
previous_end dd ?
recognition_context dd ?
definition dd ?
whitespace dd ?
ends
struct LineExcerpt
data_start dd ?
data_end dd ?
recognition_context dd ?
leftover_context dd ?
ends
SOURCE_FILE = 0
SOURCE_MEMORY = 1
SOURCE_MACRO = 2
SOURCE_CALM = 3
SRCF_PREPROCESSED = 1
SRCF_ALM_STATEMENT = 2
PMODE_RETAIN_COMMENTS = 1
PMODE_ISOLATE_LINES = 2
AMODE_SKIP = 1
AMODE_DEFINITION = 2
AMODE_POSTPONED = 4
AMODE_CALM_DEFINITION = 8
TRACE_ERROR_STACK = 1
TRACE_DISPLAY = 2
assembly_init:
; in:
; al = any combination of TRACE_# flags
mov [trace_mode],al
xor eax,eax
mov edi,variables
mov ecx,(variables_end - variables) shr 2
rep stosd
if (variables_end - variables) and 11b
mov cl,(variables_end - variables) and 11b
rep stosb
end if
mov edi,characters
prepare_characters:
stosb
inc al
jnz prepare_characters
mov esi,characters+'a'
mov edi,characters+'A'
mov ecx,'z'+1-'a'
rep movsb
mov edi,characters
mov esi,control_characters
mov cl,control_characters.count
mark_control_characters:
lodsb
mov byte [edi+eax],20h
loop mark_control_characters
mov esi,syntactical_characters
mov cl,syntactical_characters.count
mark_syntactical_characters:
lodsb
mov byte [edi+eax],0
loop mark_syntactical_characters
mov esi,include_variable
xor ecx,ecx
call get_environment_variable
mov ecx,eax
call malloc_fixed
mov [include_paths],eax
mov edi,eax
call get_environment_variable
mov cl,10
call create_string_map
mov [file_source_cache],ebx
mov cl,12
call create_string_map
mov [memory_source_cache],ebx
mov cl,10
call create_string_map
mov [file_data_cache],ebx
mov cl,7
call create_string_map
mov [auxiliary_output_areas],ebx
mov ecx,sizeof.SymbolTree_Root + sizeof.SymbolTree_Node
call create_tree_element
mov [root_namespace],eax
call create_parameter_namespace
mov [root_parameter_namespace],eax
mov ecx,4*sizeof.SymbolTree_Leaf
call create_tree_element
mov [eax+SymbolTree_Leaf.class],SYMCLASS_INSTRUCTION
mov [eax+SymbolTree_Leaf.flags],SYM_VARIABLE
mov [interceptor_symbol],eax
add eax,sizeof.SymbolTree_Leaf
mov [eax+SymbolTree_Leaf.class],SYMCLASS_STRUCTURE
mov [eax+SymbolTree_Leaf.flags],SYM_VARIABLE
mov [label_interceptor_symbol],eax
add eax,sizeof.SymbolTree_Leaf
mov [eax+SymbolTree_Leaf.class],SYMCLASS_INSTRUCTION
mov [eax+SymbolTree_Leaf.flags],SYM_VARIABLE
mov [other_interceptor_symbol],eax
add eax,sizeof.SymbolTree_Leaf
mov [void_symbol],eax
mov ecx,400
call malloc_growable
mov [tree_stack_base],eax
add eax,ecx
mov [tree_stack_end],eax
mov ecx,4000
call malloc_growable
mov [source_context],eax
mov [source_context_maximum_length],ecx
mov ecx,1000
call malloc_growable
mov [line_embeddings],eax
mov [line_embeddings_maximum_length],ecx
mov ecx,1000
call malloc_growable
mov [display_buffer],eax
mov [display_buffer_length],ecx
mov ecx,1000
call malloc_growable
mov [macro_buffer],eax
mov [macro_buffer_length],ecx
mov ecx,32*6*sizeof.ExpressionTerm
call malloc_fixed
mov [temporary_terms],eax
mov [free_temporary_terms],eax
mov ecx,4*sizeof.FloatData
call malloc_fixed
mov [temporary_floats],eax
mov ecx,400
call malloc_growable
mov [output_areas_list],eax
mov edi,eax
add eax,ecx
mov [output_areas_list_end],eax
xor eax,eax
shr ecx,2
rep stosd
mov [number_of_line_embeddings],eax
mov [virtual_area],eax
mov ecx,1000
call malloc_growable
mov [directives_stack_base],eax
add eax,ecx
mov [directives_stack_end],eax
mov ecx,1000
call malloc_growable
mov [counters_stack_base],eax
add eax,ecx
mov [counters_stack_end],eax
mov ecx,400
call malloc_growable
mov [operator_stack_base],eax
add eax,ecx
mov [operator_stack_end],eax
mov ecx,400
call malloc_growable
mov [condition_stack_base],eax
add eax,ecx
mov [condition_stack_end],eax
mov ecx,400
call malloc_growable
mov [assembly_stack_base],eax
add eax,ecx
mov [assembly_stack_end],eax
mov edx,preprocessing_workspace
call initialize_workspace
mov edx,assembly_workspace
call initialize_workspace
mov edx,identifier_workspace
call initialize_workspace
mov edx,auxiliary_workspace
call initialize_workspace
mov edx,value_workspace
call initialize_workspace
mov edx,expression_workspace
call initialize_workspace
mov edx,calculation_workspace
call initialize_workspace
mov edx,calm_code_buffer
call initialize_workspace
mov edx,calm_literals_buffer
call initialize_workspace
mov edx,calm_auxiliary_buffer
call initialize_workspace
mov ecx,256*4
call malloc_fixed
mov [operator_table],eax
mov edi,eax
mov ecx,256
xor eax,eax
rep stosd
mov esi,separating_operators
register_operators:
lodsb
test al,al
jz operators_registered
movzx ebx,al
shl ebx,2
add ebx,[operator_table]
mov ecx,sizeof.ValueDefinition
call create_tree_element
mov edx,eax
xchg [ebx],eax
mov [edx+ValueDefinition.previous],eax
inc [edx+ValueDefinition.reference_count]
lodsb
mov [edx+ValueDefinition.type],al
lodsb
mov [edx+ValueDefinition.flags],al
lodsb
mov [edx+ValueDefinition.attribute],al
lodsd
mov [edx+ValueDefinition.value],eax
jmp register_operators
operators_registered:
xor eax,eax
mov [name_volatile],al
mov [name_token],eax
mov [name_kind],NAME_CASEINSENSITIVE
or [symbol_required],1
or [symbol_expected],1
mov eax,[root_namespace]
mov [current_context.base_namespace],eax
mov esi,symbols
register_internal_symbols:
lodsb
test al,al
jnz prepare_internal_symbol
lodsb
test al,al
jz internal_symbols_registered
and [current_context.base_namespace],0
prepare_internal_symbol:
movzx ecx,al
xor ebx,ebx
mov edx,FNV_OFFSET
hash_internal_symbol:
movzx eax,byte [esi+ebx]
xor dl,[characters+eax]
imul edx,FNV_PRIME
inc ebx
cmp ebx,ecx
jb hash_internal_symbol
mov ebx,[current_context.base_namespace]
test ebx,ebx
jz register_internal_namespace
mov al,[esi+ecx]
mov [symbol_class],al
push esi
call scan_namespace
pop esi
add esi,ecx
mov ecx,sizeof.ValueDefinition
call create_tree_element
mov edx,eax
xchg [ebx+SymbolTree_Leaf.definition],eax
mov [edx+ValueDefinition.previous],eax
inc [edx+ValueDefinition.reference_count]
lodsb
lodsb
mov [edx+ValueDefinition.type],al
lodsb
mov [edx+ValueDefinition.flags],al
lodsb
mov [edx+ValueDefinition.attribute],al
lodsd
mov [edx+ValueDefinition.value],eax
jmp register_internal_symbols
register_internal_namespace:
mov [symbol_class],SYMCLASS_EXPRESSION
mov ebx,[root_namespace]
push esi
call scan_namespace
pop esi
add esi,ecx
mov ecx,sizeof.ValueDefinition
call create_tree_element
mov [ebx+SymbolTree_Leaf.definition],eax
inc [eax+ValueDefinition.reference_count]
mov [eax+ValueDefinition.type],VALTYPE_RESERVED
mov [eax+ValueDefinition.flags],VAL_INTERNAL
call get_symbol_namespace
mov [current_context.base_namespace],ebx
jmp register_internal_symbols
internal_symbols_registered:
retn
assembly_shutdown:
call discard_errors
mov ebx,[auxiliary_output_areas]
test ebx,ebx
jz auxiliary_output_areas_released
call destroy_string_map
auxiliary_output_areas_released:
mov ebx,[value_definition_chain]
release_values_from_chain:
test ebx,ebx
jz values_released
cmp [ebx+ValueDefinition.block_length],0
je release_next_value
mov eax,[ebx+ValueDefinition.value]
call mfree
release_next_value:
mov ebx,[ebx+ValueDefinition.interlink]
jmp release_values_from_chain
values_released:
mov eax,[include_paths]
call mfree
mov ebx,[file_source_cache]
test ebx,ebx
jz file_source_cache_released
mov edi,mfree
call iterate_through_map
mov ebx,[file_source_cache]
call destroy_string_map
file_source_cache_released:
mov ebx,[file_data_cache]
test ebx,ebx
jz file_data_cache_released
mov edi,release_file_data
call iterate_through_map
mov ebx,[file_data_cache]
call destroy_string_map
file_data_cache_released:
mov ebx,[memory_source_cache]
test ebx,ebx
jz memory_source_cache_released
mov edi,mfree
call iterate_through_map
mov ebx,[memory_source_cache]
call destroy_string_map
memory_source_cache_released:
mov eax,[tree_stack_base]
call mfree
mov eax,[source_context]
call mfree
mov eax,[line_embeddings]
call mfree
mov eax,[output_areas_list]
call mfree
mov eax,[directives_stack_base]
call mfree
mov eax,[counters_stack_base]
call mfree
mov eax,[operator_stack_base]
call mfree
mov eax,[condition_stack_base]
call mfree
mov eax,[assembly_stack_base]
call mfree
mov eax,[preprocessing_workspace.memory_start]
call mfree
mov eax,[assembly_workspace.memory_start]
call mfree
mov eax,[identifier_workspace.memory_start]
call mfree
mov eax,[value_workspace.memory_start]
call mfree
mov eax,[expression_workspace.memory_start]
call mfree
mov eax,[calculation_workspace.memory_start]
call mfree
mov eax,[auxiliary_workspace.memory_start]
call mfree
mov eax,[calm_code_buffer.memory_start]
call mfree
mov eax,[calm_literals_buffer.memory_start]
call mfree
mov eax,[calm_auxiliary_buffer.memory_start]
call mfree
mov eax,[display_buffer]
call mfree
mov eax,[macro_buffer]
call mfree
mov eax,[temporary_terms]
call mfree
mov eax,[temporary_floats]
call mfree
mov eax,[operator_table]
call mfree
mov eax,[tree_blocks]
call release_chained_blocks
mov eax,[storage_blocks]
call release_chained_blocks
retn
release_chained_blocks:
test eax,eax
jz blocks_released
mov ebx,[eax]
call mfree
mov eax,ebx
jmp release_chained_blocks
blocks_released:
retn
release_file_data:
test eax,eax
jz file_data_released
mov ebx,[eax+FileData.cache]
call mfree
release_file_cache:
mov eax,ebx
test eax,eax
jz file_data_released
mov ebx,[ebx+FileCache.next]
call mfree
jmp release_file_cache
file_data_released:
retn
release_auxiliary_output:
test eax,eax
jz auxiliary_output_released
dec [eax+ValueDefinition.reference_count]
xor eax,eax
mov [edx+MapEntry.value],eax
auxiliary_output_released:
retn
assembly_pass:
; in:
; esi - ASCIIZ string containing source text
; edx - path to source file
; out:
; cf clear if another pass is needed
; note:
; if both string and file sources are present, they are assembled as combined text
mov [source_file],edx
inc [current_pass]
call discard_errors
mov eax,[directives_stack_base]
mov [directives_stack],eax
mov eax,[root_namespace]
mov [current_context.base_namespace],eax
mov edx,[counters_stack_base]
mov [current_counter],edx
xor eax,eax
mov [edx],al
mov [preprocessing_mode],al
mov [next_pass_needed],al
mov [assembly_mode],al
mov [use_raw_values],al
mov [shift_tracking],al
mov [current_area],eax
mov [current_output_area_entry],eax
mov [initial_output_area_entry],eax
mov [predicted_shift],eax
mov [display_data_length],eax
mov [macro_end_position],eax
mov [proxy_number],eax
mov [output_extension],eax
mov [output_extension_length],eax
mov ebx,[source_context]
mov [ebx+SourceContext.number_of_entries],eax
add ebx,sizeof.SourceContext
mov edi,ebx
mov ecx,sizeof.SourceEntry shr 2
assert sizeof.SourceEntry and 11b = 0
rep stosd
mov [line_start],eax
mov [line_end],eax
test esi,esi
jz read_main_source_file
cmp byte [esi],0
je read_main_source_file
push ebx
call use_source
pop ebx
mov [ebx+SourceEntry.type],SOURCE_MEMORY
jmp fill_main_source_entry
read_main_source_file:
mov esi,[source_file]
test esi,esi
jz no_source_to_assemble
cmp byte [esi],0
je no_source_to_assemble
push ebx
call read_source
pop ebx
test eax,eax
jz main_source_file_not_found
mov [ebx+SourceEntry.type],SOURCE_FILE
fill_main_source_entry:
mov [ebx+SourceEntry.name],esi
mov [ebx+SourceEntry.text],eax
mov eax,[root_parameter_namespace]
and [eax+SymbolTree_Root.parameters],0
mov [local_parameter_namespace],eax
mov [ebx+SourceEntry.local_namespace],eax
mov ebx,[source_context]
inc [ebx+SourceContext.number_of_entries]
mov esi,zero_value
mov ecx,4+4
call create_output_area
mov [current_area],edx
inc [edx+ValueDefinition.reference_count]
mov ebx,[auxiliary_output_areas]
mov edi,release_auxiliary_output
call iterate_through_map
assembly_line:
xor eax,eax
mov [value_position],eax
mov [current_constituent],al
call clear_line_embeddings
get_line:
xor eax,eax
mov [symbol_class],SYMCLASS_PARAMETER
mov [symbol_expected],al
mov [symbol_required],al
mov [name_volatile],al
mov [preprocessed_context],eax
mov [alm_statement],al
mov ebx,[source_context]
mov ecx,[ebx+SourceContext.number_of_entries]
dec ecx
imul ecx,sizeof.SourceEntry
lea ebx,[ebx+sizeof.SourceContext+ecx]
xchg eax,[ebx+SourceEntry.number_of_attached_lines]
inc eax
add [ebx+SourceEntry.line_number],eax
cmp [ebx+SourceEntry.type],SOURCE_CALM
je calm_virtual_machine
mov eax,[ebx+SourceEntry.local_namespace]
mov [parameter_namespace],eax
xor edx,edx
test [eax+SymbolTree_Root.flags],NAMESPACE_UNATTACHED
jnz no_local_namespace
mov edx,eax
mov ecx,[eax+SymbolTree_Root.current_label]
test ecx,ecx
jnz local_namespace_ok
no_local_namespace:
mov eax,[current_context.base_namespace]
mov ecx,[eax+SymbolTree_Root.current_label]
local_namespace_ok:
mov [local_namespace],edx
mov [current_context.base_label],ecx
cmp [ebx+SourceEntry.type],SOURCE_MACRO
je get_line_from_macro
and [name_token],0
mov esi,[ebx+SourceEntry.text]
mov eax,[ebx+SourceEntry.offset]
add esi,eax
mov [ebx+SourceEntry.line_offset],eax
cmp byte [esi],0
je source_ended
mov edi,[preprocessing_workspace.memory_start]
preprocess_line_from_file:
mov ecx,14
mov edx,preprocessing_workspace
call reserve_workspace
lodsb
cmp al,1Ah
je convert_name_symbol
cmp al,22h
je convert_quoted_string
cmp al,27h
je convert_quoted_string
test al,al
jz file_ended
cmp al,0Ah
je line_ended
cmp al,';'
jne preprocess_syntactical_character
xor edx,edx
test [preprocessing_mode],PMODE_RETAIN_COMMENTS
jz skip_comment
preprocess_syntactical_character:
stosb
cmp al,'`'
je convert_parameter
cmp al,'\'
jne preprocess_line_from_file
test [preprocessing_mode],PMODE_ISOLATE_LINES
jnz preprocess_line_from_file
mov edx,esi
detect_line_concatenation:
mov al,[edx]
inc edx
cmp al,0Ah
je concatenate_line
cmp al,';'
je concatenation_comment
cmp al,20h
jne preprocess_line_from_file
jmp detect_line_concatenation
concatenate_line:
mov byte [edi-1],20h
inc esi
inc [ebx+SourceEntry.number_of_attached_lines]
jmp preprocess_line_from_file
concatenation_comment:
test [preprocessing_mode],PMODE_RETAIN_COMMENTS
jnz preprocess_line_from_file
mov byte [edi-1],20h
mov esi,edx
inc [ebx+SourceEntry.number_of_attached_lines]
skip_comment:
lodsb
test al,al
jz file_ended
cmp al,0Ah
je comment_ended
cmp al,22h
je skip_quoted_string
cmp al,27h
je skip_quoted_string
cmp al,1Ah
jne skip_comment
lodsd
lea esi,[esi+eax+12]
jmp skip_comment
comment_ended:
test edx,edx
jnz preprocess_line_from_file
jmp line_ended
skip_quoted_string:
lodsd
add esi,eax
jmp skip_comment
convert_quoted_string:
stosb
mov eax,esi
stosd
lodsd
add esi,eax
jmp preprocess_line_from_file
convert_name_symbol:
mov ecx,[esi]
lea eax,[esi+4+ecx+12]
push eax ebx
mov eax,[eax-4]
test eax,eax
jz name_symbol_not_cached
mov esi,eax
name_symbol_not_cached:
call preprocess_symbol
name_symbol_converted:
pop ebx esi
jmp preprocess_line_from_file
convert_parameter:
cmp byte [esi],1Ah
jne preprocess_line_from_file
lodsb
mov ecx,[esi]
lea eax,[esi+4+ecx+12]
push eax ebx
mov eax,[eax-4]
test eax,eax
jz parameter_to_convert_not_cached
mov esi,eax
parameter_to_convert_not_cached:
call preprocess_symbol
jc name_symbol_converted
call convert_parameter_to_string
jmp name_symbol_converted
file_ended:
dec esi
line_ended:
sub esi,[ebx+SourceEntry.text]
mov [ebx+SourceEntry.offset],esi
jmp line_preprocessed
source_ended:
mov ebx,[source_context]
dec [ebx+SourceContext.number_of_entries]
jnz get_line
cmp [ebx+sizeof.SourceContext+SourceEntry.type],SOURCE_MEMORY
jne no_more_lines
mov esi,[source_file]
test esi,esi
jz no_more_lines
cmp byte [esi],0
je no_more_lines
lea edi,[ebx+sizeof.SourceContext]
mov ecx,sizeof.SourceEntry shr 2
assert sizeof.SourceEntry and 11b = 0
xor eax,eax
rep stosd
call read_source
test eax,eax
jz main_source_file_not_found
mov ebx,[source_context]
inc [ebx+SourceContext.number_of_entries]
add ebx,sizeof.SourceContext
mov [ebx+SourceEntry.type],SOURCE_FILE
mov [ebx+SourceEntry.name],esi
mov [ebx+SourceEntry.text],eax
mov eax,[parameter_namespace]
mov [ebx+SourceEntry.local_namespace],eax
jmp get_line
no_more_lines:
jmp pass_done
get_line_from_macro:
mov edx,[ebx+SourceEntry.text]
mov esi,[edx+ValueDefinition.value]
mov ecx,[edx+ValueDefinition.value_length]
mov eax,[ebx+SourceEntry.offset]
test [ebx+SourceEntry.flags],SRCF_PREPROCESSED
jnz use_preprocessed_line
add ecx,esi
mov [source_end],ecx
add esi,eax
cmp esi,[source_end]
je macro_ended
mov [ebx+SourceEntry.line_offset],eax
mov edi,[preprocessing_workspace.memory_start]
preprocess_line_from_macro:
cmp esi,[source_end]
je macro_line_ended
mov ecx,14
mov edx,preprocessing_workspace
call reserve_workspace
lodsb
cmp al,1Ah
je reproduce_name_symbol
test al,al
jz macro_line_ended
stosb
cmp al,22h
je reproduce_quoted_string
cmp al,27h
je reproduce_quoted_string
cmp al,30h
je reproduce_internal_token
cmp al,40h
je reproduce_context_token
cmp al,'`'
je conversion_operator_in_macro
jmp preprocess_line_from_macro
reproduce_quoted_string:
movsd
jmp preprocess_line_from_macro
reproduce_internal_token:
mov ecx,[esi]
add ecx,4
mov edx,preprocessing_workspace
call reserve_workspace
lodsd
stosd
mov ecx,eax
rep movsb
jmp preprocess_line_from_macro
reproduce_context_token:
mov [preprocessed_context],esi
assert sizeof.RecognitionContext and 11b = 0
mov ecx,sizeof.RecognitionContext shr 2
rep movsd
cmp esi,[source_end]
je macro_line_ended
cmp byte [esi],40h
jne preprocess_line_from_macro
inc esi
sub edi,sizeof.RecognitionContext
jmp reproduce_context_token
reproduce_name_symbol:
mov [name_token],esi
lodsd
push esi ebx
mov esi,eax
call preprocess_symbol
name_symbol_reproduced:
pop ebx esi
jmp preprocess_line_from_macro
conversion_operator_in_macro:
cmp esi,[source_end]
je macro_ended
cmp byte [esi],1Ah
jne preprocess_line_from_macro
inc esi
mov [name_token],esi
lodsd
push esi ebx
mov esi,eax
call preprocess_symbol
jc name_symbol_reproduced
call convert_parameter_to_string
jmp name_symbol_reproduced
convert_parameter_to_string:
call convert_symbolic_value_to_string
mov ebx,[memory_source_cache]
xor eax,eax
call put_into_map
mov edi,[symbol_value_start]
dec edi
mov al,22h
stosb
mov eax,esi
stosd
retn
macro_ended:
mov edx,[ebx+SourceEntry.text]
and [edx+ValueDefinition.flags],not VAL_IN_USE
dec [edx+ValueDefinition.reference_count]
mov ebx,[source_context]
dec [ebx+SourceContext.number_of_entries]
jnz get_line
jmp pass_done
use_preprocessed_line:
test eax,eax
jnz macro_ended
dec eax
mov [ebx+SourceEntry.offset],eax
add ecx,esi
mov [line_start],esi
mov [line_end],ecx
jmp got_line
macro_line_ended:
mov edx,[ebx+SourceEntry.text]
sub esi,[edx+ValueDefinition.value]
mov [ebx+SourceEntry.offset],esi
line_preprocessed:
mov [line_end],edi
mov esi,[preprocessing_workspace.memory_start]
mov [line_start],esi
got_line:
and [line_context],0
assemble_instruction:
mov ebx,[interceptor_symbol]
call get_available_value
jc no_interceptor
test [edx+ValueDefinition.flags],VAL_UNCONDITIONAL
jz weak_interceptor
jmp execute_instruction
no_interceptor:
xor edx,edx
weak_interceptor:
mov [interceptor],edx
xor eax,eax
mov [label_interceptor],eax
test [assembly_mode],AMODE_CALM_DEFINITION
jnz assemble_alm_instruction
mov [symbol_definition],al
mov [instruction_branch],eax
mov dl,SYMCLASS_INSTRUCTION
call identify_symbol
jc empty_line
mov [label_branch],edx
mov al,[symbol_independent]
mov [label_independent],al
test ebx,ebx
jz unrecognized_instruction
cmp [ebx+SymbolTree_Leaf.class],SYMCLASS_INSTRUCTION
jne labeled_instruction
cmp [symbol_independent],-1
je unrecognized_instruction
call get_available_value
jc unrecognized_instruction
test [edx+ValueDefinition.flags],VAL_UNCONDITIONAL
jnz execute_instruction
cmp [interceptor],0
jne unrecognized_instruction
test [assembly_mode],AMODE_SKIP or AMODE_DEFINITION
jnz unrecognized_instruction
execute_instruction:
mov al,[edx+ValueDefinition.type]
cmp al,VALTYPE_CALM
je launch_calm
cmp al,VALTYPE_NATIVE_COMMAND
je execute_native_instruction
cmp al,VALTYPE_SYMBOLIC
je use_macro
cmp al,VALTYPE_RESERVED
jne unrecognized_instruction
mov edx,_symbolic_self_reference
call register_error
unrecognized_instruction:
test [assembly_mode],AMODE_SKIP
jnz assembly_line
test [assembly_mode],AMODE_DEFINITION
jnz add_line_to_macro
cmp [interceptor],0
jne execute_interceptor
cmp [label_interceptor],0
jne execute_label_interceptor
mov ebx,[other_interceptor_symbol]
call get_available_value
jnc execute_other_interceptor
mov edx,_illegal_instruction
call register_error
mov ebx,[label_branch]
test ebx,ebx
jz assembly_line
mov [symbol_class],SYMCLASS_INSTRUCTION
or [symbol_required],1
or [symbol_expected],1
call scan_symbol_branch
xor edx,edx
call mark_symbol_as_used
mov ebx,[instruction_branch]
test ebx,ebx
jz assembly_line
mov [symbol_class],SYMCLASS_STRUCTURE
or [symbol_required],1
or [symbol_expected],1
call scan_symbol_branch
xor edx,edx
call mark_symbol_as_used
jmp assembly_line
labeled_instruction:
mov [label_leaf],ebx
mov [label_instruction_start],esi
mov ebx,[label_interceptor_symbol]
call get_available_value
jc no_label_interceptor
test [edx+ValueDefinition.flags],VAL_UNCONDITIONAL
jnz execute_labeled_instruction
mov [label_interceptor],edx
mov eax,[embedded_context]
mov [label_instruction_context],eax
jmp identify_structure_instruction
no_label_interceptor:
call move_to_next_symbol
jc unrecognized_instruction ; orphan label
cmp al,':'
je define_label
cmp al,'='
je define_numeric_symbol
identify_structure_instruction:
mov dl,SYMCLASS_STRUCTURE
call identify_symbol
mov [instruction_branch],edx
test ebx,ebx
jz unrecognized_instruction
cmp [ebx+SymbolTree_Leaf.class],SYMCLASS_STRUCTURE
jne unrecognized_instruction
call get_available_value
jc unrecognized_instruction
test [edx+ValueDefinition.flags],VAL_UNCONDITIONAL
jnz execute_labeled_instruction
test [assembly_mode],AMODE_SKIP or AMODE_DEFINITION
jnz unrecognized_instruction
cmp [interceptor],0
jne execute_interceptor
cmp [label_interceptor],0
jne execute_label_interceptor
execute_labeled_instruction:
mov al,[edx+ValueDefinition.type]
cmp al,VALTYPE_CALM
je launch_calm
cmp al,VALTYPE_SYMBOLIC
je use_struc
cmp al,VALTYPE_NATIVE_COMMAND
jne unrecognized_instruction
execute_native_instruction:
mov eax,[current_pass]
mov [ebx+SymbolTree_Leaf.last_use_pass],eax
jmp [edx+ValueDefinition.value]
empty_line:
mov al,[assembly_mode]
and al,AMODE_SKIP or AMODE_DEFINITION
cmp al,AMODE_DEFINITION
je add_line_to_macro
jmp assembly_line
execute_interceptor:
mov ebx,[interceptor_symbol]
mov edx,[interceptor]
xor eax,eax
mov [embedded_context],eax
mov esi,[line_start]
jmp execute_instruction
execute_label_interceptor:
mov ebx,[label_interceptor_symbol]
mov edx,[label_interceptor]
mov eax,[label_instruction_context]
mov [embedded_context],eax
mov esi,[label_instruction_start]
cmp [edx+ValueDefinition.type],VALTYPE_CALM
je launch_calm
jmp use_struc
execute_other_interceptor:
mov ebx,[other_interceptor_symbol]
xor eax,eax
mov [embedded_context],eax
mov esi,[line_start]
jmp execute_instruction
instruction_assembled:
cmp [current_constituent],0
jne extra_characters_on_line
call warp_to_next_symbol
jc assembly_line
extra_characters_on_line:
mov edx,_extra_characters_on_line
call register_error
jmp assembly_line
pass_done:
mov dl,DBLOCK_CONTROL
call find_directive_block
jc assemble_postponed_block
mov esi,edi
sub esi,[edi+DirectiveBlock.length_of_data]
mov edx,_missing_end_directive
call register_delayed_error
call close_control_directive_block
jmp pass_done
assemble_postponed_block:
mov dl,DBLOCK_POSTPONED
call find_directive_block
jc no_postponed_blocks
mov ebx,edi
mov esi,edi
sub esi,[edi+DirectiveBlock.length_of_data]
mov edi,[source_context]
call clone_source_context
mov edi,ebx
call close_directive_block
or [assembly_mode],AMODE_POSTPONED
jmp assembly_line
no_postponed_blocks:
mov ebx,[root_namespace]
call detect_mispredictions
assemble_suspended_block:
mov dl,DBLOCK_SUSPENDED
call find_directive_block
jc no_suspended_blocks
cmp [next_pass_needed],0
jne ignore_suspended_block
mov ebx,edi
mov esi,edi
sub esi,[edi+DirectiveBlock.length_of_data]
mov edi,[source_context]
call clone_source_context
mov edi,ebx
call close_directive_block
or [assembly_mode],AMODE_POSTPONED
jmp assembly_line
ignore_suspended_block:
call close_directive_block
jmp assemble_suspended_block
no_suspended_blocks:
mov esi,[directives_stack]
signal_unclosed_blocks:
cmp esi,[directives_stack_base]
je unclosed_blocks_signalled
sub esi,sizeof.DirectiveBlock
mov eax,[esi+DirectiveBlock.length_of_data]
sub esi,eax
mov edx,_missing_end_directive
call register_delayed_error
jmp signal_unclosed_blocks
unclosed_blocks_signalled:
xor eax,eax
xchg eax,[current_area]
dec [eax+ValueDefinition.reference_count]
mov al,[next_pass_needed]
sub al,1
retn
main_source_file_not_found:
mov ebx,esi
mov edx,_source_file_not_found
call register_error
no_source_to_assemble:
mov esi,zero_value
mov ecx,4+4
call create_output_area
mov [current_area],edx
inc [edx+ValueDefinition.reference_count]
stc
retn
initialize_workspace:
; in:
; edx - Workspace
; preserves: ebx, edx, esi, edi
push edx
mov ecx,1000h
call malloc_growable
pop edx
mov [edx+Workspace.memory_start],eax
add eax,ecx
mov [edx+Workspace.memory_end],eax
retn
reserve_workspace:
; in:
; edx - Workspace
; edi - top of used workspace area
; ecx = size of required reserve
; out:
; cf set if workspace had to be expanded
; edi - top of used workspace area (possibly relocated when cf is set)
; preserves: ebx, edx, esi
mov eax,[edx+Workspace.memory_end]
sub eax,ecx
jc not_enough_workspace
cmp edi,eax
ja not_enough_workspace
clc
retn
not_enough_workspace:
add ecx,edi
jc allocation_overflow
sub ecx,[edx+Workspace.memory_start]
push ecx
bsr eax,ecx
xchg ecx,eax
dec cl
shr eax,cl
inc eax
shl eax,cl
mov ecx,eax
pop eax
cmp ecx,eax
jbe allocation_overflow
cmp edi,[edx+Workspace.memory_start]
je reestablish_workspace
expand_workspace:
mov eax,[edx+Workspace.memory_start]
sub edi,eax
push edx
call realloc
pop edx
update_workspace:
mov [edx+Workspace.memory_start],eax
add edi,eax
add eax,ecx
mov [edx+Workspace.memory_end],eax
stc
retn
reestablish_workspace:
push edx ecx
xor eax,eax
xchg eax,[edx+Workspace.memory_start]
call mfree
pop ecx
call malloc_growable
pop edx
xor edi,edi
jmp update_workspace
allocation_overflow:
jmp out_of_memory
grow_stack:
; in:
; eax = base address of memory block containing stack data
; ecx = required minimum size of memory block
; out:
; eax = new base address of memory block containing stack data
; ecx = new size of memory block
; preserves: ebx, esi, edi
push ecx
bsr edx,ecx
xchg ecx,edx
sub cl,2
shr edx,cl
inc edx
shl edx,cl
mov ecx,edx
pop edx
cmp ecx,edx
jbe allocation_overflow
call realloc
retn
create_source_entry:
; out:
; cf set when the maximum number of entries in context has been exceeded
; when cf = 0:
; ebx - new SourceEntry in the main SourceContext
; preserves: edx, esi
mov ebx,[source_context]
mov eax,[ebx+SourceContext.number_of_entries]
cmp eax,[maximum_depth_of_stack]
jae source_context_full
inc [ebx+SourceContext.number_of_entries]
imul eax,sizeof.SourceEntry
add eax,sizeof.SourceContext
mov edi,eax
add eax,sizeof.SourceEntry
cmp eax,[source_context_maximum_length]
jbe source_context_length_ok
mov ecx,eax
mov eax,ebx
mov ebx,edx
call realloc
mov [source_context],eax
mov [source_context_maximum_length],ecx
mov edx,ebx
mov ebx,eax
source_context_length_ok:
add ebx,edi
mov edi,ebx
mov ecx,sizeof.SourceEntry shr 2
assert sizeof.SourceEntry and 11b = 0
xor eax,eax
rep stosd
cmp [alm_statement],0
je source_entry_created
mov [edi-sizeof.SourceEntry+SourceEntry.flags],SRCF_ALM_STATEMENT
source_entry_created:
clc
retn
source_context_full:
stc
retn
create_parameter_namespace:
; out:
; eax - SymbolTree_Root
; preserves: ebx, edx, esi, edi
mov ecx,sizeof.SymbolTree_Root + sizeof.SymbolTree_LocalNode
call create_tree_element
or [eax+SymbolTree_Root.attributes],SYMTREE_LOCAL
or [eax+SymbolTree_Root.flags],NAMESPACE_UNATTACHED
retn
clone_source_context:
; in:
; esi - SourceContext
; edi - buffer
; out:
; edi = pointer advanced past the stored data
; preserves: ebx
cmp edi,[source_context]
sete [source_context_affected]
mov eax,[esi+SourceContext.number_of_entries]
assert sizeof.SourceContext and 11b = 0
mov ecx,sizeof.SourceContext shr 2
rep movsd
test eax,eax
jnz clone_source_entry
retn
clone_source_entry:
assert SOURCE_FILE < SOURCE_MACRO & SOURCE_MEMORY < SOURCE_MACRO & SOURCE_CALM > SOURCE_MACRO
cmp [esi+SourceEntry.type],SOURCE_MACRO
jb copy_source_entry
mov edx,[esi+SourceEntry.text]
inc [edx+ValueDefinition.reference_count]
cmp [source_context_affected],0
je copy_source_entry
or [edx+ValueDefinition.flags],VAL_IN_USE
copy_source_entry:
assert sizeof.SourceEntry and 11b = 0
mov ecx,sizeof.SourceEntry shr 2
rep movsd
dec eax
jnz clone_source_entry
retn
release_source_context:
; in:
; eax - SourceContext
; preserves: eax, ebx, esi, edi
cmp eax,[source_context]
sete [source_context_affected]
push eax
mov ecx,[eax+SourceContext.number_of_entries]
add eax,sizeof.SourceContext
test ecx,ecx
jnz release_source_entry
pop eax
retn
release_source_entry:
assert SOURCE_FILE < SOURCE_MACRO & SOURCE_MEMORY < SOURCE_MACRO & SOURCE_CALM > SOURCE_MACRO
cmp [eax+SourceEntry.type],SOURCE_MACRO
jb source_entry_released
mov edx,[eax+SourceEntry.text]
dec [edx+ValueDefinition.reference_count]
cmp [source_context_affected],0
je source_entry_released
and [edx+ValueDefinition.flags],not VAL_IN_USE
source_entry_released:
add eax,sizeof.SourceEntry
loop release_source_entry
pop eax
retn
get_file_source_entry:
; out:
; ebx - SourceEntry in the main SourceContext
; preserves: edx, esi, edi
mov ebx,[source_context]
mov ecx,[ebx+SourceContext.number_of_entries]
mov eax,ecx
imul eax,sizeof.SourceEntry
lea ebx,[ebx+sizeof.SourceContext+eax]
find_file_source_entry:
sub ebx,sizeof.SourceEntry
cmp [ebx+SourceEntry.type],SOURCE_FILE
loopne find_file_source_entry
retn
preprocess_symbol:
; in:
; esi - contents of the name token (32-bit length and name followed by two hashes)
; edi - pointer into preprocessing_workspace where the preprocessed text should be stored
; out:
; edi - just after the preprocessed text
; cf set when symbol was used as-is (was not recognized as a parameter)
; when cf = 0:
; [symbol_value_start] - start of the preprocessed text
; [symbol_value_end] - end of the preprocessed text (the same as edi)
mov eax,edi
sub eax,[preprocessing_workspace.memory_start]
mov [symbol_value_start],eax
mov [symbol_data],esi
lodsd
mov ecx,eax
mov eax,[esi+ecx+4]
mov [case_insensitive_hash],eax
mov edx,[esi+ecx]
mov [name_kind],NAME_CASESENSITIVE
mov ebx,[parameter_namespace]
call scan_namespace
jnc parameter_found
mov [name_kind],NAME_CASEINSENSITIVE
mov ebx,[parameter_namespace]
test [ebx+SymbolTree_Root.attributes],SYMTREE_WITH_CASEINSENSITIVE_PARAMETERS
jz no_local_parameter_recognized
mov edx,[case_insensitive_hash]
call scan_namespace
jnc parameter_found
no_local_parameter_recognized:
cmp byte [esi],'%'
jne no_parameter_recognized
cmp ecx,2
ja no_parameter_recognized
jb current_counter_value
cmp byte [esi+1],'%'
je current_limit_value
no_parameter_recognized:
mov edi,[preprocessing_workspace.memory_start]
add edi,[symbol_value_start]
mov al,1Ah
stosb
mov eax,[symbol_data]
stosd
stc
retn
parameter_found:
mov edi,[preprocessing_workspace.memory_start]
add edi,[symbol_value_start]
mov edx,[ebx+SymbolTree_Leaf.definition]
mov al,[edx+ValueDefinition.type]
cmp al,VALTYPE_SYMBOLIC
je simple_parameter_value
cmp al,VALTYPE_SYMBOLIC_SEQUENCE
je iterator_value
cmp al,VALTYPE_NUMERIC_SEQUENCE
je named_counter_value
cmp al,VALTYPE_NATIVE_COMMAND
jne no_parameter_recognized
jmp [edx+ValueDefinition.value]
current_counter_value:
mov ebx,[parameter_namespace]
test [ebx+SymbolTree_Root.parameters],SPECPARM_COUNTER
jz no_parameter_recognized
mov esi,[current_counter]
mov dl,DBLOCK_CONTROL
call find_directive_block
find_breakable_block:
jc no_parameter_recognized
test [edi+DirectiveBlock.flags],CTRLF_BREAKABLE
jz look_deeper_for_breakable_block
mov eax,[parameter_namespace]
cmp eax,[edi+DirectiveBlock.parameter_namespace]
je found_breakable_block
look_deeper_for_breakable_block:
mov esi,[edi+DirectiveBlock.prior_counter_position]
add esi,[counters_stack_base]
call find_next_directive_block
jmp find_breakable_block
found_breakable_block:
cmp byte [esi],0
je no_parameter_recognized
mov edi,[preprocessing_workspace.memory_start]
add edi,[symbol_value_start]
movzx ecx,byte [esi]
add ecx,6
mov edx,preprocessing_workspace
call reserve_workspace
mov al,30h
stosb
mov edx,edi
xor eax,eax
lodsb
stosd
mov ecx,eax
rep movsb
test byte [edi-1],80h
jz parameter_replaced
xor al,al
stosb
inc dword [edx]
jmp parameter_replaced
current_limit_value:
mov ebx,[parameter_namespace]
test [ebx+SymbolTree_Root.parameters],SPECPARM_LIMIT
jz no_parameter_recognized
mov dl,DBLOCK_CONTROL
call find_directive_block
find_block_with_limit:
jc no_parameter_recognized
test [edi+DirectiveBlock.flags],CTRLF_BREAKABLE
jz look_deeper_for_block_with_limit
test [edi+DirectiveBlock.flags],CTRLF_HAS_REPEAT_DATA
jz look_deeper_for_block_with_limit
mov eax,[parameter_namespace]
cmp eax,[edi+DirectiveBlock.parameter_namespace]
je found_block_with_limit
look_deeper_for_block_with_limit:
call find_next_directive_block
jmp find_block_with_limit
found_block_with_limit:
mov ebx,edi
sub ebx,sizeof.RepeatData
mov edi,[preprocessing_workspace.memory_start]
add edi,[symbol_value_start]
mov ecx,[ebx+RepeatData.limit_length]
add ecx,6
mov edx,preprocessing_workspace
call reserve_workspace
mov al,30h
stosb
mov edx,edi
mov eax,[ebx+RepeatData.limit_length]
stosd
sub ebx,eax
mov esi,ebx
mov ecx,eax
rep movsb
test byte [edi-1],80h
jz parameter_replaced
xor al,al
stosb
inc dword [edx]
jmp parameter_replaced
named_counter_value:
mov edx,[edx+ValueDefinition.value]
mov ebx,[edx]
add ebx,[counters_stack_base]
cmp byte [ebx],0
je no_parameter_recognized
add edx,4
movzx ecx,byte [ebx]
cmp ecx,[edx]
jae estimate_counter_length
mov ecx,[edx]
estimate_counter_length:
add ecx,6
mov esi,edx
mov edx,preprocessing_workspace
call reserve_workspace
mov al,30h
stosb
xor eax,eax
stosd
push edi
movzx ecx,byte [ebx]
inc ebx
lodsd
sub eax,ecx
jnc counter_base_selected
add ecx,eax
neg eax
xchg ebx,esi
counter_base_selected:
mov edx,eax
jecxz counter_added_to_base
xor ah,ah
add_counter_to_base:
lodsb
add al,ah
setc ah
add al,[ebx]
adc ah,0
inc ebx
add al,-1
adc ah,0
stosb
loop add_counter_to_base
counter_added_to_base:
mov ecx,edx
jecxz counter_carried
carry_counter:
lodsb
add al,ah
setc ah
add al,-1
adc ah,0
stosb
loop carry_counter
counter_carried:
pop edx
mov al,ah
dec al
jnz extend_counter_value
cmp edx,edi
je counter_value_finished
test byte [edi-1],80h
jz counter_value_finished
extend_counter_value:
stosb
counter_value_finished:
mov ecx,edi
sub ecx,edx
mov [edx-4],ecx
jmp parameter_replaced
iterator_value:
mov edx,[edx+ValueDefinition.value]
mov ebx,[edx]
add ebx,[counters_stack_base]
movzx ecx,byte [ebx]
test ecx,ecx
jz no_parameter_recognized
push edi
mov edi,value_index
xor eax,eax
mov [edi],eax
mov esi,ebx
inc esi
rep movsb
mov eax,[value_index]
pop edi
shl eax,2
mov esi,[edx+eax]
mov ecx,[edx+eax+4]
sub ecx,esi
add esi,edx
jmp copy_parameter_value
simple_parameter_value:
mov esi,[edx+ValueDefinition.value]
mov ecx,[edx+ValueDefinition.value_length]
copy_parameter_value:
push ecx
mov edx,preprocessing_workspace
call reserve_workspace
pop ecx
cmp [preprocessed_context],0
jne copy_with_preprocessed_context
rep movsb
jmp parameter_replaced
copy_token_pointer:
movsd
sub ecx,4
copy_with_preprocessed_context:
test ecx,ecx
jz parameter_replaced
lodsb
stosb
dec ecx
cmp al,1Ah
je copy_token_pointer
cmp al,22h
je copy_token_pointer
cmp al,27h
je copy_token_pointer
cmp al,30h
je copy_token_data
cmp al,40h
jne copy_with_preprocessed_context
mov eax,ecx
cmp dword [esi],0
jne copy_parameter_context
mov edx,esi
mov esi,[preprocessed_context]
assert sizeof.RecognitionContext and 11b = 0
mov ecx,sizeof.RecognitionContext shr 2
rep movsd
lea esi,[edx+sizeof.RecognitionContext]
mov ecx,eax
sub ecx,sizeof.RecognitionContext
jmp copy_with_preprocessed_context
copy_parameter_context:
assert sizeof.RecognitionContext and 11b = 0
mov ecx,sizeof.RecognitionContext shr 2
rep movsd
mov ecx,eax
sub ecx,sizeof.RecognitionContext
jmp copy_with_preprocessed_context
copy_token_data:
lodsd
stosd
sub ecx,4
sub ecx,eax
xchg ecx,eax
rep movsb
xchg ecx,eax
jmp copy_with_preprocessed_context
local_symbol_name:
mov ecx,1+sizeof.RecognitionContext+1+4+1+sizeof.RecognitionContext
mov edx,preprocessing_workspace
call reserve_workspace
mov al,40h
stosb
mov eax,[local_namespace]
mov [edi+RecognitionContext.base_namespace],eax
xor eax,eax
mov [edi+RecognitionContext.base_label],eax
add edi,sizeof.RecognitionContext
mov al,1Ah
stosb
mov eax,[symbol_data]
stosd
mov al,40h
stosb
mov esi,[preprocessed_context]
assert sizeof.RecognitionContext and 11b = 0
mov ecx,sizeof.RecognitionContext shr 2
test esi,esi
jz reset_context
rep movsd
stc
retn
reset_context:
xor eax,eax
rep stosd
stc
retn
parameter_replaced:
mov [symbol_value_end],edi
mov eax,[preprocessing_workspace.memory_start]
add [symbol_value_start],eax
clc
retn
convert_symbolic_value_to_string:
; in:
; [symbol_value_start] - start of the preprocessed text to convert
; [symbol_value_end] - end of the preprocessed text to convert
; out:
; esi - 32-bit length followed by string data
; ecx = total length of string data (including the length prefix)
mov esi,[symbol_value_start]
mov edx,assembly_workspace
mov edi,[edx+Workspace.memory_start]
add edi,4
convert_token_to_text:
mov ecx,[symbol_value_end]
sub ecx,esi
jbe finish_conversion
mov edx,assembly_workspace
call reserve_workspace
lodsb
cmp al,1Ah
je convert_name_token_to_text
cmp al,22h
je convert_string_token_to_text
cmp al,27h
je convert_string_token_to_text
cmp al,30h
je convert_internal_number_to_text
cmp al,40h
je ignore_context_token
stosb
jmp convert_token_to_text
ignore_context_token:
add esi,sizeof.RecognitionContext
jmp convert_token_to_text
convert_name_token_to_text:
lodsd
mov ebx,esi
mov esi,eax
mov ecx,[esi]
mov edx,assembly_workspace
call reserve_workspace
lodsd
mov ecx,eax
rep movsb
mov esi,ebx
jmp convert_token_to_text
convert_string_token_to_text:
lodsd
mov ebx,esi
mov esi,eax
call enclose_string
mov esi,ebx
cmp byte [esi-4-1],27h
jne convert_token_to_text
dec edi
jmp convert_token_to_text
enclose_string:
mov ecx,[esi]
inc ecx
shl ecx,1
mov edx,assembly_workspace
call reserve_workspace
lodsd
mov ecx,eax
mov al,27h
stosb
copy_string_characters:
jecxz string_characters_copied
lodsb
stosb
dec ecx
cmp al,27h
jne copy_string_characters
stosb
jmp copy_string_characters
string_characters_copied:
mov al,27h
stosb
retn
convert_internal_number_to_text:
mov edx,esi
lodsd
add esi,eax
push esi edi
call convert_number_back
pop edi
mov esi,edx
mov ecx,[esi]
mov edx,assembly_workspace
call reserve_workspace
lodsd
mov ecx,eax
rep movsb
pop esi
jmp convert_token_to_text
finish_conversion:
mov esi,[assembly_workspace.memory_start]
mov ecx,edi
sub ecx,esi
lea eax,[ecx-4]
mov [esi],eax
retn
compare_symbolic_values:
; in:
; esi - first symbolic value
; edi - second symbolic value
; ecx = length to compare
; out:
; ecx = zero when values equal, or a number of bytes following the point of difference
; esi - the point of difference in first value
; edi - the point of difference in second value
mov al,[esi]
cmp al,[edi]
jne symbolic_values_compared
inc esi
inc edi
cmp al,1Ah
je compare_tokens_with_data
cmp al,22h
je compare_tokens_with_data
cmp al,27h
je compare_tokens_with_data
cmp al,30h
je compare_internal_tokens
cmp al,40h
je compare_context_tokens
loop compare_symbolic_values
symbolic_values_compared:
retn
compare_tokens_with_data:
dec ecx
mov eax,[esi]
mov edx,[edi]
cmp eax,edx
jne compare_token_data
add esi,4
add edi,4
sub ecx,4
jnz compare_symbolic_values
retn
compare_token_data:
mov ebx,[eax]
cmp ebx,[edx]
jne symbolic_values_compared
add eax,ebx
add edx,ebx
xchg esi,eax
xchg edi,edx
xchg ecx,ebx
dec ecx
shr ecx,2
inc ecx
cmp byte [eax-1],1Ah
jne compare_token_dwords
inc ecx
add esi,4
add edi,4
compare_token_dwords:
std
repe cmpsd
cld
jne token_data_content_differs
lea esi,[eax+4]
lea edi,[edx+4]
mov ecx,ebx
sub ecx,4
jnz compare_symbolic_values
retn
token_data_content_differs:
mov esi,eax
mov edi,edx
mov ecx,ebx
retn
compare_internal_tokens:
mov eax,[esi]
cmp eax,[edi]
jne symbolic_values_compared
add esi,4
add edi,4
sub ecx,1+4
sub ecx,eax
xchg ecx,eax
repe cmpsb
je internal_tokens_equal
inc ecx
dec esi
dec edi
add ecx,eax
retn
internal_tokens_equal:
add ecx,eax
jnz compare_symbolic_values
retn
compare_context_tokens:
dec ecx
assert sizeof.RecognitionContext and 11b = 0
mov edx,sizeof.RecognitionContext shr 2
compare_contexts:
mov eax,[esi]
cmp eax,[edi]
jne symbolic_values_compared
add esi,4
add edi,4
sub ecx,4
jz symbolic_values_compared
dec edx
jnz compare_contexts
jmp compare_symbolic_values
move_to_next_symbol:
; in:
; esi = pointer into preprocessed line or current embedded value
; zeroed ecx is recommended for whitespace detection
; out:
; esi = pointer advanced past the whitespace and context tokens
; cf set when reached end of line or embedded value
; ecx increased when there was whitespace on the way, preserved otherwise
; when cf = 0:
; esi - next symbol
; al = initial byte of the next symbol
; preserves: ebx, edx, edi
; note:
; [embedded_context] is updated to point to a RecognitionContext that should be in force at the new position (null means current namespace context);
cmp esi,[line_end]
jb next_token_available
stc
retn
next_token_available:
mov al,[esi]
cmp al,40h
je set_embedded_context
cmp al,20h
je pass_whitespace
clc
retn
pass_whitespace:
inc esi
inc ecx
jmp move_to_next_symbol
set_embedded_context:
inc esi
cmp [esi+RecognitionContext.base_namespace],0
je restore_embedded_context
mov [embedded_context],esi
add esi,sizeof.RecognitionContext
jmp move_to_next_symbol
restore_embedded_context:
add esi,sizeof.RecognitionContext
mov eax,[number_of_line_embeddings]
test eax,eax
jz clear_embedded_context
dec eax
imul eax,sizeof.LineEmbedding
add eax,[line_embeddings]
mov eax,[eax+LineEmbedding.recognition_context]
mov [embedded_context],eax
jmp move_to_next_symbol
clear_embedded_context:
and [embedded_context],0
jmp move_to_next_symbol
warp_to_next_symbol:
; in:
; esi = pointer into preprocessed line or current embedded value
; zeroed ecx is recommended for whitespace detection
; out:
; esi - next symbol
; cf set when end of line reached
; ecx increased when there was whitespace on the way, preserved otherwise
; when cf = 0:
; al = initial byte of the next symbol
; preserves: ebx, edx, edi
; note:
; [embedded_context] is updated to point to a RecognitionContext that should be in force at the new position (null means current namespace context);
call move_to_next_symbol
jc warp_through_embedding_boundary
retn
warp_through_embedding_boundary:
mov eax,[number_of_line_embeddings]
sub eax,1
jc reached_end_of_line
mov [number_of_line_embeddings],eax
imul eax,sizeof.LineEmbedding
add eax,[line_embeddings]
mov esi,eax
push edx
add ecx,[esi+LineEmbedding.whitespace]
mov edx,[esi+LineEmbedding.recognition_context]
mov [embedded_context],edx
mov edx,[esi+LineEmbedding.definition]
dec [edx+ValueDefinition.reference_count]
and [edx+ValueDefinition.flags],not VAL_IN_USE
mov edx,[esi+LineEmbedding.previous_end]
mov [line_end],edx
mov esi,[esi+LineEmbedding.previous_pointer]
pop edx
jmp warp_to_next_symbol
reached_end_of_line:
; stc
retn
cut_piece_of_line:
; in:
; esi = pointer into preprocessed line or current embedded value
; edi - LineExcerpt to be filled with information about cut piece of line
; dl = initial byte of symbol that should end the cut value, zero to cut up to the end of line (or current embedded value)
; dh = initial byte of symbol that would open the nested piece, zero for no nesting
; [breakpoint_token] = initial byte of symbol that should unconditionally end the cut value if it is met
; out:
; esi = pointer advanced past the cut piece
; al = initial byte of symbol at pointer, zero when no more symbols there
; preserves: ebx, edx, edi
; note: when [breakpoint_token] has the same value as either dl or dh, it has no effect
mov [number_of_enclosings],1
call move_to_next_symbol
mov [edi+LineExcerpt.data_start],esi
mov [edi+LineExcerpt.data_end],esi
jc last_piece_in_line
mov ecx,[embedded_context]
mov [edi+LineExcerpt.recognition_context],ecx
cut_piece:
cmp al,dh
je nested_piece
cmp al,dl
je close_nested_piece
cmp al,[breakpoint_token]
jne cut_token
retn
nested_piece:
inc [number_of_enclosings]
jmp cut_token
close_nested_piece:
dec [number_of_enclosings]
jz end_of_piece
cut_token:
cmp al,1Ah
je cut_token_with_data
cmp al,22h
je cut_token_with_data
cmp al,27h
je cut_token_with_data
cmp al,30h
je cut_internal_token
inc esi
cut_next_token:
mov [edi+LineExcerpt.data_end],esi
call move_to_next_symbol
jnc cut_piece
last_piece_in_line:
xor al,al
end_of_piece:
mov ecx,[embedded_context]
mov [edi+LineExcerpt.leftover_context],ecx
retn
cut_token_with_data:
add esi,1+4
jmp cut_next_token
cut_internal_token:
inc esi
lodsd
add esi,eax
jmp cut_next_token
extract_piece_of_line:
; in:
; esi = pointer into preprocessed line (not an embedded value)
; edi - buffer for token sequence, must be large enough to hold all the remaining tokens in line and an additional context token
; dl = initial byte of symbol that should end the cut value, zero to cut up to the end of line
; dh = initial byte of symbol that would open the nested piece, zero for no nesting
; [breakpoint_token] = initial byte of symbol that should unconditionally end the cut value if it is met
; [contextless_processing] = zero to have current context added to the extracted value
; out:
; esi = pointer advanced past the processed piece
; edi - end of the extracted sequence of tokens in provided buffer
; al = initial byte of symbol at pointer, zero when no more symbols there
; [context_boundary] = equal to edi if the extracted sequence ends with a context token
; preserves: ebx, edx
; note:
; when [breakpoint_token] has the same value as either dl or dh, it has no effect
and [context_boundary],0
call move_to_next_symbol
jc no_piece_to_extract
mov [number_of_enclosings],1
mov [whitespace_boundary],edi
mov al,40h
stosb
mov eax,[embedded_context]
test eax,eax
jz extract_current_context
xchg esi,eax
assert sizeof.RecognitionContext and 11b = 0
mov ecx,sizeof.RecognitionContext shr 2
rep movsd
mov esi,eax
mov [context_boundary],edi
jmp extract_piece
extract_contextless:
dec edi
jmp extract_piece
extract_current_context:
cmp [contextless_processing],0
jne extract_contextless
call store_current_context
extract_piece:
lodsb
cmp al,dh
je nested_piece_to_extract
cmp al,dl
je close_extracted_nested_piece
cmp al,[breakpoint_token]
je extraction_breakpoint
extract_token:
cmp al,40h
je extract_context_token
cmp al,20h
je extract_whitespace
and [whitespace_boundary],0
stosb
cmp al,1Ah
je extract_token_with_data
cmp al,22h
je extract_token_with_data
cmp al,27h
je extract_token_with_data
cmp al,30h
je extract_internal_token
extract_next_token:
cmp esi,[line_end]
jne extract_piece
xor al,al
cmp [whitespace_boundary],0
jne drop_final_whitespace
retn
nested_piece_to_extract:
inc [number_of_enclosings]
jmp extract_token
close_extracted_nested_piece:
dec [number_of_enclosings]
jnz extract_token
extraction_breakpoint:
dec esi
cmp [whitespace_boundary],0
jne drop_final_whitespace
retn
drop_final_whitespace:
mov edi,[whitespace_boundary]
retn
no_piece_to_extract:
xor al,al
retn
extract_whitespace:
cmp [whitespace_boundary],0
jne whitespace_boundary_ok
mov [whitespace_boundary],edi
whitespace_boundary_ok:
stosb
jmp extract_next_token
extract_token_with_data:
movsd
jmp extract_next_token
extract_internal_token:
lodsd
stosd
mov ecx,eax
rep movsb
jmp extract_next_token
extract_context_token:
cmp [whitespace_boundary],0
jne context_whitespace_boundary_ok
mov [whitespace_boundary],edi
context_whitespace_boundary_ok:
mov [embedded_context],esi
cmp dword [esi],0
jne embedded_context_for_extraction_ok
and [embedded_context],0
embedded_context_for_extraction_ok:
call make_recognition_context_token
jmp extract_next_token
make_recognition_context_token:
; in:
; al = 40h
; esi - RecognitionContext (contents of the context token)
; edi - buffer for the context token
; [context_boundary] = equal to edi if it is at the end of another context token
; [contextless_processing] = zero to convert context reset into a context switch capturing the current context
; out:
; esi - past the processed RecognitionContext
; edi - past the extracted context token
; [context_boundary] - past the extracted context token
; preserves: ebx, edx
cmp edi,[context_boundary]
je reuse_recognition_context
stosb
jmp store_recognition_context
reuse_recognition_context:
sub edi,sizeof.RecognitionContext
store_recognition_context:
; in:
; esi - RecognitionContext to read (may have base namespace zeroed to indicate context reset)
; edi - RecognitionContext to fill
; out:
; esi - past the processed RecognitionContext
; edi - past the filled RecognitionContext
; [context_boundary] - past the filled RecognitionContext
; [contextless_processing] = zero to convert context reset into a context switch capturing the current context
; preserves: ebx, edx
cmp [contextless_processing],0
jne copy_recognition_context
cmp [esi+RecognitionContext.base_namespace],0
je capture_context_reset
copy_recognition_context:
assert sizeof.RecognitionContext and 11b = 0
mov ecx,sizeof.RecognitionContext shr 2
rep movsd
mov [context_boundary],edi
retn
capture_context_reset:
add esi,sizeof.RecognitionContext
store_current_context:
; in:
; edi - RecognitionContext to fill
; out:
; edi - past the filled RecognitionContext
; [context_boundary] - past the filled RecognitionContext
; preserves: ebx, edx, esi
mov eax,[current_context.base_namespace]
mov ecx,[current_context.base_label]
mov [edi+RecognitionContext.base_namespace],eax
mov [edi+RecognitionContext.base_label],ecx
add edi,sizeof.RecognitionContext
mov [context_boundary],edi
retn
embed_symbolic_value:
; in:
; ebx - SymbolTree_Leaf
; edx - ValueDefinition
; esi = pointer into preprocessed line or current embedded value
; ecx = number of whitespace tokens to imply after emdedding
; out:
; esi = pointer into embedded value
; preserves: ebx, edx
mov eax,[number_of_line_embeddings]
inc eax
mov [number_of_line_embeddings],eax
imul eax,sizeof.LineEmbedding
cmp eax,[line_embeddings_maximum_length]
jbe line_embedding_allocated
push eax ecx edx
mov ecx,sizeof.LineEmbedding
add ecx,[line_embeddings_maximum_length]
mov eax,[line_embeddings]
call grow_stack
mov [line_embeddings_maximum_length],ecx
mov [line_embeddings],eax
pop edx ecx eax
line_embedding_allocated:
sub eax,sizeof.LineEmbedding
add eax,[line_embeddings]
mov [eax+LineEmbedding.whitespace],ecx
mov edi,[line_end]
mov [eax+LineEmbedding.previous_pointer],esi
mov [eax+LineEmbedding.previous_end],edi
mov ecx,[embedded_context]
mov [eax+LineEmbedding.recognition_context],ecx
and [embedded_context],0
mov esi,[edx+ValueDefinition.value]
mov ecx,[edx+ValueDefinition.value_length]
add ecx,esi
mov [line_end],ecx
mov [eax+LineEmbedding.definition],edx
inc [edx+ValueDefinition.reference_count]
or [edx+ValueDefinition.flags],VAL_IN_USE
retn
clear_line_embeddings:
; preserves: ebx, ecx, edx, esi, edi
; note:
; when esi is said to point into preprocessed line or current embedded value, it must be between [line_start] and [line_end];
; these two pointers change upon entering a symbolic value of evaluated identifier (with embed_symbolic_value)
; and are restored when warp_to_next_symbol goes past the end of that embedded value;
; this function needs to be called before setting up a new line for the assembly
; and it discards the stack of stored [line_start] and [line_end] boundaries
xor eax,eax
mov [line_start],eax
mov [line_end],eax
mov [embedded_context],eax
cmp eax,[number_of_line_embeddings]
je embedded_values_ok
push ecx
discard_line_embedding:
mov ecx,eax
imul ecx,sizeof.LineEmbedding
add ecx,[line_embeddings]
mov ecx,[ecx+LineEmbedding.definition]
dec [ecx+ValueDefinition.reference_count]
and [ecx+ValueDefinition.flags],not VAL_IN_USE
inc eax
cmp eax,[number_of_line_embeddings]
jne discard_line_embedding
and [number_of_line_embeddings],0
pop ecx
embedded_values_ok:
retn
identify_symbol_in_namespace:
; in:
; ebx - SymbolTree_Root
; esi = pointer into preprocessed line or current embedded value
; dl = SYMCLASS_#
; [symbol_definition] = non-zero when symbol needs to be identified for the purpose of definition
; out:
; cf set when there were no more symbols in preprocessed line or current embedded value
; esi = pointer advanced past the processed identifier and following whitespace or to the first token of a different symbol
; ecx = number of whitespace tokens encountered immediately before the new position
; when cf = 0:
; ebx - SymbolTree_Leaf, null when no symbol was identified
; edx - SymbolTree_Foliage, may be null if either ebx or edi is null
; edi - SymbolTree_Root, null when there was no identifier at all or when identified symbol is not in a namespace
; [symbol_start] - first token of identified symbol
; [symbol_independent] = zero when identifier is relative to current label, all bits set for special identifiers
; note:
; when ebx is null but edi is not, the identifier was malformed
; when edi is null but ebx is not, a special symbol was identified; this is possible only for SYMCLASS_INSTRUCTION and SYMCLASS_STRUCTURE
mov [expected_class],dl
xor ecx,ecx
call move_to_next_symbol
jnc namespaces_ok
retn
identify_symbol:
; in:
; esi = pointer into preprocessed line or current embedded value
; dl = SYMCLASS_#
; [symbol_definition] = non-zero when symbol needs to be identified for the purpose of definition
; out:
; cf set when there were no more symbols in preprocessed line or current embedded value
; esi = pointer advanced past the processed identifier and following whitespace or to the first token of a different symbol
; ecx = number of whitespace tokens encountered immediately before the new position
; [recognition_context] = applied recognition context
; when cf = 0:
; ebx - SymbolTree_Leaf, null when no symbol was identified
; edx - SymbolTree_Foliage, may be null if either ebx or edi is null
; edi - SymbolTree_Root, null when there was no identifier at all or when identified symbol is not in a namespace
; [symbol_start] - first token of identified symbol (within current embedded value or preprocessed line)
; [symbol_independent] = zero when identifier is relative to current label, all bits set for special identifiers
; note:
; when ebx is null but edi is not, the identifier was malformed
; when edi is null but ebx is not, a special symbol was identified; this is possible only for SYMCLASS_INSTRUCTION and SYMCLASS_STRUCTURE
mov [expected_class],dl
xor ecx,ecx
call move_to_next_symbol
jc identification_finished
xor ebx,ebx
mov edx,[embedded_context]
test edx,edx
jnz use_namespace
mov edx,current_context
use_namespace:
mov eax,[edx+RecognitionContext.base_namespace]
mov [recognition_context.base_namespace],eax
mov eax,[edx+RecognitionContext.base_label]
mov [recognition_context.base_label],eax
namespaces_ok:
mov [symbol_start],esi
and [symbol_independent],0
cmp [symbol_definition],0
setnz al
shl al,bsf RECOGNIZE_DEFINITION
mov [recognizer_setting],al
xor edx,edx
xor edi,edi
mov al,[esi]
cmp al,1Ah
je starting_with_name
cmp al,'.'
je starting_dot
cmp al,'#'
je starting_with_concatenation
cmp al,'?'
je starting_question_mark
return_no_identifier:
xor ebx,ebx
return_no_namespace:
xor edx,edx
xor edi,edi
clc
retn
starting_with_name:
call detect_numeric_symbol
jc return_no_identifier
valid_starting_name:
or [symbol_independent],1
valid_name:
inc esi
mov [name_token],esi
lodsd
mov edi,eax
and [name_volatile],0
xor ecx,ecx
identify_name:
mov dh,[recognizer_setting]
call move_to_next_symbol
jc run_recognizer
cmp al,'#'
jne name_complete
call check_concatenation
jnc name_concatenation
xor al,al
name_complete:
test ecx,ecx
jnz run_recognizer
cmp al,'?'
jne run_recognizer
or dh,RECOGNIZE_CASE_INSENSITIVE
inc esi
call move_to_next_symbol
jc run_recognizer
cmp al,'#'
jne name_modifiers_complete
call check_concatenation
jc run_recognizer
name_modifiers_complete:
test ecx,ecx
jnz run_recognizer
cmp al,1Ah
je malformed_identifier
run_recognizer:
push esi ecx
cmp esi,[line_end]
je recognize_final_symbol
test ecx,ecx
jnz recognize_final_symbol
cmp byte [esi],'.'
jne recognize_final_symbol
mov esi,edi
lodsd
mov ecx,eax
mov dl,SYMCLASS_EXPRESSION
and dh,not RECOGNIZE_DEFINITION
call recognize_symbol
pop ecx esi
dot_operator:
inc esi
xor ecx,ecx
call move_to_next_symbol
jc ending_with_dot
cmp al,'#'
je name_concatenation_after_dot
test ecx,ecx
jnz ending_with_dot
cmp al,'.'
je multiple_dot_operator
identify_name_after_dot:
cmp al,30h
je number_after_dot
cmp al,1Ah
jne ending_with_dot
call get_symbol_namespace
xor edi,edi
jmp valid_name
name_concatenation_after_dot:
call check_concatenation
jc ending_with_dot
cmp al,'.'
je multiple_dot_operator
number_after_dot:
call get_symbol_namespace
mov al,[esi]
xor edi,edi
jmp name_concatenation
recognize_final_symbol:
mov dl,[expected_class]
mov esi,edi
lodsd
mov ecx,eax
call recognize_symbol
pop ecx esi
symbol_identified:
clc
identification_finished:
retn
ending_with_dot:
mov al,[expected_class]
cmp al,SYMCLASS_EXPRESSION
jne find_expected_class
clc
retn
starting_question_mark:
inc esi
xor ecx,ecx
call move_to_next_symbol
jc alone_question_mark
cmp al,'#'
jne symbol_after_question_mark
call check_concatenation
jc alone_question_mark
symbol_after_question_mark:
test ecx,ecx
jnz alone_question_mark
cmp al,'?'
je repeated_question_mark
cmp [symbol_definition],0
jne no_forced_definition
mov [expected_class],SYMCLASS_EXPRESSION
or [recognizer_setting],RECOGNIZE_DEFINITION
no_forced_definition:
cmp al,'.'
je starting_dot
cmp al,1Ah
je valid_starting_name
cmp al,30h
je concatenation_with_internal_number
alone_question_mark:
mov dl,[expected_class]
cmp dl,SYMCLASS_INSTRUCTION
je return_interceptor_symbol
cmp dl,SYMCLASS_STRUCTURE
je return_label_interceptor_symbol
mov eax,[symbol_start]
cmp byte [eax],'?'
jne malformed_identifier
mov esi,eax
jmp return_no_identifier
return_label_interceptor_symbol:
mov ebx,[label_interceptor_symbol]
jmp return_special_symbol
return_interceptor_symbol:
mov ebx,[interceptor_symbol]
return_special_symbol:
or [symbol_independent],-1
jmp return_no_namespace
repeated_question_mark:
inc esi
xor ecx,ecx
call move_to_next_symbol
jc return_other_interceptor_symbol
cmp al,'#'
jne return_other_interceptor_symbol
call check_concatenation
jnc symbol_after_question_mark
return_other_interceptor_symbol:
cmp [expected_class],SYMCLASS_INSTRUCTION
jne malformed_identifier
mov ebx,[other_interceptor_symbol]
jmp return_special_symbol
multiple_dot_operator:
mov ebx,edi
xor edi,edi
jmp starting_dot
additional_dot:
inc edi
starting_dot:
inc esi
xor ecx,ecx
call move_to_next_symbol
jc alone_dot
cmp al,'#'
jne symbol_after_starting_dot
call check_concatenation
jc alone_dot
symbol_after_starting_dot:
test ecx,ecx
jnz alone_dot
cmp al,'.'
je additional_dot
cmp al,30h
je name_after_starting_dot
cmp al,1Ah
jne alone_dot
name_after_starting_dot:
test ebx,ebx
jnz name_after_multiple_dot_operator
call get_base_label
mov edi,ebx
mov al,[esi]
jmp identify_name_after_dot
name_after_multiple_dot_operator:
test edx,edx
jz parent_namespace_ready
call get_symbol_namespace
parent_namespace_ready:
call synthesize_dot_label
mov edi,ebx
mov al,[esi]
jmp identify_name_after_dot
alone_dot:
test ebx,ebx
jz identify_current_label
test edx,edx
jz malformed_identifier
push ecx
call get_symbol_namespace
pop ecx
call synthesize_dot_label
jmp get_label_symbol
malformed_identifier:
xor ebx,ebx
xor edx,edx
mov edi,[recognition_context.base_namespace]
jmp symbol_identified
identify_current_label:
call get_base_label
mov eax,[local_namespace]
test eax,eax
jz get_label_symbol
cmp edx,[eax+SymbolTree_Root.current_label]
jne get_label_symbol
test [eax+SymbolTree_Root.flags],NAMESPACE_LABEL_FORWARDING
setnz [symbol_independent]
get_label_symbol:
mov edi,ebx
mov al,[expected_class]
find_expected_class:
mov [symbol_class],al
mov al,[symbol_definition]
mov [symbol_required],al
mov [symbol_expected],al
mov ebx,edx
call scan_symbol_branch
jnc current_label_identified
mov [symbol_class],SYMCLASS_EXPRESSION
or [symbol_required],1
or [symbol_expected],1
mov ebx,edx
call scan_symbol_branch
current_label_identified:
clc
retn
get_base_label:
mov edx,[recognition_context.base_label]
test edi,edi
jnz synthesize_dot_label
test edx,edx
jnz current_label_ready
synthesize_dot_label:
push ecx esi
mov esi,[identifier_workspace.memory_start]
mov eax,edi
mov [esi],eax
mov edx,FNV_OFFSET
xor ecx,ecx
hash_dot_label:
test eax,eax
jz dot_label_synthesised
xor dl,al
imul edx,FNV_PRIME
inc ecx
shr eax,8
jmp hash_dot_label
dot_label_synthesised:
or [name_volatile],1
and [name_token],0
mov [name_kind],NAME_NUMERIC
mov [symbol_class],SYMCLASS_EXPRESSION
or [symbol_required],1
or [symbol_expected],1
test ebx,ebx
jnz identify_dot_label
mov ebx,[recognition_context.base_namespace]
identify_dot_label:
call scan_namespace
pop esi ecx
current_label_ready:
mov ebx,[edx+SymbolTree_Foliage.root]
retn
starting_with_concatenation:
xor ecx,ecx
call check_concatenation
jc empty_concatenation
name_concatenation:
cmp al,30h
je concatenation_with_internal_number
cmp al,1Ah
jne empty_concatenation
test edi,edi
jz starting_with_name
inc esi
lodsd
push ebx esi
mov ebx,eax
attach_to_name:
mov esi,edi
mov edi,[identifier_workspace.memory_start]
cmp esi,edi
jne initial_concatenation
lodsd
add esi,eax
mov edi,esi
jmp get_precalculated_hashes
initial_concatenation:
test esi,esi
jz initial_conversion
mov ecx,[esi]
add ecx,4+8
mov edx,identifier_workspace
call reserve_workspace
lodsd
stosd
mov ecx,eax
rep movsb
get_precalculated_hashes:
xchg ebx,esi
mov ecx,[esi]
mov eax,[identifier_workspace.memory_start]
add [eax],ecx
add ecx,8
mov edx,identifier_workspace
call reserve_workspace
mov ecx,[ebx]
mov edx,[ebx+4]
lodsd
lea ebx,[esi+eax]
xor eax,eax
concatenation_hash:
lodsb
xor cl,al
xor dl,[characters+eax]
imul ecx,FNV_PRIME
imul edx,FNV_PRIME
stosb
cmp esi,ebx
jne concatenation_hash
mov eax,ecx
stosd
mov eax,edx
stosd
pop esi ebx
mov edi,[identifier_workspace.memory_start]
or [name_volatile],1
and [name_token],0
xor ecx,ecx
jmp identify_name
initial_conversion:
xor eax,eax
stosd
mov esi,edi
mov eax,FNV_OFFSET
mov [esi],eax
mov [esi+4],eax
jmp get_precalculated_hashes
concatenation_with_internal_number:
inc esi
mov edx,esi
lodsd
add esi,eax
push ebx esi
push edi
call convert_number_back
pop edi
mov ebx,edx
jmp attach_to_name
empty_concatenation:
test edi,edi
jnz identify_name_with_empty_concatenation
call move_to_next_symbol
jc malformed_identifier
cmp al,'.'
je starting_dot
cmp al,'?'
je starting_question_mark
jmp malformed_identifier
identify_name_with_empty_concatenation:
mov dh,[recognizer_setting]
call move_to_next_symbol
jc run_recognizer
jmp name_complete
detect_numeric_symbol:
; in:
; esi - name token in preprocessed line or in current embedded value
; out:
; cf set when symbol starting with this token should be considered numeric
; preserves: ebx, ecx, edx, esi, edi
mov eax,[esi+1]
mov al,[eax+4]
cmp al,'$'
je detect_pascal_hexadecimal
sub al,'0'
cmp al,10
jb numeric_symbol_detected
not_a_numeric_symbol:
clc
retn
detect_pascal_hexadecimal:
mov eax,[esi+1]
cmp dword [eax],2
jb not_a_numeric_symbol
movzx eax,byte [eax+4+1]
mov al,[characters+eax]
sub al,'0'
cmp al,10
jb numeric_symbol_detected
sub al,'a'-'0'
jc not_a_numeric_symbol
cmp al,16-10
jae not_a_numeric_symbol
numeric_symbol_detected:
stc
retn
check_concatenation:
; in:
; esi - concatenation character in preprocessed line or in current embedded value
; ecx = number of whitespace tokens encountered immediately before current position
; out:
; cf set when there is no concatenation applicable to previous symbol
; esi = pointer advanced past the processed operator and the whitespace that follows it
; ecx = number of whitespace tokens encountered immediately before the new position
; when cf = 0:
; esi - symbol that follows concatenation operator
; al = initial byte of symbol following concatenation operator
; preserves: ebx, edx, edi
test ecx,ecx
jnz no_concatenation
inc esi
call move_to_next_symbol
jc no_concatenation
cmp al,'#'
je check_concatenation
test ecx,ecx
jnz no_concatenation
; clc
retn
no_concatenation:
stc
retn
get_literal:
; in:
; esi - token in preprocessed line or in current embedded value
; out:
; al = type of value
; esi = pointer advanced past the processed literal and the whitespace that follows it
; ecx = number of whitespace tokens encountered immediately before the new position
; when al = 22h:
; edx - 32-bit length followed by string data
; when al = 30h:
; edx - 32-bit length followed by numeric data, null when invalid number
; when al = 2Eh:
; edx - FloatData, null when invalid number
; when al is any other value, it is a simple special character, and edx is zero
xor ecx,ecx
mov al,[esi]
cmp al,1Ah
je get_literal_number
cmp al,22h
je get_literal_string
cmp al,27h
je missing_end_quote
cmp al,30h
je get_internal_number
inc esi
mov dl,al
call move_to_next_symbol
mov al,dl
xor edx,edx
retn
get_literal_number:
mov edx,[esi+1]
add esi,5
check_for_more_parts_of_number:
call move_to_next_symbol
jc number_ready_for_conversion
test ecx,ecx
jnz check_for_number_concatenation
cmp al,'.'
je get_literal_float
check_for_number_concatenation:
cmp al,'#'
jne number_ready_for_conversion
call check_concatenation
jc number_ready_for_conversion
number_concatenation:
cmp al,30h
je attach_internal_number_to_number
cmp al,'.'
je get_literal_float
cmp al,1Ah
jne number_ready_for_conversion
attach_name_to_number:
mov ebx,[esi+1]
add esi,5
push esi
attach_to_number:
mov esi,edx
mov edi,[identifier_workspace.memory_start]
cmp esi,edi
jne initial_concatenation_of_number
lodsd
add esi,eax
mov edi,esi
jmp attach_segment_of_number
initial_concatenation_of_number:
mov ecx,[esi]
add ecx,4
mov edx,identifier_workspace
call reserve_workspace
lodsd
stosd
mov ecx,eax
rep movsb
attach_segment_of_number:
mov esi,ebx
mov ecx,[esi]
mov eax,[identifier_workspace.memory_start]
add [eax],ecx
mov edx,identifier_workspace
call reserve_workspace
lodsd
mov ecx,eax
rep movsb
pop esi
mov edx,[identifier_workspace.memory_start]
jmp check_for_more_parts_of_number
attach_internal_number_to_number:
mov ebx,edx
inc esi
mov edx,esi
lodsd
add esi,eax
push esi
push ebx
call convert_number_back
mov ebx,edx
pop edx
jmp attach_to_number
number_ready_for_conversion:
push ecx
call convert_number
pop ecx
jc get_literal_float
mov al,30h
retn
missing_end_quote:
mov edx,_missing_end_quote
call register_error
get_literal_string:
mov edx,[esi+1]
add esi,5
call move_to_next_symbol
mov al,22h
retn
get_internal_number:
lea edx,[esi+1]
mov eax,[edx]
lea esi,[esi+1+4+eax]
call move_to_next_symbol
jc internal_number_ready
test ecx,ecx
jnz check_for_internal_number_concatenation
cmp al,'.'
je convert_internal_number_back
check_for_internal_number_concatenation:
cmp al,'#'
jne internal_number_ready
call check_concatenation
jc internal_number_ready
cmp al,1Ah
je convert_internal_number_back
cmp al,30h
jne internal_number_ready
convert_internal_number_back:
push esi
call convert_number_back
mov esi,edx
mov ecx,[esi]
add ecx,4
mov edi,[identifier_workspace.memory_start]
mov edx,identifier_workspace
call reserve_workspace
lodsd
stosd
mov ecx,eax
rep movsb
pop esi
mov edx,[identifier_workspace.memory_start]
mov al,[esi]
jmp number_concatenation
internal_number_ready:
mov al,30h
retn
get_literal_float:
xor eax,eax
mov [zero_digits],eax
mov [decimal_places],eax
mov [literal_exponent],eax
mov [literal_exponent_sign],al
mov [literal_fractional_part],al
mov [waiting_for_digit],al
mov [float_literal_status],al
push esi ecx
mov ebx,edx
call start_decimal_converter
lea esi,[ebx+4]
mov ecx,[ebx]
get_float_digit:
lodsb
cmp al,27h
je skip_float_digit
cmp al,'_'
je skip_float_digit
cmp al,'9'
ja float_nondigit
sub al,'0'
jz float_digit_zero
jc float_nondigit
test [float_literal_status],1
jnz reject_float_digit
inc [decimal_places]
push esi ecx
xor ecx,ecx
xchg ecx,[zero_digits]
call convert_decimal_digit
pop ecx esi
and [waiting_for_digit],0
skip_float_digit:
loop get_float_digit
jmp float_digits_ok
float_digit_zero:
test [float_literal_status],1
jnz reject_float_digit
inc [decimal_places]
inc [zero_digits]
and [waiting_for_digit],0
loop get_float_digit
float_digits_ok:
pop ecx esi
call move_to_next_symbol
jc no_more_tokens_in_float
cmp al,'.'
je decimal_point
cmp al,'#'
jne no_more_tokens_in_float
call check_concatenation
jc no_more_tokens_in_float
jmp next_token_in_float
decimal_point:
test ecx,ecx
jnz no_more_tokens_in_float
mov al,[literal_fractional_part]
or al,[literal_exponent_sign]
jz decimal_point_allowed
or [float_literal_status],-1
decimal_point_allowed:
or [literal_fractional_part],1
or [waiting_for_digit],1
and [decimal_places],0
get_following_digits:
inc esi
xor ecx,ecx
call move_to_adjacent_symbol
jc invalid_float_value
next_token_in_float:
cmp al,1Ah
je next_digits_section
cmp al,30h
je internal_number_as_digits
no_more_tokens_in_float:
cmp [waiting_for_digit],0
jne invalid_float_value
cmp [float_literal_status],-1
je invalid_float_value
call finish_decimal_conversion
push esi ecx
mov esi,edi
xor ecx,ecx
cmp [literal_fractional_part],0
je float_decimal_places_ok
mov ecx,[decimal_places]
float_decimal_places_ok:
sub ecx,[zero_digits]
neg ecx
add ecx,[literal_exponent]
jo float_conversion_failed
call multiply_float_by_power_of_ten
test [edi+FloatData.attributes],FLOAT_INFINITE or FLOAT_INDETERMINATE or FLOAT_UNDERFLOW
jnz float_conversion_failed
mov edx,edi
pop ecx esi
mov al,2Eh
retn
float_conversion_failed:
pop ecx esi
invalid_float_value:
xor edx,edx
mov al,2Eh
retn
next_digits_section:
inc esi
lodsd
xor ecx,ecx
push esi ecx
mov esi,eax
lodsd
mov ecx,eax
cmp [literal_exponent_sign],0
jne get_exponent_digit
jmp get_float_digit
internal_number_as_digits:
inc esi
mov edx,esi
lodsd
add esi,eax
xor ecx,ecx
push esi ecx
call convert_number_back
mov esi,edx
lodsd
mov ecx,eax
cmp [literal_exponent_sign],0
jne get_exponent_digit
jmp get_float_digit
get_literal_exponent:
mov al,1
xchg al,[literal_exponent_sign]
test al,al
jnz reject_exponent_digit
or [waiting_for_digit],1
loop get_exponent_digit
pop ecx esi
call move_to_adjacent_symbol
jc invalid_float_value
cmp al,1Ah
je next_digits_section
cmp al,30h
je internal_number_as_digits
cmp al,'+'
je get_following_digits
cmp al,'-'
jne invalid_float_value
neg [literal_exponent_sign]
jmp get_following_digits
get_exponent_digit:
xor eax,eax
lodsb
cmp al,27h
je skip_exponent_digit
cmp al,'_'
je skip_exponent_digit
sub al,'0'
jc reject_exponent_digit
cmp al,10
jae reject_exponent_digit
test [float_literal_status],1
jnz reject_exponent_digit
mov edx,[literal_exponent]
imul edx,10
jo reject_exponent_digit
cmp [literal_exponent_sign],0
jnl exponent_digit_ok
neg eax
exponent_digit_ok:
add edx,eax
jo reject_exponent_digit
mov [literal_exponent],edx
and [waiting_for_digit],0
skip_exponent_digit:
loop get_exponent_digit
jmp float_digits_ok
reject_exponent_digit:
or [float_literal_status],-1
jmp skip_exponent_digit
float_nondigit:
cmp [waiting_for_digit],0
jne reject_float_digit
movzx eax,byte [esi-1]
mov al,[characters+eax]
cmp al,'e'
je get_literal_exponent
cmp al,'f'
jne reject_float_digit
or [float_literal_status],1
jmp skip_float_digit
reject_float_digit:
or [float_literal_status],-1
jmp skip_float_digit
move_to_adjacent_symbol:
call move_to_next_symbol
jc adjacent_symbol_ok
cmp al,'#'
je check_concatenation
neg ecx
adjacent_symbol_ok:
retn
skip_literal:
; in:
; esi - token in preprocessed line or in current embedded value
; out:
; esi = pointer advanced past the processed literal and the whitespace that follows it
; ecx = number of whitespace tokens encountered immediately before the new position
xor ecx,ecx
mov al,[esi]
cmp al,1Ah
je skip_literal_number
cmp al,22h
je skip_literal_string
cmp al,27h
je skip_literal_string
cmp al,30h
je skip_internal_number
inc esi
call move_to_next_symbol
retn
skip_literal_string:
add esi,5
call move_to_next_symbol
retn
skip_literal_number:
xor dh,dh
inc esi
lodsd
mov dl,[eax+4]
cmp dl,'0'
jb skip_number_segments
cmp dl,'9'
ja skip_number_segments
or dh,1
check_for_possible_exponent:
mov ecx,[eax]
mov dl,[eax+4+ecx-1]
cmp dl,'e'
je possible_exponent
cmp dl,'E'
jne skip_number_segments
possible_exponent:
or dh,2
jmp skip_number_segments
skip_internal_number:
inc esi
lodsd
add esi,eax
mov dh,1
skip_number_segments:
xor ecx,ecx
call move_to_next_symbol
jc literal_symbol_skipped
cmp al,'#'
je check_literal_concatenation
test ecx,ecx
jnz literal_symbol_skipped
cmp al,'.'
jne check_for_exponent
skip_decimal_point:
inc esi
xor ecx,ecx
call move_to_next_symbol
jc literal_symbol_skipped
cmp al,'#'
je check_literal_concatenation
test ecx,ecx
jnz literal_symbol_skipped
skip_attached_segment:
cmp al,'.'
je skip_decimal_point
cmp al,30h
je skip_attached_internal_number
cmp al,1Ah
jne check_for_exponent
skip_attached_number:
inc esi
lodsd
test dh,1
jnz check_for_possible_exponent
jmp skip_number_segments
skip_attached_internal_number:
inc esi
lodsd
add esi,eax
and dh,not 2
jmp skip_number_segments
check_for_exponent:
cmp dh,1+2
jne literal_symbol_skipped
cmp al,'+'
je skip_exponent
cmp al,'-'
jne literal_symbol_skipped
skip_exponent:
xor dh,dh
jmp skip_decimal_point
check_literal_concatenation:
call check_concatenation
jnc skip_attached_segment
literal_symbol_skipped:
retn
get_processed_value:
; in:
; esi = pointer into preprocessed line or current embedded value
; out:
; cf set when there were no more symbols on the line
; when cf = 0:
; al = type of value
; esi = pointer advanced past the processed element and the whitespace that follows it within the boundaries of current embedded value or preprocessed line
; ecx = number of whitespace tokens encountered immediately before the new position
; when al = 1Ah:
; ebx - SymbolTree_Leaf, null when malformed identifier
; edx - ValueDefinition, null when symbol with no valid value
; additional variables set as by identify_symbol
; when al = 22h:
; edx - 32-bit length followed by string data
; when al = 30h:
; edx - 32-bit length followed by numeric data, null when invalid number
; when al = 2Eh:
; edx - FloatData, null when invalid number
; when al is any other value, it is a simple special character, and edx is zero
; note:
; to detect whitespace between two consecutive symbols, warp_to_next_symbol should be called after this function,
; since it may further increase ecx when there is additional whitespace outside of current embedded value
; when [use_raw_values] flag is set, this function is identical to get_raw_value
call get_raw_value
jc no_more_values
cmp al,1Ah
jne value_ok
test edx,edx
jz value_ok
cmp [edx+ValueDefinition.type],VALTYPE_SYMBOLIC
je symbolic_value
value_ok:
clc
retn
symbolic_value:
cmp [use_raw_values],0
jne value_ok
mov eax,[current_pass]
mov [ebx+SymbolTree_Leaf.last_use_pass],eax
test [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE
jnz no_infinite_regress
test [edx+ValueDefinition.flags],VAL_IN_USE
jz no_infinite_regress
push edx
mov edx,_symbolic_self_reference
call register_error
pop edx
retn
no_infinite_regress:
call embed_symbolic_value
jmp get_processed_value
get_raw_value:
; same as get_processed_value, but it does not expand symbolic value and returns it just like any other
call warp_to_next_symbol
jc no_more_values
and [symbol_definition],0
mov dl,SYMCLASS_EXPRESSION
call identify_symbol
jc get_raw_value
test edi,edi
jz literal_value
mov edi,edx
test ebx,ebx
jz invalid_symbol_value
call get_available_value
jc invalid_symbol_value
mov al,1Ah
clc
retn
invalid_symbol_value:
xor edx,edx
mov al,1Ah
clc
retn
literal_value:
call get_literal
clc
retn
no_more_values:
; stc
retn