struct OutputArea cached_offset dq ? definition dd ? ; pointer to ValueDefinition ends struct AreaHeader flags dd ? ; AREA_# base_address_length dd ? uninitialized_length dd ? ends AREA_VIRTUAL = 1 AREA_VARIABLE = 2 AREA_SHIFT_TRACKING_DISABLED = 4 create_output_area: ; in: ; esi - base address in format of VALTYPE_NUMERIC value ; ecx = length of base address value ; out: ; ebx - AreaHeader ; edx - ValueDefinition mov eax,[current_output_area_entry] test eax,eax jz create_first_output_area push ecx lea ecx,[eax+sizeof.OutputArea*2] cmp ecx,[output_areas_list_end] jbe get_next_output_entry mov eax,[output_areas_list] sub ecx,eax sub [current_output_area_entry],eax sub [initial_output_area_entry],eax sub [output_areas_list_end],eax call grow_stack mov [output_areas_list],eax add [current_output_area_entry],eax add [initial_output_area_entry],eax add eax,ecx mov edi,eax xchg [output_areas_list_end],eax sub ecx,eax sub edi,ecx shr ecx,2 xor eax,eax rep stosd mov eax,[current_output_area_entry] get_next_output_entry: add eax,sizeof.OutputArea cmp [initial_output_area_entry],0 je another_initial_output_area mov edi,[eax-sizeof.OutputArea+OutputArea.definition] mov ecx,[edi+ValueDefinition.value_length] mov edi,[edi+ValueDefinition.value] mov ebx,[edi+AreaHeader.uninitialized_length] sub ecx,sizeof.AreaHeader sub ecx,[edi+AreaHeader.base_address_length] jz prior_uninitialized_length_ok and dword [prior_uninitialized_length],0 and dword [prior_uninitialized_length+4],0 prior_uninitialized_length_ok: add dword [prior_uninitialized_length],ebx adc dword [prior_uninitialized_length+4],0 xor edx,edx add ecx,ebx adc edx,0 add ecx,dword [eax-sizeof.OutputArea+OutputArea.cached_offset] adc edx,dword [eax-sizeof.OutputArea+OutputArea.cached_offset+4] mov dword [eax+OutputArea.cached_offset],ecx mov dword [eax+OutputArea.cached_offset+4],edx pop ecx jmp new_output_entry_ready another_initial_output_area: pop ecx jmp create_initial_output_area create_first_output_area: mov eax,[output_areas_list] create_initial_output_area: mov [initial_output_area_entry],eax and dword [eax+OutputArea.cached_offset],0 and dword [eax+OutputArea.cached_offset+4],0 and dword [prior_uninitialized_length],0 and dword [prior_uninitialized_length+4],0 new_output_entry_ready: mov [current_output_area_entry],eax lea ebx,[eax+OutputArea.definition] call create_area retn create_area: ; in: ; ebx - where pointer to ValueDefinition should be stored, may already hold a previously used one (should contain null otherwise) ; esi - base address in format of VALTYPE_NUMERIC value ; ecx = length of base address value ; out: ; ebx - AreaHeader ; edx - ValueDefinition mov [address_length],ecx mov edx,[ebx] test edx,edx jz current_area_definition_unusable cmp [edx+ValueDefinition.reference_count],1 je area_definition_ready dec [edx+ValueDefinition.reference_count] current_area_definition_unusable: mov ecx,retired_definition retrieve_retired_detached_value: mov edx,[ecx] test edx,edx jz create_area_definition cmp [edx+ValueDefinition.reference_count],0 jne retired_detached_value_immutable xor eax,eax xchg eax,[edx+ValueDefinition.previous] mov [ecx],eax jmp adopt_area_definition retired_detached_value_immutable: lea ecx,[edx+ValueDefinition.previous] jmp retrieve_retired_detached_value create_area_definition: mov ecx,sizeof.ValueDefinition call create_tree_element mov ecx,eax xchg ecx,[value_definition_chain] mov [eax+ValueDefinition.interlink],ecx mov edx,eax adopt_area_definition: mov [ebx],edx or [edx+ValueDefinition.flags],VAL_DETACHED inc [edx+ValueDefinition.reference_count] area_definition_ready: mov ecx,[address_length] add ecx,sizeof.AreaHeader mov eax,[edx+ValueDefinition.block_length] test eax,eax jz allocate_area_block cmp ecx,eax jbe initialize_area_block push ecx edx xor eax,eax xchg eax,[edx+ValueDefinition.value] call mfree pop edx ecx allocate_area_block: push edx call malloc_growable pop edx mov [edx+ValueDefinition.value],eax mov [edx+ValueDefinition.block_length],ecx initialize_area_block: mov ebx,[edx+ValueDefinition.value] lea edi,[ebx+sizeof.AreaHeader] mov ecx,[address_length] mov [ebx+AreaHeader.base_address_length],ecx rep movsb mov [ebx+AreaHeader.uninitialized_length],ecx mov [ebx+AreaHeader.flags],ecx sub edi,ebx mov [edx+ValueDefinition.value_length],edi mov [edx+ValueDefinition.type],VALTYPE_AREA mov ecx,[current_pass] mov [edx+ValueDefinition.pass],ecx retn initialize_output: ; in: ecx = number of bytes that should be added to output ; out: edi - output buffer to be filled with data ; preserves: esi mov edx,[current_area] mov ebx,[edx+ValueDefinition.value] add ecx,[ebx+AreaHeader.uninitialized_length] jc output_out_of_memory mov eax,[edx+ValueDefinition.value_length] lea edi,[ebx+eax] add ecx,eax jc output_out_of_memory mov [edx+ValueDefinition.value_length],ecx cmp ecx,[edx+ValueDefinition.block_length] jbe area_reserve_sufficient mov eax,[edx+ValueDefinition.value] sub edi,eax push edx push ecx bsr edx,ecx xchg ecx,edx dec cl shr edx,cl inc edx shl edx,cl pop ecx cmp edx,ecx jbe output_out_of_memory xchg ecx,edx call realloc pop edx mov ebx,eax add edi,eax mov [edx+ValueDefinition.value],ebx mov [edx+ValueDefinition.block_length],ecx area_reserve_sufficient: mov ecx,[ebx+AreaHeader.uninitialized_length] jecxz output_buffer_ready xor eax,eax rep stosb mov [ebx+AreaHeader.uninitialized_length],eax output_buffer_ready: retn output_out_of_memory: jmp out_of_memory uninitialized_output: ; in: ecx = number of uninitialized bytes to be added to output ; preserves: ebx, ecx, esi, edi mov edx,[current_area] mov eax,[edx+ValueDefinition.value] add [eax+AreaHeader.uninitialized_length],ecx jc area_overflow mov edx,[edx+ValueDefinition.value_length] sub edx,sizeof.AreaHeader sub edx,[eax+AreaHeader.base_address_length] add edx,[eax+AreaHeader.uninitialized_length] jc area_overflow retn area_overflow: mov edx,_area_overflow call register_error mov edx,[current_area] or ecx,-1 mov eax,[edx+ValueDefinition.value] sub ecx,[edx+ValueDefinition.value_length] add ecx,sizeof.AreaHeader add ecx,[eax+AreaHeader.base_address_length] mov [eax+AreaHeader.uninitialized_length],ecx retn trim_output: ; preserves: ecx, esi xor eax,eax mov dword [prior_uninitialized_length],eax mov dword [prior_uninitialized_length+4],eax mov edi,[current_output_area_entry] trim_current_output_area: mov edx,[edi+OutputArea.definition] mov eax,[edx+ValueDefinition.value] and [eax+AreaHeader.uninitialized_length],0 mov ebx,[edx+ValueDefinition.value_length] sub ebx,sizeof.AreaHeader sub ebx,[eax+AreaHeader.base_address_length] jnz output_trimmed cmp edi,[initial_output_area_entry] je output_trimmed sub edi,sizeof.OutputArea jmp trim_current_output_area output_trimmed: mov [current_output_area_entry],edi retn load_from_area: ; in: ; [value_length] = length of data to load ; edi - buffer for loaded data ; edx - ValueDefinition with VALTYPE_AREA ; [data_area_symbol] - SymbolTree_Leaf linked to the same ValueDefinition with update_value_link (required only for predicted loads, can be null) ; [data_offset] = offset within the area ; out: ; cf set when data could not be loaded ; when cf = 0, buffer filled with loaded data ; preserves: esi mov eax,[edx+ValueDefinition.value] mov ecx,[edx+ValueDefinition.pass] cmp ecx,[current_pass] je area_ok test [eax+AreaHeader.flags],AREA_VARIABLE jnz source_area_inaccessible area_ok: mov ebx,[data_offset] add ebx,[eax+AreaHeader.base_address_length] jc area_offset_unavailable add ebx,sizeof.AreaHeader jc area_offset_unavailable mov ecx,[edx+ValueDefinition.value_length] sub ecx,ebx jb load_out_of_initialized_data call prepare_load_length jc area_offset_unavailable xchg esi,ebx add esi,eax rep movsb mov esi,ebx cmp [value_length],0 je load_ok mov ecx,[eax+AreaHeader.uninitialized_length] jmp load_uninitialized_data load_out_of_initialized_data: add ecx,[eax+AreaHeader.uninitialized_length] jc load_uninitialized_data xor ecx,ecx load_uninitialized_data: call prepare_load_length jc area_offset_unavailable xor al,al rep stosb cmp [value_length],0 je load_ok xor ebx,ebx xchg ebx,[data_area_symbol] test ebx,ebx jz area_offset_unavailable mov edx,[ebx+SymbolTree_Leaf.retired_definition] test edx,edx jz area_with_no_history mov ecx,[current_pass] sub ecx,[edx+ValueDefinition.pass] cmp ecx,1 ja area_with_no_history or [ebx+SymbolTree_Leaf.flags],SYM_LINK_PREDICTED mov eax,[edx+ValueDefinition.value] jmp load_from_area prepare_load_length: cmp ecx,[value_length] jbe value_length_ok mov ecx,[value_length] value_length_ok: add [data_offset],ecx jc load_length_ready sub [value_length],ecx load_length_ready: retn source_area_inaccessible: mov edx,_invalid_area jmp load_failed area_with_no_history: or [next_pass_needed],1 area_offset_unavailable: mov edx,_address_out_of_range load_failed: call register_error stc retn load_ok: clc retn prepare_area_to_write: ; in: ; [data_area] - ValueDefinition with VALTYPE_AREA ; [data_offset] = offset within the area ; [value_length] = length of data to be written ; out: ; cf set when writing specified length is not possible ; when cf = 0: ; edi - buffer for specified number of bytes directly within the area ; preserves: esi mov edx,[data_area] mov eax,[edx+ValueDefinition.value] or [eax+AreaHeader.flags],AREA_VARIABLE mov ebx,[data_offset] xor ecx,ecx add ebx,[eax+AreaHeader.base_address_length] adc ecx,0 add ebx,sizeof.AreaHeader adc ecx,0 add ebx,[value_length] adc ecx,0 jnz write_outside_initialized_area cmp ebx,[edx+ValueDefinition.value_length] ja write_outside_initialized_area lea edi,[eax+ebx] mov ecx,[value_length] sub edi,ecx retn area_to_write_not_accessible: stc retn write_outside_initialized_area: sub ebx,[edx+ValueDefinition.value_length] sbb ecx,0 jnz write_outside_boundaries sub [eax+AreaHeader.uninitialized_length],ebx jnc extend_area add [eax+AreaHeader.uninitialized_length],ebx write_outside_boundaries: push edx mov edx,_address_out_of_range call register_error pop edx test ecx,ecx jnz area_to_write_not_accessible test [eax+AreaHeader.flags],AREA_VIRTUAL jz area_to_write_not_accessible test [edx+ValueDefinition.flags],VAL_IN_USE jnz area_to_write_not_accessible and [eax+AreaHeader.uninitialized_length],0 extend_virtual_area: call expand_value jmp prepare_area_to_write extend_area: test [eax+AreaHeader.flags],AREA_VIRTUAL jnz extend_virtual_area call expand_value call update_output_offsets jmp prepare_area_to_write find_output_area: ; in: ; [file_offset] = offset within the output ; out: ; cf set when not found an area that would contain a byte at specified offset ; when cf = 0: ; ebx - OutputArea ; [file_offset] = offset relative to the beginning of the found area (upper 32 bits are zero) mov esi,[initial_output_area_entry] mov edi,[current_output_area_entry] add edi,sizeof.OutputArea search_areas: mov ebx,edi sub ebx,esi jz output_area_not_found test ebx,1 shl bsf sizeof.OutputArea jz bisect_areas_list sub ebx,sizeof.OutputArea bisect_areas_list: shr ebx,1 add ebx,esi mov eax,dword [file_offset] mov edx,dword [file_offset+4] sub eax,dword [ebx+OutputArea.cached_offset] sbb edx,dword [ebx+OutputArea.cached_offset+4] jc search_earlier_areas jnz search_later_areas mov edx,[ebx+OutputArea.definition] mov ecx,[edx+ValueDefinition.value_length] mov edx,[edx+ValueDefinition.value] sub ecx,sizeof.AreaHeader sub ecx,[edx+AreaHeader.base_address_length] add ecx,[edx+AreaHeader.uninitialized_length] cmp eax,ecx jae search_later_areas output_area_found: mov dword [file_offset],eax and dword [file_offset+4],0 ; clc retn output_area_not_found: stc retn search_later_areas: lea esi,[ebx+sizeof.OutputArea] jmp search_areas search_earlier_areas: mov edi,ebx jmp search_areas read_from_output: ; in: ; edi - buffer for read data ; [value_length] = length of data to read ; [file_offset] = offset within the output ; out: ; [value_length] = number of bytes that were not in the existing output and could not be read push edi call find_output_area pop edi jc output_reading_done read_output_areas: cmp [value_length],0 je output_reading_done mov edx,[ebx+OutputArea.definition] mov ecx,[edx+ValueDefinition.value_length] mov eax,ecx mov edx,[edx+ValueDefinition.value] sub ecx,sizeof.AreaHeader sub ecx,[edx+AreaHeader.base_address_length] sub dword [file_offset],ecx jnc initialized_load_done lea esi,[edx+eax] mov ecx,dword [file_offset] add esi,ecx neg ecx cmp ecx,[value_length] jbe initialized_load_length_ok mov ecx,[value_length] initialized_load_length_ok: sub [value_length],ecx rep movsb mov dword [file_offset],ecx initialized_load_done: mov ecx,[edx+AreaHeader.uninitialized_length] sub dword [file_offset],ecx jnc uninitialized_load_done mov ecx,dword [file_offset] neg ecx cmp ecx,[value_length] jbe uninitialized_load_length_ok mov ecx,[value_length] uninitialized_load_length_ok: sub [value_length],ecx xor al,al rep stosb mov dword [file_offset],ecx uninitialized_load_done: cmp ebx,[current_output_area_entry] je output_reading_done add ebx,sizeof.OutputArea jmp read_output_areas output_reading_done: retn rewrite_output: ; in: ; esi - data to write ; [value_length] = length of data to write ; [file_offset] = offset within the output ; out: ; [value_length] = number of bytes that were not in the existing output and could not be rewritten push esi call find_output_area pop esi jc output_rewriting_done rewrite_output_areas: cmp [value_length],0 je output_rewriting_done mov edx,[ebx+OutputArea.definition] mov ecx,[edx+ValueDefinition.value_length] mov edx,[edx+ValueDefinition.value] sub ecx,sizeof.AreaHeader sub ecx,[edx+AreaHeader.base_address_length] mov edi,[edx+AreaHeader.uninitialized_length] add ecx,edi sub dword [file_offset],ecx jnc rewrite_next_area mov eax,[value_length] xor ecx,ecx add eax,edi adc ecx,ecx add eax,dword [file_offset] adc ecx,0 jz rewrite_initialized_data cmp eax,edi jbe rewrite_uninitialized_data mov eax,edi rewrite_uninitialized_data: test eax,eax jz rewrite_initialized_data push ebx sub [edx+AreaHeader.uninitialized_length],eax mov edx,[ebx+OutputArea.definition] mov ebx,eax call expand_value call update_output_offsets pop ebx rewrite_initialized_data: mov edx,[ebx+OutputArea.definition] mov ecx,[edx+ValueDefinition.value_length] mov edi,[edx+ValueDefinition.value] or [edi+AreaHeader.flags],AREA_VARIABLE add edi,[edi+AreaHeader.uninitialized_length] add edi,ecx mov ecx,dword [file_offset] add edi,ecx neg ecx cmp ecx,[value_length] jbe rewrite_length_ok mov ecx,[value_length] rewrite_length_ok: sub [value_length],ecx rep movsb mov dword [file_offset],ecx rewrite_next_area: cmp ebx,[current_output_area_entry] je output_rewriting_done add ebx,sizeof.OutputArea jmp rewrite_output_areas output_rewriting_done: retn update_output_offsets: ; in: ; edx - ValueDefinition of output area that had some of uninitialized data made initialized ; preserves: esi mov eax,[current_output_area_entry] cmp edx,[eax+OutputArea.definition] je output_offsets_ok and dword [prior_uninitialized_length],0 and dword [prior_uninitialized_length+4],0 recount_prior_uninitialized_length: cmp eax,[initial_output_area_entry] je output_offsets_ok sub eax,sizeof.OutputArea mov edi,[eax+OutputArea.definition] mov ebx,[edi+ValueDefinition.value] mov ecx,[ebx+AreaHeader.uninitialized_length] add dword [prior_uninitialized_length],ecx adc dword [prior_uninitialized_length+4],0 cmp edx,edi je output_offsets_ok mov ecx,[edi+ValueDefinition.value_length] sub ecx,sizeof.AreaHeader sub ecx,[ebx+AreaHeader.base_address_length] jz recount_prior_uninitialized_length output_offsets_ok: retn get_current_address_value: ; out: ; esi - address in format of VALTYPE_NUMERIC value ; ecx = length of address value ; note: the returned value is placed in assembly workspace mov eax,[current_area] mov esi,[eax+ValueDefinition.value] mov ebx,[eax+ValueDefinition.value_length] mov edx,assembly_workspace mov edi,[edx+Workspace.memory_start] mov ecx,[esi+AreaHeader.base_address_length] add ecx,4 call reserve_workspace mov ecx,[esi+AreaHeader.base_address_length] sub ebx,ecx sub ebx,sizeof.AreaHeader add ebx,[esi+AreaHeader.uninitialized_length] ; jc internal_error add esi,sizeof.AreaHeader xor eax,eax stosd lodsd mov ecx,eax xor edx,edx jecxz offset_added_to_base_address add_offset_to_base_address: lodsb add al,bl setc dl stosb shr ebx,8 add ebx,edx loop add_offset_to_base_address offset_added_to_base_address: mov edx,[assembly_workspace.memory_start] add edx,4 mov eax,ebx cmp byte [esi-1],80h cmc sbb eax,0 stosd optimize_base_address: movsx eax,byte [edi-2] cmp ah,[edi-1] jne base_address_ready dec edi cmp edi,edx jne optimize_base_address base_address_ready: mov ecx,edi sub ecx,edx mov [edx-4],ecx mov ecx,esi measure_variable_terms: lodsd test eax,eax jz variable_terms_measured lodsd add esi,eax jmp measure_variable_terms variable_terms_measured: xchg ecx,esi sub ecx,esi rep movsb mov esi,[assembly_workspace.memory_start] mov ecx,edi sub ecx,esi retn get_output_length: ; out: ; edx:eax = length of current output (not counting uninitialized data) ; preserves: esi mov ebx,[current_output_area_entry] mov eax,dword [ebx+OutputArea.cached_offset] mov edx,dword [ebx+OutputArea.cached_offset+4] mov edi,[ebx+OutputArea.definition] mov ecx,[edi+ValueDefinition.value_length] mov edi,[edi+ValueDefinition.value] sub ecx,sizeof.AreaHeader sub ecx,[edi+AreaHeader.base_address_length] jz current_area_entirely_uninitialized add eax,ecx adc edx,0 retn current_area_entirely_uninitialized: sub eax,dword [prior_uninitialized_length] sbb edx,dword [prior_uninitialized_length+4] retn get_output_position: ; out: ; edx:eax = current position in the output (including uninitialized data) ; preserves: esi mov ebx,[current_output_area_entry] mov eax,dword [ebx+OutputArea.cached_offset] mov edx,dword [ebx+OutputArea.cached_offset+4] mov edi,[ebx+OutputArea.definition] mov ecx,[edi+ValueDefinition.value_length] mov edi,[edi+ValueDefinition.value] sub ecx,sizeof.AreaHeader sub ecx,[edi+AreaHeader.base_address_length] add ecx,[edi+AreaHeader.uninitialized_length] add eax,ecx adc edx,0 retn create_output_path: ; in: ; ebx - base path and name ; esi - file extension ; ecx = length of the extension ; out: ; edx - output path (generated in temporary storage) push ecx mov edi,ebx xor al,al or ecx,-1 repne scasb dec edi mov ecx,edi locate_extension: cmp edi,ebx je copy_path_name dec edi mov al,[edi] cmp al,'\' je copy_path_name cmp al,'/' je copy_path_name cmp al,'.' jne locate_extension mov ecx,edi copy_path_name: sub ecx,ebx push ecx mov edi,[preprocessing_workspace.memory_start] inc ecx mov edx,preprocessing_workspace call reserve_workspace pop ecx xchg esi,ebx rep movsb mov esi,ebx pop ecx mov ebx,ecx add ecx,2 call reserve_workspace mov ecx,ebx jecxz extension_attached mov al,'.' stosb rep movsb extension_attached: xor al,al stosb mov edx,[preprocessing_workspace.memory_start] retn write_output_file: ; in: ; ebx - source path ; edi - output path ; out: ; cf set when write failed ; note: ; when output path is null, source path is used with replaced or attached extension mov [base_path],edi xor eax,eax mov [output_failures],eax mov dword [uninitialized_length],eax mov dword [uninitialized_length+4],eax mov edx,edi test edx,edx jnz create_output_file mov [base_path],ebx mov esi,[output_extension] mov ecx,[output_extension_length] call create_output_path create_output_file: call create jc output_write_failed mov esi,[initial_output_area_entry] write_area: mov edx,[esi+OutputArea.definition] mov eax,[edx+ValueDefinition.value] mov ecx,[edx+ValueDefinition.value_length] sub ecx,[eax+AreaHeader.base_address_length] sub ecx,sizeof.AreaHeader jz write_next_area mov eax,dword [uninitialized_length] or eax,dword [uninitialized_length+4] jz write_initialized_data write_uninitialized_data: mov edi,[assembly_workspace.memory_start] mov ecx,1000h shr 2 xor eax,eax rep stosd mov ecx,1000h cmp dword [uninitialized_length+4],0 jne portion_length_ok cmp ecx,dword [uninitialized_length] jbe portion_length_ok mov ecx,dword [uninitialized_length] portion_length_ok: sub dword [uninitialized_length],ecx sbb dword [uninitialized_length+4],0 mov edx,[assembly_workspace.memory_start] call write jc file_write_failed mov eax,dword [uninitialized_length] or eax,dword [uninitialized_length+4] jnz write_uninitialized_data write_initialized_data: mov edx,[esi+OutputArea.definition] mov eax,[edx+ValueDefinition.value] mov ecx,[edx+ValueDefinition.value_length] mov edx,[eax+AreaHeader.base_address_length] add edx,sizeof.AreaHeader sub ecx,edx add edx,eax call write jc file_write_failed write_next_area: mov edx,[esi+OutputArea.definition] mov eax,[edx+ValueDefinition.value] mov eax,[eax+AreaHeader.uninitialized_length] add dword [uninitialized_length],eax adc dword [uninitialized_length+4],0 cmp esi,[current_output_area_entry] je close_output_file add esi,sizeof.OutputArea jmp write_area close_output_file: call close mov ebx,[auxiliary_output_areas] mov edi,write_auxiliary_output_area call iterate_through_map cmp [output_failures],0 jne output_write_failed clc retn file_write_failed: call close output_write_failed: stc retn write_auxiliary_output_area: ; in: ; eax = ValueDefinition, null for cached extension not used for auxiliary output ; esi - file extension ; ecx = length of the extension test eax,eax jz auxiliary_output_processed mov ebx,[base_path] test ebx,ebx jz auxiliary_output_processed push eax call create_output_path call create pop edx jc auxiliary_file_creation_failed mov eax,[edx+ValueDefinition.value] mov ecx,[edx+ValueDefinition.value_length] mov edx,[eax+AreaHeader.base_address_length] add edx,sizeof.AreaHeader sub ecx,edx add edx,eax call write jc auxiliary_file_write_failed call close auxiliary_output_processed: retn auxiliary_file_write_failed: call close auxiliary_file_creation_failed: inc [output_failures] retn