format PE console 4.0

include 'E:/bin/fasm/include/win32ax.inc'
include 'E:/bin/fasm/include/api/kernel32.inc'
include 'E:/bin/fasm/include/api/user32.inc'
include 'E:/bin/fasm/include/api/shell32.inc'

entry _main

section '.text' code readable executable
use32

_main:

	call	get_command_line_args

	call	load_source_file

	call	alloc_dst_buff

	call	assemble

	;call	 write_destination_file


	;invoke  MessageBox, 0, [dst_hwnd], 0,0

_exit:
	cinvoke system,"pause"
	cinvoke exit,0
	ret


alloc_dst_buff:
	invoke	VirtualAlloc,0,[dst_buff_size],MEM_COMMIT,PAGE_READWRITE
	test	eax,eax
	jz	_exit
	mov	[dst_ptr],eax
	ret


; Loads the source file (asm code input)
; out ( [src_path] )
; change ( eax, ebx, ecx )
load_source_file:
	cmp	byte[src_path],0
	je	_exit

	invoke	CreateFile,src_path,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
	cmp	eax,INVALID_HANDLE_VALUE
	je	_exit

	push	eax			; store file handle
	invoke	GetFileSize,eax,0
	push	eax			; store file size
	invoke	VirtualAlloc,0,eax,MEM_COMMIT,PAGE_READWRITE;MEM_RESERVE
	test	eax,eax
	jz	_exit
	mov	[src_ptr],eax

	pop	ebx    ; size
	pop	ecx    ; file hwnd
	push	ecx
	push	ebx

	invoke	ReadFile,ecx,eax,ebx,useless,0
	test	eax,eax
	jz	_exit

	pop	ebx

; + --- BEGIN: Add special characters in the end of file

	mov	edi,[src_ptr]
	mov	word[edi+ebx],(' ' shl 8) + C_NEWLINE

; + ---- END

	pop	eax
	invoke	CloseHandle,eax
	ret


; Saves the binary file
write_destination_file:
	mov	esi,src_path
    @@: inc	esi
	cmp	byte[esi],0
	jnz	@b
	mov	dword[esi-3],'bin'

	invoke	CreateFile,src_path,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
	cmp	eax,INVALID_HANDLE_VALUE
	je	exit
	push	eax
	invoke	WriteFile,eax,[dst_ptr],[dst_total_bytes_to_write],useless,0
	test	eax,eax
	jz	_exit
	pop	eax
	invoke	CloseHandle,eax
	ret


; Get command line args, in the format:
;     <app_path> <src_file_path> [<parameters>]
;     zasm.exe example.asm -output myapp.exe
; change (app_path, src_path)
get_command_line_args:
	invoke	GetCommandLine
	mov	esi,eax
	mov	edi,app_path
	call	.storePath
	mov	edi,src_path
	call	.storePath
	; TODO: read parameters
	jmp	.done
    .storePath:
	cmp	byte[esi],'"'
	je	.quotedPath
    @@: movsb
	cmp	byte[esi],0
	je	.done
	cmp	byte[esi],' '
	jne	@b
	inc	esi ; skip <space>
	ret
    .quotedPath:
	inc	esi ; skip <open ">
    @@: movsb
	cmp	byte[esi],'"'
	jne	@b
	inc	esi ; skip <close ">
	inc	esi ; skip <space>
 .done: ret


; +---------------------------------------------------------------------------


macro savebyte
{
	stosb
	inc	[dst_total_bytes_to_write]
}


assemble:
	xor	eax,eax
	mov	esi,[src_ptr]
	;mov	 edi,[dst_ptr]
	;lodsb

	call	bnf_string
	test	eax,eax
	jnz	invNum

	ret
invNum:
	mov eax,str_number
	call invalid

bnf_code:
	;call	 bnf_capname
	mov	edx,keyword
	mov	ebx,header_list
	mov	ecx,header_list.size
	call	array_search
	ret


bnf_instruction:
	mov	edx,keyword
	mov	ebx,instr_list
	mov	ecx,instr_list.size
	call	array_search
	ret


bnf_string:	lodsb
		cmp	al,"'"
		je	.get
		cmp	al,'"'
		jne	.no
	  .get: mov	ah,al
		call	bnf_anything
		cmp	ah,al
		jne	.no
	  .yes: xor	eax,eax
	   .no: ret
; <string> = '<anything>' | "<anything>"
; return ( eax ) 0 = is string


bnf_anything:	lodsb
		test	al,al
		jz	.done
		cmp	al,C_NEWLINE
		je	.done
		cmp	ah,al
		jne	bnf_anything
	 .done: ret
; <anything>
; in ( char_stop_condition ah )
bnf_anything_m: lodsb
		test	al,al
		jz	.done
		cmp	ah,al
		jne	bnf_anything
	 .done: ret
; <anything_multiline>
; in ( char_stop_condition ah )


bnf_number:	mov	ebx,esi
		call	bnf_decimal
		test	al,al
		jz	.yes
		mov	esi,ebx
		call	bnf_binary
		test	al,al
		jz	.yes
		mov	esi,ebx
		call	bnf_hexadecimal
		test	al,al
		jnz	.no
	  .yes: xor	eax,eax
	   .no: ret
; <number> = <decimal> | <binary> | <hexadecimal>
; change ( ebx )
; return ( eax ) 0 = is number


bnf_hexadecimal:lodsb
		cmp	al,'0'
		jne	.no
		lodsb
		cmp	al,'x'
		jne	.no
		mov	ebx,esi
	    @@: lodsb
		cmp	al,'0'
		jb	@f
		cmp	al,'9'
		jbe	@b
		cmp	al,'A'
		jb	@f
		cmp	al,'F'
		jbe	@b
	    @@: dec	esi
		cmp	ebx,esi
		je	.no
		call	bnf_let_or_num
		setz	al		; 1 = valid
	   .no: ret
