; 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