
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 + $
