505 lines
10 KiB
PHP
505 lines
10 KiB
PHP
|
|
; flat assembler g interface for Windows IDE
|
|
; Copyright (c) 2014-2024, Tomasz Grysztar.
|
|
; All rights reserved.
|
|
|
|
flat_assembler:
|
|
|
|
mov esi,[esp+4]
|
|
mov [source_path],esi
|
|
|
|
mov [maximum_number_of_errors],MAX_ERRORS
|
|
|
|
xor al,al
|
|
call assembly_init
|
|
|
|
mov eax,[maximum_number_of_passes]
|
|
shl eax,16
|
|
invoke PostMessage,[hwnd_progress],PBM_SETRANGE,0,eax
|
|
invoke PostMessage,[hwnd_progress],PBM_SETPOS,0,0
|
|
invoke GetTickCount
|
|
mov [timer],eax
|
|
|
|
assemble:
|
|
mov esi,source_header
|
|
mov edx,[source_path]
|
|
call assembly_pass
|
|
jc assembly_done
|
|
|
|
mov ebx,[current_pass]
|
|
invoke PostMessage,[hwnd_progress],PBM_SETPOS,ebx,0
|
|
cmp ebx,[maximum_number_of_passes]
|
|
jb assemble
|
|
|
|
call show_display_data
|
|
cmp [first_error],0
|
|
jne assembly_failed
|
|
|
|
call assembly_shutdown
|
|
mov esi,_code_cannot_be_generated
|
|
jmp fatal_error
|
|
|
|
assembly_done:
|
|
call show_display_data
|
|
cmp [first_error],0
|
|
jne assembly_failed
|
|
|
|
mov [executable_path],0
|
|
mov ebx,[source_path]
|
|
mov edi,[output_path]
|
|
call write_output_file
|
|
jc write_failed
|
|
|
|
mov eax,[current_pass]
|
|
xor edx,edx
|
|
call itoa
|
|
call display_error_string
|
|
mov esi,_passes
|
|
cmp [current_pass],1
|
|
jne display_passes_suffix
|
|
mov esi,_pass
|
|
display_passes_suffix:
|
|
xor ecx,ecx
|
|
call display_error_string
|
|
invoke GetTickCount
|
|
sub eax,[timer]
|
|
xor edx,edx
|
|
add eax,50
|
|
mov ecx,1000
|
|
div ecx
|
|
mov ebx,eax
|
|
mov eax,edx
|
|
xor edx,edx
|
|
mov ecx,100
|
|
div ecx
|
|
mov [timer],eax
|
|
xchg eax,ebx
|
|
or ebx,eax
|
|
jz display_output_length
|
|
xor edx,edx
|
|
call itoa
|
|
call display_error_string
|
|
mov esi,_message_suffix
|
|
mov ecx,1
|
|
call display_error_string
|
|
mov eax,[timer]
|
|
xor edx,edx
|
|
call itoa
|
|
call display_error_string
|
|
mov esi,_seconds
|
|
xor ecx,ecx
|
|
call display_error_string
|
|
display_output_length:
|
|
call get_output_length
|
|
push eax edx
|
|
call itoa
|
|
call display_error_string
|
|
pop edx eax
|
|
mov esi,_bytes
|
|
cmp eax,1
|
|
jne display_bytes_suffix
|
|
test edx,edx
|
|
jnz display_bytes_suffix
|
|
mov esi,_byte
|
|
display_bytes_suffix:
|
|
xor ecx,ecx
|
|
call display_error_string
|
|
mov esi,_new_line
|
|
xor ecx,ecx
|
|
call display_error_string
|
|
summary_done:
|
|
|
|
call assembly_shutdown
|
|
xor eax,eax
|
|
jmp shutdown
|
|
|
|
assembly_failed:
|
|
|
|
mov ebx,[first_error]
|
|
collect_error:
|
|
call show_error_message
|
|
xor esi,esi
|
|
mov ecx,1
|
|
call display_error_string
|
|
mov esi,[ebx+Error.preprocessed_data]
|
|
mov ecx,[ebx+Error.preprocessed_length]
|
|
push ebx
|
|
call show_preprocessed_line
|
|
pop ebx
|
|
xor esi,esi
|
|
mov ecx,1
|
|
call display_error_string
|
|
mov eax,[ebx+sizeof.Error+SourceContext.number_of_entries]
|
|
test eax,eax
|
|
jz display_error_message
|
|
lea esi,[ebx+sizeof.Error+sizeof.SourceContext]
|
|
dec eax
|
|
imul eax,sizeof.SourceEntry
|
|
lea edi,[esi+eax]
|
|
push esi edi
|
|
lea esi,[edi+SourceEntry.type]
|
|
mov ecx,1
|
|
call display_error_string
|
|
pop edi esi
|
|
find_file_source:
|
|
cmp [edi+SourceEntry.type],SOURCE_FILE
|
|
jne try_next_source_entry
|
|
cmp [edi+SourceEntry.line_number],0
|
|
jne file_source_found
|
|
try_next_source_entry:
|
|
cmp edi,esi
|
|
je no_file_source
|
|
sub edi,sizeof.SourceEntry
|
|
jmp find_file_source
|
|
file_source_found:
|
|
lea esi,[edi+SourceEntry.line_number]
|
|
mov ecx,4
|
|
push edi
|
|
call display_error_string
|
|
pop edi
|
|
mov esi,[edi+SourceEntry.name]
|
|
call display_error_string
|
|
jmp error_collected
|
|
no_file_source:
|
|
xor esi,esi
|
|
mov ecx,4
|
|
call display_error_string
|
|
error_collected:
|
|
xor esi,esi
|
|
mov ecx,1
|
|
call display_error_string
|
|
mov ebx,[ebx+Error.next]
|
|
test ebx,ebx
|
|
jnz collect_error
|
|
|
|
call assembly_shutdown
|
|
mov eax,2
|
|
jmp shutdown
|
|
|
|
write_failed:
|
|
call assembly_shutdown
|
|
mov esi,_write_failed
|
|
jmp fatal_error
|
|
out_of_memory:
|
|
call assembly_shutdown
|
|
mov esi,_out_of_memory
|
|
fatal_error:
|
|
xor ecx,ecx
|
|
call display_error_string
|
|
mov eax,1
|
|
|
|
shutdown:
|
|
push eax
|
|
invoke PostMessage,[hwnd_compiler],WM_COMMAND,IDOK,0
|
|
call [ExitThread]
|
|
|
|
LINE_FEED equ 13,10
|
|
|
|
system_init:
|
|
invoke HeapCreate,0,20000h,0
|
|
mov [memory],eax
|
|
test eax,eax
|
|
jz out_of_memory
|
|
invoke GetSystemTime,systemtime
|
|
invoke SystemTimeToFileTime,systemtime,filetime
|
|
mov ebx,[filetime.dwLowDateTime]
|
|
mov eax,[filetime.dwHighDateTime]
|
|
sub ebx,116444736000000000 and 0FFFFFFFFh
|
|
sbb eax,116444736000000000 shr 32
|
|
xor edx,edx
|
|
mov ecx,10000000
|
|
div ecx
|
|
mov dword [timestamp+4],eax
|
|
mov eax,ebx
|
|
div ecx
|
|
mov dword [timestamp],eax
|
|
retn
|
|
|
|
system_shutdown:
|
|
cmp [memory],0
|
|
je memory_released
|
|
invoke HeapDestroy,[memory]
|
|
memory_released:
|
|
retn
|
|
|
|
malloc:
|
|
malloc_fixed:
|
|
malloc_growable:
|
|
; in: ecx = requested size
|
|
; out: eax - allocated block, ecx = allocated size, on error jumps to out_of_memory (does not return)
|
|
; preserves: ebx, esi, edi
|
|
; note:
|
|
; use of malloc_fixed hints that block will be kept as is until the end of assembly
|
|
; use of malloc_growable hints that block is likely to be resized
|
|
invoke HeapAlloc,[memory],0,ecx
|
|
test eax,eax
|
|
jz out_of_memory
|
|
memory_allocated:
|
|
push eax
|
|
invoke HeapSize,[memory],0,eax
|
|
mov ecx,eax
|
|
pop eax
|
|
cmp ecx,-1
|
|
je out_of_memory
|
|
retn
|
|
realloc:
|
|
; in: eax - memory block, ecx = requested size
|
|
; out: eax - resized block, ecx = allocated size, on error jumps to out_of_memory (does not return)
|
|
; preserves: ebx, esi, edi
|
|
invoke HeapReAlloc,[memory],0,eax,ecx
|
|
test eax,eax
|
|
jnz memory_allocated
|
|
jmp out_of_memory
|
|
mfree:
|
|
; in: eax - memory block
|
|
; out: cf set on error
|
|
; preserves: ebx, esi, edi
|
|
; note: eax may have value 0 or -1, it should be treated as invalid input then
|
|
test eax,eax
|
|
jz interface_error
|
|
cmp eax,-1
|
|
je interface_error
|
|
invoke HeapFree,[memory],0,eax
|
|
test eax,eax
|
|
jz interface_error
|
|
clc
|
|
retn
|
|
interface_error:
|
|
stc
|
|
retn
|
|
|
|
open:
|
|
; in: edx - path to file
|
|
; out: ebx = file handle, cf set on error
|
|
; preserves: esi, edi
|
|
mov ebx,edx
|
|
invoke WaitForSingleObject,[mutex],-1
|
|
invoke CreateFile,ebx,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0
|
|
cmp eax,-1
|
|
je release_mutex
|
|
mov ebx,eax
|
|
clc
|
|
retn
|
|
create:
|
|
; in: edx - path to file
|
|
; out: ebx = file handle, cf set on error
|
|
; preserves: esi, edi
|
|
cmp [executable_path],0
|
|
jne path_catching_done
|
|
push esi edi
|
|
mov esi,edx
|
|
mov edi,executable_path
|
|
catch_path:
|
|
lodsb
|
|
stosb
|
|
test al,al
|
|
jnz catch_path
|
|
pop edi esi
|
|
path_catching_done:
|
|
mov ebx,edx
|
|
invoke WaitForSingleObject,[mutex],-1
|
|
invoke CreateFile,ebx,GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0
|
|
cmp eax,-1
|
|
je release_mutex
|
|
mov ebx,eax
|
|
clc
|
|
retn
|
|
write:
|
|
; in: ebx = file handle, edx - data, ecx = number of bytes
|
|
; out: cf set on error
|
|
; preserves: ebx, esi, edi
|
|
push ecx
|
|
invoke WriteFile,ebx,edx,ecx,systmp,0
|
|
pop ecx
|
|
test eax,eax
|
|
jz interface_error
|
|
cmp ecx,[systmp]
|
|
jne interface_error
|
|
clc
|
|
retn
|
|
read:
|
|
; in: ebx = file handle, edx - buffer, ecx = number of bytes
|
|
; out: cf set on error
|
|
; preserves: ebx, esi, edi
|
|
push ecx
|
|
invoke ReadFile,ebx,edx,ecx,systmp,0
|
|
pop ecx
|
|
test eax,eax
|
|
jz interface_error
|
|
cmp ecx,[systmp]
|
|
jne interface_error
|
|
clc
|
|
retn
|
|
close:
|
|
; in: ebx = file handle
|
|
; preserves: ebx, esi, edi
|
|
invoke CloseHandle,ebx
|
|
release_mutex:
|
|
invoke ReleaseMutex,[mutex] ; this relies on the fact that engine never opens more than one file at once
|
|
stc
|
|
retn
|
|
lseek:
|
|
; in: ebx = file handle, cl = method, edx:eax = offset
|
|
; out: edx:eax = new offset from the beginning of file, cf set on error
|
|
; preserves: ebx, esi, edi
|
|
movzx ecx,cl
|
|
mov [systmp],edx
|
|
invoke SetFilePointer,ebx,eax,systmp,ecx
|
|
cmp eax,-1
|
|
jne lseek_ok
|
|
invoke GetLastError
|
|
test eax,eax
|
|
jnz interface_error
|
|
not eax
|
|
lseek_ok:
|
|
mov edx,[systmp]
|
|
clc
|
|
retn
|
|
|
|
get_timestamp:
|
|
; out: edx:eax = timestamp
|
|
; preserves: ebx, ecx, esi, edi
|
|
; note: during the passes of a single assembly this function should always return the same value
|
|
mov eax,dword [timestamp]
|
|
mov edx,dword [timestamp+4]
|
|
retn
|
|
|
|
display_string:
|
|
; in:
|
|
; esi - string
|
|
; ecx = string length, zero for ASCIIZ string
|
|
; preserves: ebx, esi
|
|
push ebx
|
|
mov ebx,stdout
|
|
jmp write_string
|
|
display_error_string:
|
|
; in:
|
|
; esi - string
|
|
; ecx = string length, zero for ASCIIZ string
|
|
; preserves: ebx, esi
|
|
push ebx esi
|
|
mov ebx,stderr
|
|
write_string:
|
|
test ecx,ecx
|
|
jnz write_string_to_buffer
|
|
xor al,al
|
|
mov edi,esi
|
|
or ecx,-1
|
|
repne scasb
|
|
neg ecx
|
|
sub ecx,2
|
|
write_string_to_buffer:
|
|
mov edi,[ebx]
|
|
push ecx
|
|
invoke HeapSize,[hheap],0,dword [ebx]
|
|
mov edx,eax
|
|
sub eax,4
|
|
sub eax,[edi]
|
|
mov ecx,[esp]
|
|
sub ecx,eax
|
|
jb string_buffer_ready ; do not fit tight, leave room for terminating zero
|
|
add edx,ecx
|
|
inc edx
|
|
invoke HeapReAlloc,[hheap],0,dword [ebx],edx
|
|
test eax,eax
|
|
jz shutdown
|
|
mov [ebx],eax
|
|
mov edi,eax
|
|
string_buffer_ready:
|
|
pop ecx
|
|
mov eax,[edi]
|
|
add [edi],ecx
|
|
lea edi,[edi+4+eax]
|
|
xor al,al
|
|
test esi,esi
|
|
jz zero_string
|
|
rep movsb
|
|
stosb
|
|
pop esi ebx
|
|
retn
|
|
zero_string:
|
|
rep stosb
|
|
pop esi ebx
|
|
retn
|
|
|
|
get_environment_variable:
|
|
; in:
|
|
; esi - name
|
|
; edi - buffer for value
|
|
; ecx = size of buffer
|
|
; out:
|
|
; eax = length of value
|
|
; preserves: ebx, esi, edi
|
|
push ecx
|
|
invoke GetEnvironmentVariable,esi,string_buffer,1000h
|
|
invoke GetPrivateProfileString,_section_environment,esi,string_buffer,string_buffer,1000h,ini_path
|
|
mov ecx,[esp]
|
|
push esi edi
|
|
mov esi,string_buffer
|
|
copy_env_val:
|
|
lodsb
|
|
jecxz next_env_char
|
|
mov [edi],al
|
|
dec ecx
|
|
next_env_char:
|
|
inc edi
|
|
test al,al
|
|
jnz copy_env_val
|
|
mov eax,edi
|
|
pop edi esi ecx
|
|
sub eax,edi
|
|
retn
|
|
|
|
include '../../assembler.inc'
|
|
include '../../symbols.inc'
|
|
include '../../expressions.inc'
|
|
include '../../conditions.inc'
|
|
include '../../floats.inc'
|
|
include '../../directives.inc'
|
|
include '../../calm.inc'
|
|
include '../../errors.inc'
|
|
include '../../map.inc'
|
|
include '../../reader.inc'
|
|
include '../../output.inc'
|
|
include '../../console.inc'
|
|
|
|
section '.rdata' data readable
|
|
|
|
include '../../tables.inc'
|
|
include '../../messages.inc'
|
|
|
|
_pass db ' pass, ',0
|
|
_passes db ' passes, ',0
|
|
_dot db '.'
|
|
_seconds db ' seconds, ',0
|
|
_byte db ' byte.',0
|
|
_bytes db ' bytes.',0
|
|
|
|
_write_failed db 'Failed to write the output file.',0
|
|
_out_of_memory db 'Not enough memory to complete the assembly.',0
|
|
_code_cannot_be_generated db 'Could not generate code within the allowed number of passes.',0
|
|
|
|
version_string db VERSION,0
|
|
|
|
section '.bss' readable writeable
|
|
|
|
include '../../variables.inc'
|
|
|
|
source_string dd ?
|
|
source_path dd ?
|
|
output_path dd ?
|
|
|
|
maximum_number_of_passes dd ?
|
|
|
|
timestamp dq ?
|
|
systemtime SYSTEMTIME
|
|
filetime FILETIME
|
|
|
|
memory dd ?
|
|
systmp dd ?
|
|
|
|
timer dd ?
|
|
|
|
stdout dd ?
|
|
stderr dd ?
|