652 lines
12 KiB
NASM
652 lines
12 KiB
NASM
|
|
match ,{
|
|
|
|
include '../libc/struct.inc'
|
|
|
|
} match -,{
|
|
else
|
|
|
|
include 'selfhost.inc'
|
|
|
|
end match
|
|
_ equ }
|
|
|
|
include '../version.inc'
|
|
|
|
BUFFER_SIZE = 4000h
|
|
STACK_SIZE = 4000h
|
|
|
|
format MZ
|
|
heap 0
|
|
stack stack_segment:stack_top-stack_bottom
|
|
entry loader:startup
|
|
|
|
segment loader use16
|
|
|
|
startup:
|
|
|
|
mov ax,1687h
|
|
int 2Fh
|
|
or ax,ax ; DPMI installed?
|
|
jnz short no_dpmi
|
|
test bl,1 ; 32-bit programs supported?
|
|
jz short no_dpmi
|
|
mov word [cs:mode_switch],di
|
|
mov word [cs:mode_switch+2],es
|
|
mov bx,si ; allocate memory for DPMI data
|
|
mov ah,48h
|
|
int 21h
|
|
jnc switch_to_protected_mode
|
|
init_failed:
|
|
call startup_error
|
|
db 'DPMI initialization failed.',0Dh,0Ah,0
|
|
no_dpmi:
|
|
call startup_error
|
|
db '32-bit DPMI services are not available.',0Dh,0Ah,0
|
|
startup_error:
|
|
pop si
|
|
push cs
|
|
pop ds
|
|
show_message:
|
|
lodsb
|
|
test al,al
|
|
jz message_shown
|
|
mov dl,al
|
|
mov ah,2
|
|
int 21h
|
|
jmp show_message
|
|
message_shown:
|
|
mov ax,4CFFh
|
|
int 21h
|
|
switch_to_protected_mode:
|
|
mov es,ax
|
|
mov ds,[ds:2Ch]
|
|
mov ax,1
|
|
call far [cs:mode_switch] ; switch to protected mode
|
|
jc init_failed
|
|
mov cx,1
|
|
xor ax,ax
|
|
int 31h ; allocate descriptor for code
|
|
jc init_failed
|
|
mov si,ax
|
|
xor ax,ax
|
|
int 31h ; allocate descriptor for data
|
|
jc init_failed
|
|
mov di,ax
|
|
mov dx,cs
|
|
lar cx,dx
|
|
shr cx,8
|
|
or cx,0C000h
|
|
mov bx,si
|
|
mov ax,9
|
|
int 31h ; set code descriptor access rights
|
|
jc init_failed
|
|
mov dx,ds
|
|
lar cx,dx
|
|
shr cx,8
|
|
or cx,0C000h
|
|
mov bx,di
|
|
int 31h ; set data descriptor access rights
|
|
jc init_failed
|
|
mov ecx,main
|
|
shl ecx,4
|
|
mov dx,cx
|
|
shr ecx,16
|
|
mov ax,7
|
|
int 31h ; set data descriptor base address
|
|
jc init_failed
|
|
mov bx,si
|
|
int 31h ; set code descriptor base address
|
|
jc init_failed
|
|
mov cx,0FFFFh
|
|
mov dx,0FFFFh
|
|
mov ax,8 ; set segment limit to 4 GB
|
|
int 31h
|
|
jc init_failed
|
|
mov bx,di
|
|
int 31h
|
|
jc init_failed
|
|
mov ax,ds
|
|
mov ds,di
|
|
mov [main_selector],di
|
|
mov [psp_selector],es
|
|
mov gs,ax ; environment selector in GS
|
|
cli
|
|
mov ss,di
|
|
mov esp,stack_top
|
|
sti
|
|
mov es,di
|
|
mov cx,1
|
|
xor ax,ax
|
|
int 31h ; allocate descriptor for BIOS data segment
|
|
jc init_failed
|
|
mov bx,ax
|
|
mov ax,gs
|
|
lar cx,ax
|
|
shr cx,8
|
|
mov ax,9
|
|
int 31h ; set descriptor access rights
|
|
jc init_failed
|
|
xor cx,cx
|
|
mov dx,400h
|
|
mov ax,7
|
|
int 31h ; set base address of BIOS data segment
|
|
jc init_failed
|
|
xor cx,cx
|
|
mov dx,0FFh
|
|
mov ax,8
|
|
int 31h ; set limit of BIOS data segment
|
|
jc init_failed
|
|
mov fs,bx ; BIOS data selector in FS
|
|
push si
|
|
push start
|
|
retf
|
|
|
|
mode_switch dd ?
|
|
|
|
segment main use32
|
|
|
|
start:
|
|
|
|
call system_init
|
|
|
|
call get_arguments
|
|
mov bl,al
|
|
cmp [no_logo],0
|
|
jne logo_ok
|
|
mov esi,_logo
|
|
xor ecx,ecx
|
|
call display_string
|
|
logo_ok:
|
|
test bl,bl
|
|
jnz display_usage_information
|
|
|
|
xor al,al
|
|
mov ecx,[verbosity_level]
|
|
jecxz init
|
|
or al,TRACE_ERROR_STACK
|
|
dec ecx
|
|
jz init
|
|
or al,TRACE_DISPLAY
|
|
init:
|
|
|
|
call assembly_init
|
|
|
|
mov eax,[fs:6Ch]
|
|
mov [timer],eax
|
|
|
|
assemble:
|
|
mov esi,[initial_commands]
|
|
mov edx,[source_path]
|
|
call assembly_pass
|
|
jc assembly_done
|
|
|
|
mov eax,[current_pass]
|
|
cmp eax,[maximum_number_of_passes]
|
|
jb assemble
|
|
|
|
call show_display_data
|
|
|
|
mov esi,_error_prefix
|
|
xor ecx,ecx
|
|
call display_error_string
|
|
mov esi,_code_cannot_be_generated
|
|
xor ecx,ecx
|
|
call display_error_string
|
|
mov esi,_message_suffix
|
|
xor ecx,ecx
|
|
call display_error_string
|
|
|
|
jmp assembly_failed
|
|
|
|
assembly_done:
|
|
|
|
call show_display_data
|
|
|
|
cmp [first_error],0
|
|
jne assembly_failed
|
|
|
|
cmp [no_logo],0
|
|
jne summary_done
|
|
mov eax,[current_pass]
|
|
xor edx,edx
|
|
call itoa
|
|
call display_string
|
|
mov esi,_passes
|
|
cmp [current_pass],1
|
|
jne display_passes_suffix
|
|
mov esi,_pass
|
|
display_passes_suffix:
|
|
xor ecx,ecx
|
|
call display_string
|
|
mov eax,[fs:6Ch]
|
|
sub eax,[timer]
|
|
mov ecx,36000
|
|
mul ecx
|
|
shrd eax,edx,16
|
|
shr edx,16
|
|
mov ecx,10
|
|
div ecx
|
|
mov [timer],edx
|
|
or edx,eax
|
|
jz display_output_length
|
|
xor edx,edx
|
|
call itoa
|
|
call display_string
|
|
mov esi,_message_suffix
|
|
mov ecx,1
|
|
call display_string
|
|
mov eax,[timer]
|
|
xor edx,edx
|
|
call itoa
|
|
call display_string
|
|
mov esi,_seconds
|
|
xor ecx,ecx
|
|
call display_string
|
|
display_output_length:
|
|
call get_output_length
|
|
push eax edx
|
|
call itoa
|
|
call display_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_string
|
|
mov esi,_new_line
|
|
xor ecx,ecx
|
|
call display_string
|
|
summary_done:
|
|
|
|
mov ebx,[source_path]
|
|
mov edi,[output_path]
|
|
call write_output_file
|
|
jc write_failed
|
|
|
|
call assembly_shutdown
|
|
call system_shutdown
|
|
|
|
mov ax,4C00h
|
|
int 21h
|
|
|
|
assembly_failed:
|
|
|
|
call show_errors
|
|
|
|
call assembly_shutdown
|
|
call system_shutdown
|
|
|
|
mov ax,4C02h
|
|
int 21h
|
|
|
|
write_failed:
|
|
mov ebx,_write_failed
|
|
jmp fatal_error
|
|
|
|
out_of_memory:
|
|
mov ebx,_out_of_memory
|
|
jmp fatal_error
|
|
|
|
fatal_error:
|
|
|
|
mov esi,_error_prefix
|
|
xor ecx,ecx
|
|
call display_error_string
|
|
mov esi,ebx
|
|
xor ecx,ecx
|
|
call display_error_string
|
|
mov esi,_message_suffix
|
|
xor ecx,ecx
|
|
call display_error_string
|
|
|
|
call assembly_shutdown
|
|
call system_shutdown
|
|
|
|
mov ax,4C03h
|
|
int 21h
|
|
|
|
display_usage_information:
|
|
|
|
mov esi,_usage
|
|
xor ecx,ecx
|
|
call display_string
|
|
|
|
call system_shutdown
|
|
|
|
mov ax,4C01h
|
|
int 21h
|
|
|
|
get_arguments:
|
|
push ds
|
|
mov ds,[psp_selector]
|
|
mov esi,81h
|
|
mov edi,command_line
|
|
mov ecx,7Fh
|
|
move_command_line:
|
|
lodsb
|
|
cmp al,0Dh
|
|
je command_line_moved
|
|
stosb
|
|
loop move_command_line
|
|
command_line_moved:
|
|
pop ds
|
|
xor eax,eax
|
|
stosb
|
|
mov [initial_commands],eax
|
|
mov [source_path],eax
|
|
mov [output_path],eax
|
|
mov [no_logo],al
|
|
mov [verbosity_level],eax
|
|
mov [maximum_number_of_passes],100
|
|
mov [maximum_number_of_errors],1
|
|
mov [maximum_depth_of_stack],10000
|
|
mov esi,command_line
|
|
mov edi,parameters
|
|
get_argument:
|
|
xor ah,ah
|
|
read_character:
|
|
lodsb
|
|
test al,al
|
|
jz no_more_arguments
|
|
cmp al,22h
|
|
je switch_quote
|
|
cmp ax,20h
|
|
je end_argument
|
|
stosb
|
|
jmp read_character
|
|
end_argument:
|
|
xor al,al
|
|
stosb
|
|
find_next_argument:
|
|
mov al,[esi]
|
|
test al,al
|
|
jz no_more_arguments
|
|
cmp al,20h
|
|
jne next_argument_found
|
|
inc esi
|
|
jmp find_next_argument
|
|
switch_quote:
|
|
xor ah,1
|
|
jmp read_character
|
|
next_argument_found:
|
|
cmp al,'-'
|
|
je get_option
|
|
cmp al,'/'
|
|
je get_option
|
|
cmp [source_path],0
|
|
je get_source_path
|
|
cmp [output_path],0
|
|
je get_output_path
|
|
error_in_arguments:
|
|
or al,-1
|
|
retn
|
|
get_source_path:
|
|
mov [source_path],edi
|
|
jmp get_argument
|
|
get_output_path:
|
|
mov [output_path],edi
|
|
jmp get_argument
|
|
no_more_arguments:
|
|
cmp [source_path],0
|
|
je error_in_arguments
|
|
xor al,al
|
|
stosb
|
|
retn
|
|
get_option:
|
|
inc esi
|
|
lodsb
|
|
cmp al,'e'
|
|
je set_errors_limit
|
|
cmp al,'E'
|
|
je set_errors_limit
|
|
cmp al,'i'
|
|
je insert_initial_command
|
|
cmp al,'I'
|
|
je insert_initial_command
|
|
cmp al,'p'
|
|
je set_passes_limit
|
|
cmp al,'P'
|
|
je set_passes_limit
|
|
cmp al,'r'
|
|
je set_recursion_limit
|
|
cmp al,'R'
|
|
je set_recursion_limit
|
|
cmp al,'v'
|
|
je set_verbose_mode
|
|
cmp al,'V'
|
|
je set_verbose_mode
|
|
cmp al,'n'
|
|
je set_no_logo
|
|
cmp al,'N'
|
|
jne error_in_arguments
|
|
set_no_logo:
|
|
or [no_logo],-1
|
|
mov al,[esi]
|
|
cmp al,20h
|
|
je find_next_argument
|
|
test al,al
|
|
jnz error_in_arguments
|
|
jmp find_next_argument
|
|
set_verbose_mode:
|
|
call get_option_value
|
|
jc error_in_arguments
|
|
cmp edx,2
|
|
ja error_in_arguments
|
|
mov [verbosity_level],edx
|
|
jmp find_next_argument
|
|
set_errors_limit:
|
|
call get_option_value
|
|
jc error_in_arguments
|
|
test edx,edx
|
|
jz error_in_arguments
|
|
mov [maximum_number_of_errors],edx
|
|
jmp find_next_argument
|
|
set_recursion_limit:
|
|
call get_option_value
|
|
jc error_in_arguments
|
|
test edx,edx
|
|
jz error_in_arguments
|
|
mov [maximum_depth_of_stack],edx
|
|
jmp find_next_argument
|
|
set_passes_limit:
|
|
call get_option_value
|
|
jc error_in_arguments
|
|
test edx,edx
|
|
jz error_in_arguments
|
|
mov [maximum_number_of_passes],edx
|
|
jmp find_next_argument
|
|
get_option_value:
|
|
xor eax,eax
|
|
mov edx,eax
|
|
find_option_value:
|
|
cmp byte [esi],20h
|
|
jne get_option_digit
|
|
inc esi
|
|
jmp find_option_value
|
|
get_option_digit:
|
|
lodsb
|
|
cmp al,20h
|
|
je option_value_ok
|
|
test al,al
|
|
jz option_value_ok
|
|
sub al,30h
|
|
jc invalid_option_value
|
|
cmp al,9
|
|
ja invalid_option_value
|
|
imul edx,10
|
|
jo invalid_option_value
|
|
add edx,eax
|
|
jc invalid_option_value
|
|
jmp get_option_digit
|
|
option_value_ok:
|
|
dec esi
|
|
clc
|
|
ret
|
|
invalid_option_value:
|
|
stc
|
|
ret
|
|
insert_initial_command:
|
|
push edi
|
|
find_command_segment:
|
|
cmp byte [esi],20h
|
|
jne command_segment_found
|
|
inc esi
|
|
jmp find_command_segment
|
|
command_segment_found:
|
|
xor ah,ah
|
|
cmp byte [esi],22h
|
|
jne measure_command_segment
|
|
inc esi
|
|
inc ah
|
|
measure_command_segment:
|
|
mov ebx,esi
|
|
scan_command_segment:
|
|
mov ecx,esi
|
|
mov al,[esi]
|
|
test al,al
|
|
jz command_segment_measured
|
|
cmp ax,20h
|
|
je command_segment_measured
|
|
cmp ax,22h
|
|
je command_segment_measured
|
|
inc esi
|
|
cmp al,22h
|
|
jne scan_command_segment
|
|
command_segment_measured:
|
|
sub ecx,ebx
|
|
mov edi,[initial_commands]
|
|
lea eax,[ecx+2]
|
|
test edi,edi
|
|
jz allocate_initial_commands_buffer
|
|
mov edx,[initial_commands_length]
|
|
add edi,edx
|
|
add eax,edx
|
|
cmp eax,[initial_commands_maximum_length]
|
|
ja grow_initial_commands_buffer
|
|
copy_initial_command:
|
|
xchg esi,ebx
|
|
rep movsb
|
|
mov esi,ebx
|
|
sub edi,[initial_commands]
|
|
mov [initial_commands_length],edi
|
|
mov al,[esi]
|
|
test al,al
|
|
jz initial_command_ready
|
|
cmp al,20h
|
|
jne command_segment_found
|
|
initial_command_ready:
|
|
mov edi,[initial_commands]
|
|
add edi,[initial_commands_length]
|
|
mov ax,0Ah
|
|
stosw
|
|
inc [initial_commands_length]
|
|
pop edi
|
|
jmp find_next_argument
|
|
allocate_initial_commands_buffer:
|
|
push ecx
|
|
mov ecx,eax
|
|
call malloc
|
|
mov [initial_commands],eax
|
|
mov [initial_commands_maximum_length],ecx
|
|
mov edi,eax
|
|
pop ecx
|
|
jmp copy_initial_command
|
|
grow_initial_commands_buffer:
|
|
push ecx
|
|
mov ecx,eax
|
|
mov eax,[initial_commands]
|
|
call realloc
|
|
mov [initial_commands],eax
|
|
mov [initial_commands_maximum_length],ecx
|
|
mov edi,eax
|
|
add edi,[initial_commands_length]
|
|
pop ecx
|
|
jmp copy_initial_command
|
|
|
|
include 'system.inc'
|
|
|
|
include '../malloc.inc'
|
|
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'
|
|
|
|
_logo db 'flat assembler version g.',VERSION,13,10,0
|
|
|
|
_usage db 'Usage: fasmg source [output]',13,10
|
|
db 'Optional settings:',13,10
|
|
db ' -e limit Set the maximum number of displayed errors (default 1)',13,10
|
|
db ' -p limit Set the maximum allowed number of passes (default 100)',13,10
|
|
db ' -r limit Set the maximum depth of the stack (default 10000)',13,10
|
|
db ' -v flag Enable or disable showing all lines from the stack (default 0)',13,10
|
|
db ' -i command Insert instruction at the beginning of source',13,10
|
|
db ' -n Do not show logo nor summary',13,10
|
|
db 0
|
|
|
|
_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
|
|
|
|
include '../tables.inc'
|
|
include '../messages.inc'
|
|
|
|
align 4
|
|
|
|
include '../variables.inc'
|
|
|
|
psp_selector dw ?
|
|
main_selector dw ?
|
|
|
|
malloc_freelist dd ?
|
|
|
|
source_path dd ?
|
|
output_path dd ?
|
|
maximum_number_of_passes dd ?
|
|
|
|
initial_commands dd ?
|
|
initial_commands_length dd ?
|
|
initial_commands_maximum_length dd ?
|
|
|
|
timestamp dq ?
|
|
|
|
timer dd ?
|
|
verbosity_level dd ?
|
|
no_logo db ?
|
|
|
|
command_line db 80h dup ?
|
|
parameters db 80h dup ?
|
|
|
|
segment buffer_segment
|
|
|
|
buffer = (buffer_segment-main) shl 4
|
|
|
|
db BUFFER_SIZE dup ?
|
|
|
|
segment stack_segment
|
|
|
|
stack_bottom = (stack_segment-main) shl 4
|
|
|
|
db STACK_SIZE dup ?
|
|
|
|
stack_top = stack_bottom + $
|