; <hexadecimal> solve 0x[0-9A-F]+
; change ( ebx )
; return ( al ) 0 = is hexadecimal


bnf_decimal:	lodsb
		cmp	al,'0'
		jb	.no
		cmp	al,'9'
		ja	.no
	    @@: lodsb
		cmp	al,'0'
		jb	@f
		cmp	al,'9'
		jbe	@b
	    @@: dec	esi
		call	bnf_let_or_num
		setz	al		; 1 = valid
	   .no: ret
; <decimal> solve [0-9]+
; return ( al ) 0 = is decimal


bnf_binary:	lodsb
		cmp	al,'0'
		jb	.no
		cmp	al,'1'
		ja	.no
	    @@: lodsb
		cmp	al,'0'
		je	@b
		cmp	al,'1'
		je	@b
		cmp	al,'b'
		jne	.no
		call	bnf_let_or_num
		setz	al		; 1 = valid
	   .no: ret
; <binary> solve [0-1]+b
; return ( al ) 0 = is binary


bnf_let_or_num: lodsb
		cmp	al,'0'
		jb	.no
		cmp	al,'9'
		jbe	.yes
		cmp	al,'A'
		jb	.no
		cmp	al,'Z'
		jbe	.yes
		cmp	al,'a'
		jb	.no
		cmp	al,'z'
		jbe	.yes
		jmp	.no
	  .yes: xor	eax,eax
	   .no: ret
; solve [0-9A-Za-z]+
; return ( al ) 0 = is letter or number


bnf_skipspace:	lodsb
		cmp	al,C_TAB
		je	bnf_skipspace
		cmp	al,C_SPACE
		je	bnf_skipspace
		cmp	al,C_RETURN
		je	bnf_skipspace
		ret


array_search:
	; perform a search in a array
	; in ( keyword edx, list ebx, list_size ecx )
	; out ()
	; change ( al, ecx )
	mov	edx,keyword
	mov	ebx,instr_list
	mov	ecx,instr_list.size

	push	esi
	push	edi
  .for: mov	esi,edx
	mov	edi,[ebx+ecx*4]

    @@: mov	al,[esi]
	cmp	[edi],al
	jne	.neq
	inc	esi
	inc	edi
	test	al,al
	jnz	@b

	; <here i found a item!>

	jmp	.done

  .neq: dec	ecx
	jns	.for
 .done: pop	edi
	pop	esi
	ret
	.done


expected:
	; Prints a expected character msg and exit
	; input(eax character)
	cinvoke printf,msg_exp,eax
	jmp	_exit
	ret
	msg_exp db "'%c' expected!",C_NEWLINE,0


invalid:
	; Prints an invalid msg and exit
	; input(eax string pointer)
	cinvoke printf,msg_inv,eax
	jmp	_exit
	ret
	msg_inv db 'Invalid %s',C_NEWLINE,0

error:
	; Prints an error msg and exit
	; input(eax string pointer)
	cinvoke printf,msg_err,eax
	jmp	_exit
	ret
	msg_err db 'Error: %s',C_NEWLINE,0

msg:
	; Prints a msg
	; input(ebx string pointer)
	cinvoke printf,msg_msg,ebx
	ret
	msg_msg db '%s',C_NEWLINE,0


; +-----------------------------------------------------------------------------


section '.data' code readable writable

	C_TAB	  = 0x09	; \t
	C_NEWLINE = 0x0A	; \n
	C_RETURN  = 0x0D	; \r
	C_SPACE   = 0x20	; ' '

	app_path db 256 dup 0
	src_path db 256 dup 0
	keyword  db 256 dup 0

	src_ptr dd ?

	dst_ptr dd ?
	dst_buff_size dd 512
	dst_total_bytes_to_write dd 0


	useless dd ?


    instr_list dd instr_list_str.test
	       dd instr_list_str.put
	       dd instr_list_str.pop
	       dd instr_list_str.and
	       dd instr_list_str.or
	       dd instr_list_str.add
	       dd instr_list_str.sub
	       dd instr_list_str.inc
	       dd instr_list_str.dec
	       dd instr_list_str.xor
	       dd instr_list_str.mov
	 .size = ($-instr_list) shr 2 - 1

    header_list dd header_str.pe
		dd header_str.pec
		dd header_str.dll
		dd header_str.com
		dd header_str.bin
	 .size = ($-header_list) shr 2 - 1


    instr_list_str:
	.mov db 'mov',0
	.add db 'add',0
	.sub db 'sub',0
	.inc db 'inc',0
	.dec db 'dec',0
	.xor db 'xor',0
	.put db 'put',0
	.pop db 'pop',0
	.and db 'and',0
	 .or db 'or',0
       .test db 'test',0

    regs32_list_str:
	.eax db 'eax',0
	.ebx db 'ebx',0
	.ecx db 'ecx',0
	.edx db 'edx',0

    header_str:
	.pe db 'PE',0
       .pec db 'PEC',0
       .dll db 'DLL',0
       .com db 'COM',0
       .bin db 'BIN',0


    str_number db 'number',0

    ;INSTR_MOV:
    ;	 dd instr_list_str.mov
    ;	 dd 2 ; total operands
    ;	 dd 0 ; op1
    ;	 dd 1 ; op2


section '.idata' import data readable

	library kernel32,'kernel32.dll',msvcrt,'MSVCRT.DLL',\
		user32,'user32.dll',shell32,'shell32.dll'
	import msvcrt,printf,'printf',scanf,'scanf',exit,'exit',system,'system'
